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

import java.util.*;
import java.io.*;
import paraselene.*;
import paraselene.ajax.*;
import paraselene.ajax.data.*;


class OutputCall {
	int next_key;
	Page last_page, next_page;
	RequestParameter	req;
	SessionData	data;
	Page[]	out = null;
	Forward forward;

	Page[] getPage() {
		return out;
	}

	OutputCall( int nk, Page lp, Forward fw, RequestParameter r, SessionData sd ) {
		next_key = nk;
		last_page = ( lp instanceof NullPage)?	null:	lp;
		next_page = fw.getPage( lp );
		forward = fw;
		req = r;
		data = sd;
		if ( next_page != null ) {
			next_page.setHistoryKey( next_key );
		}
	}

	void call_main( boolean refresh_f ) throws Throwable {
		next_page.setRequestParameter( req );
		Option.trace( "%s/%s output(%s) call", next_page.getID(), next_page.getUniqueKey(), (last_page == null)?	"null": last_page.getID() );
		data.hist.forceNewNo( next_page.getHistoryKey() );
		data.hist.get( next_page.getHistoryKey() ).add( next_page.getParentPage() );
		Page	view = SandBox.output( next_page, last_page, req, refresh_f );
		if ( view == null )	view = next_page;
		next_page.setAgencyPage( view );
		next_page.setInitialized( false );
		next_page.setRequestParameter( null );
		if ( view != next_page ) {
			view.setAgencyPage( null );
			view.setInitialized( false );
			view.setRequestParameter( null );
		}
		out = new Page[]{ next_page, view };
	}

	void call( boolean refresh_f ) throws Throwable {
		PageID	last_id = null;
		if ( last_page != null ) {
			last_id = last_page.getID();
		}
		if ( data == null )	return;
		if ( forward.history_f ) {
			if ( next_page.getID() == last_id ) {
				last_id.getPageFactory().returnPage( next_page );
				next_page = last_page;
			}
			else if ( next_page.isAllowHistoryAdd() ) {
				Page	sub = null;
				History	history = data.hist.get( next_key );
				if ( history != null ) {
					sub = history.getPage( next_page.getID() );
				}
				if ( sub != null ) {
					next_page.getID().getPageFactory().returnPage( next_page );
					next_page = sub;
				}
			}
		}
		call_main( refresh_f );
	}

	static PostBack[] make( History h, Page next_page, Page[] out, boolean lock_f ) throws Exception {
		h.add( next_page );
		TagData[]	data = null;
		try{
			if ( lock_f)	h.lock();
			data = TagData.getTag( out[0], out[1] );
		}
		finally{
			if ( lock_f )	h.unlock();
		}
		PostBack[]	ret = new PostBack[data.length];
		for ( int i = 0; i < ret.length; i++ ) {
			ret[i] = new PostBack( data[i] );
		}
		Option.trace( "%s/%s to TagData JSON", next_page.getID(), next_page.getUniqueKey() );
		return ret;
	}

	PostBack[] make() throws Exception {
		data.hist.forceNewNo( next_key );
		return make( data.hist.get( next_key ), next_page, out, true );
	}
}

class OpenOutput extends OutputCall {
	OpenOutput( int nk, Page lp, Forward fw, Popup.Type type, RequestParameter r, SessionData sd ) {
		super( nk, lp, fw, r, sd );
		next_page.setPopupType( type );
	}

	void call( boolean refresh_f ) throws Throwable {
		Popup	p = (Popup)forward;
		if ( p.active_page != null )	return;
		call_main( refresh_f );
	}

	static PostBack[] make( Popup p, History h, Page next_page, Page out, boolean lock_f  ) throws Exception {
		if ( p.active_page == null ) {
			h.add( next_page );
			Option.trace( "%s/%s to Popup open JSON", next_page.getID(), next_page.getUniqueKey() );
			try {
				if ( lock_f )	h.lock();
				return new PostBack[] { new PostBack( new Open( out, false ) ) };
			}
			finally{
				if ( lock_f )	h.unlock();
			}
		}
		Popup.Type	ptype = p.active_page.getPopupType();
		if ( !Popup.isModeless( ptype ) ) {
			Option.trace( "%s/%s to Popup active cancel, because it is not MODELESS opened", next_page.getID(), next_page.getUniqueKey() );
			return new PostBack[0];
		}
		if ( Popup.isModelessAlert( ptype, h, true ) ) {
			Option.trace( "%s/%s to Popup active cancel, because MODAL opened", next_page.getID(), next_page.getUniqueKey() );
			return new PostBack[0];
		}
		Option.trace( "%s/%s to Popup active JSON", next_page.getID(), next_page.getUniqueKey() );
		return new PostBack[] { new PostBack( new Open( p.active_page, true ) ) };
	}

	PostBack[] make() throws Exception {
		if ( out == null ) {
			out = new Page[]{
				next_page, next_page
			};
		}
		data.hist.forceNewNo( next_key );
		return make( (Popup)forward, data.hist.get( next_key ), next_page, out[1], true );
	}
}

