/*
 * Paraselene
 * Copyright (c) 2009, 2010  Akira Terasaki
 * このファイルは同梱されているLicense.txtに定めた条件に同意できる場合にのみ
 * 利用可能です。
 */
package paraselene.supervisor;


import paraselene.*;
import paraselene.ajax.*;
import java.util.*;
import java.net.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;


class DataHolder implements ServerInformation {
	private HttpServletRequest	req;
	private boolean search_engine = false;
	private PageFactory	fact;

	RequestParameter	request;
	HttpServletResponse	response;
	SessionData	data;
	boolean	session_null = true;

	private String	serverName;
	private String	contextPath;

	DataHolder( Supervisor sv, HttpServletRequest rq ) {
		req = rq;
		serverName = req.getServerName();
		contextPath = req.getContextPath();
		fact = sv.getPageFactory();
	}
	HttpServletRequest getRequest() {
		return req;
	}
	public String getServerName(){ return serverName; }
	public String getContextPath(){ return contextPath; }
	public TransactionSequencer getTransactionSequencer() {
		return data.seq;
	}
	public boolean isSearchEngine(){ return search_engine; }
	void setSearchEngine( boolean flag ) {
		search_engine = flag;
	}
	public HistorySet getHistorySet() { return data.hist; }
	public PageFactory getPageFactory() { return fact; }

	String encodeURL( String url, Page page ) {
		if ( request.judgeMobile() == RequestParameter.Mobile.NO_MOBILE ) {
			if ( page != null ) {
				if ( !page.isEncodeURL() )	return url;
			}
		}
		return response.encodeURL( url );
	}
}

class RedirectData implements Serializable {
	private static final long serialVersionUID = 2L;
	URI		posted;
	String	uri;
	URI		redirect_path;
	Forward	fw;
	Page[]	out_page;
}

class SessionData implements Serializable, HttpSessionBindingListener {
	private static final long serialVersionUID = 25L;
	TransactionSequencer seq = new TransactionSequencer();
	HistorySet hist = null;
	private HashMap<PageID, RedirectData>	pending = new HashMap<PageID, RedirectData>();
	private volatile int client;

	private String getAdder( HttpServletRequest req ) {
		String	addr = req.getRemoteAddr();
		if ( addr == null )	return null;
		String[]	bt = addr.split( "[\\.:]" );
		if ( bt.length < 4 )	return addr;
		StringBuilder	buf = new StringBuilder( bt[0].trim() );
		int	len = bt.length - 1;
		for ( int i = 1; i < len; i++ ) {
			buf = buf.append( "." );
			buf = buf.append( bt[1].trim() );
		}
		return buf.toString();
	}

	private int getClient( HttpServletRequest req ) {
		String[]	data = new String[] {
			req.getHeader( RequestParameter.UA_HEAD ),
			getAdder( req ),
			req.getHeader( RequestParameter.X_FORWARD )
		};
		StringBuilder	buf = new StringBuilder();
		for ( int i = 0; i < data.length; i++ ) {
			if ( data[i] == null )	data[i] = "non";
			buf = buf.append( "*" );
			buf = buf.append( data[i] );
		}
		String	str = buf.toString();
		Option.trace( str );
		return str.hashCode();
	}

	SessionData( HttpServletRequest req ) throws ParaseleneException {
		client = getClient( req );
		hist = new HistorySet( req.getSession().getId() );
	}

	boolean isSame( HttpServletRequest req ) {
		if ( client == getClient( req ) )	return true;
		client = 0;
		System.out.println( "-- unmatched client --------------------------"  );
		System.out.println( new Date().toString() );
		System.out.println( "IP = " + req.getRemoteAddr() );
		System.out.println( "User Agent = " + req.getHeader( RequestParameter.UA_HEAD ) );
		System.out.println( "SessionID = " + req.getSession().getId() );
		System.out.println( "---------------------------------------------\n" );
		return false;
	}

	boolean write( Supervisor sv, Page out, DataHolder holder ) throws Exception {
		PageID	id = out.getID();
		RedirectData	data = null;
		synchronized( pending ) {
			data = pending.get( id );
		}
		id.getPageFactory().returnPage( out );
		if ( data == null ) {
			Option.trace( "redirect pending data null" );
			return false;
		}
		if ( holder.request.getURI().toString().indexOf( data.uri ) < 0 ) {
			Option.trace( "redirect path unmatched pending(%s) call(%s)", data.uri, holder.request.getURI().toString() );
			return false;
		}
		sv.write( data.out_page, false, data.fw, holder );
		return true;
	}

	Forward redirect( Page[] out_ret, Forward fw, RequestParameter req ) throws Exception {
		RedirectData	rd = new RedirectData();
		rd.fw = fw;
		rd.out_page = out_ret;
		rd.posted = req.getURI();
		rd.uri = TransactionSequencer.makeRedirectURI( seq, out_ret[0].getID() );
		String	path = Supervisor.makeWithSessionURI(
			URIValue.makeAbsolutePath(
				URIValue.Scheme.getScheme( rd.posted ), rd.posted.getPort(), rd.uri
			), null, null
		);
		synchronized( pending ) {
			pending.put( out_ret[0].getID(), rd );
		}
		rd.redirect_path = new URI( path );
		return new Forward( rd.redirect_path, false );
	}

	Forward redirect( URI post ) {
		synchronized( pending ) {
			Collection<RedirectData>	list = pending.values();
			for ( RedirectData rd : pending.values() ) {
				if ( rd.posted.equals( post ) ) {
					return new Forward( rd.redirect_path, false );
				}
			}
			return null;
		}
	}

	public void valueBound( HttpSessionBindingEvent ev ) {}
	public void valueUnbound( HttpSessionBindingEvent ev ) {
		if ( !planed_clear ) {
			hist.drop();
			Ajax.removeComet( ev.getSession().getId() );
		}
	}
	boolean	planed_clear = false;
}