class CloseOutput extends OutputCall {
	CloseOutput( int nk, Page lp, Forward fw, RequestParameter r, SessionData sd ) {
		super( nk, lp, fw, r, sd );
	}

	void call( boolean refresh_f ) throws Throwable {}

	static PostBack[] make( Closure c, History h, Page next_page ) {
		h.removePopup( c.close_page );
		Option.trace( "%s/%s to Popup close JSON", next_page.getID(), next_page.getUniqueKey() );
		return new PostBack[] { new PostBack( new Close( c.close_page ) ) };
	}

	PostBack[] make() throws Exception {
		data.hist.forceNewNo( next_key );
		return make( (Closure)forward, data.hist.get( next_key ), next_page );
	}
}

class ScriptOutput extends OutputCall {
	ScriptOutput( int nk, Page lp, Forward fw, RequestParameter r, SessionData sd ) {
		super( nk, lp, fw, r, sd );
	}
	void call( boolean refresh_f ) throws Throwable {}

	static PostBack[] make( JavaScript js ) throws Exception {
		StringBuilder	buf = new StringBuilder();
		for ( int i = 0; i < js.cmd.length; i++ ){
			buf = buf.append( js.cmd[i] );
			buf = buf.append( " / " );
		}
		buf = buf.append( " to JavaScript JSON" );
		Option.trace( buf.toString() );
		return new PostBack[] { new PostBack( new Exec( js.cmd ) ) };
	}

	PostBack[] make() throws Exception {
		return make( (JavaScript)forward );
	}
}

class NopOutput extends OutputCall {
	NopOutput( int nk, Page lp, Forward fw, RequestParameter r, SessionData sd ) {
		super( nk, lp, fw, r, sd );
	}
	void call( boolean refresh_f ) throws Throwable {}

	static PostBack[] _make() throws Exception {
		Option.trace( "NOP JSON" );
		return new PostBack[] { new PostBack() };
	}

	PostBack[] make() throws Exception {
		return _make();
	}
}

class LocationOutput extends OutputCall {
	LocationOutput( int nk, Page lp, Forward fw, RequestParameter r, SessionData sd ) {
		super( nk, lp, fw, r, sd );
	}

	void call( boolean refresh_f ) throws Throwable {
		if ( !forward.isLeave() )	super.call( refresh_f );
	}

	PostBack[] make() throws Exception {
		data.hist.forceNewNo( next_key );
		data.hist.get( next_key ).add( next_page );
		Forward	fw = forward;
		if ( !forward.isLeave() ) {
			fw = data.redirect( out, forward, req );
		}
		String	url = Supervisor.makeWithSessionURI( fw.getRedirectURI().toString(), null, null );
		Option.trace( "%s redirect JSON", url );
		return new PostBack[] { new PostBack( url ) };
	}
}

class OutputHolder {
	private Forward[]		forward = null;
	private OutputCall[]	opc = null;
	private boolean[]		refresh_f = null;

	OutputHolder( boolean ajax_f, int nk, Page lp, Forward fw, RequestParameter r, SessionData sd ) {
		Option.trace( "%s ajax %s", lp.getID(), ajax_f );
		AjaxForward[]	af = fw.getAjaxForward( ajax_f );
		if ( af != null ) {
			forward = new Forward[af.length];
			refresh_f = new boolean[af.length];
			for ( int i = 0; i < af.length; i++ ) {
				forward[i] = (Forward)af[i];
				refresh_f[i] = forward[i].isRefresh( ajax_f );
			}
			opc = new OutputCall[af.length];
		}
		else {
			forward = new Forward[] { fw };
			refresh_f = new boolean[] { fw.isRefresh( ajax_f ) };
			opc = new OutputCall[1];
		}
		if ( ajax_f ) {
			for ( int i = 0; i < forward.length; i++ ) {
				opc[i] = forward[i].makeOutput( nk, lp, r, sd );
			}
			return;
		}
/**
		Option.trace( "new OutputCall <- %s", forward[0] );
		opc[0] = new OutputCall( nk, lp, forward[0], r, sd );
**/
		Option.trace( "new LocationOutput <- %s", forward[0] );
		opc[0] = new LocationOutput( nk, lp, forward[0], r, sd );
	}

	void call() throws Throwable {
		for ( int i = 0; i < opc.length; i++ ) {
			opc[i].call( refresh_f[i] );
		}
	}

	Page[] getPage() {
		Page[]	p = opc[0].getPage();
		return p;
	}

	Serializable[] getPostBack() throws Exception {
		ArrayList<Serializable>	ret = new ArrayList<Serializable>();
		for ( int i = 0; i < opc.length; i++ ) {
			PostBack[]	pb = opc[i].make();
			for ( int j = 0; j < pb.length; j++ ) {
				ret.add( pb[j] );
			}
		}
		return ret.toArray( new Serializable[0] );
	}
}

