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


import java.util.*;
import java.net.*;
import java.io.*;
import paraselene.*;
import paraselene.supervisor.*;
import paraselene.tag.*;
import paraselene.tag.table.*;
import paraselene.dyna.mockup.*;

public class Linker extends paraselene.mockup.output.Readme implements OutputNo {
	public String getString( int no ) {
		int	cnt = org_html.size();
		StringBuilder	buf = new StringBuilder();
		switch( no ) {
		case 1:
			return Param.PACKAGE.get();
		case 2:
			buf = buf.append( "/**\n * Json\n */\nPARASELENE_JSON(-1),\n" );
			buf = buf.append( "/**\n * FormJson\n */\nPARASELENE_FORM_JSON(-2),\n" );
			buf = buf.append( "/**\n * Resource\n */\nPARASELENE_RESOURCE(-30),\n" );
			for ( int i = 0; i < cnt; i++ ) {
				PrePage	page = page_map.get( org_html.get( i ) );
				buf = buf.append( "/**\n * " );
				buf = buf.append( page.getMyselfURI().getPath() );
				buf = buf.append( "\n */\n" );
				buf = buf.append( page.getEnumName( true ) );
				buf = buf.append( "(" );
				buf = buf.append( page.getID().getID() );
				buf = buf.append( i == (cnt - 1)?	");\n":	"),\n" );
			}
			return buf.toString();
		case 3:
			buf = buf.append( "page=new " );
			buf = buf.append( Param.PACKAGE.get() );
			buf = buf.append( ".Json();\n" );
			buf = buf.append( "pf.addDefine(PARASELENE_JSON,page.getClass());\n");
			buf = buf.append( "map.put(PARASELENE_JSON.id,PARASELENE_JSON);\n" );
			buf = buf.append( "pf.returnPage(page);\n" );

			buf = buf.append( "page=new " );
			buf = buf.append( Param.PACKAGE.get() );
			buf = buf.append( "." );
			buf = buf.append( Param.PACKAGE_BASE.get() );
			buf = buf.append( ".FormJson();\n" );
			buf = buf.append( "pf.addDefine(PARASELENE_FORM_JSON,page.getClass());\n");
			buf = buf.append( "map.put(PARASELENE_FORM_JSON.id,PARASELENE_FORM_JSON);\n" );
			buf = buf.append( "pf.returnPage(page);\n" );


			buf = buf.append( "page=new " );
			buf = buf.append( Param.PACKAGE.get() );
			buf = buf.append( "." );
			buf = buf.append( Param.PACKAGE_BASE.get() );
			buf = buf.append( ".Resource();\n" );
			buf = buf.append( "pf.addDefine(PARASELENE_RESOURCE,page.getClass());\n");
			buf = buf.append( "map.put(PARASELENE_RESOURCE.id,PARASELENE_RESOURCE);\n" );
			buf = buf.append( "pf.returnPage(page);\n" );

			for ( int i = 0; i < cnt; i++ ) {
				PrePage page = page_map.get( org_html.get( i ) );
				buf = buf.append( "page=new " );
				buf = buf.append( page.getClassName() );
				buf = buf.append( "();\n" );
				buf = buf.append( "pf.addDefine(" );
				buf = buf.append( page.getEnumName( true ) );
				buf = buf.append( ",page.getClass());\n" );
				buf = buf.append( "map.put(" );
				buf = buf.append( page.getEnumName( true ) );
				buf = buf.append( ".id," );
				buf = buf.append( page.getEnumName( true ) );
				buf = buf.append( ");\n" );
				buf = buf.append( "pf.returnPage(page);\n" );
			}
			return buf.toString();
		case 4:
			return Param.OUT_SOURCE_PATH.get();
		case 33:
			return Param.PACKAGE_BASE.get();
		case 34:
			return Param.PACKAGE_VIEW.get();
		case 35:
			return Param.PACKAGE_LOGIC.get();
		}
		return null;
	}

	private ArrayList<String>	org_html = new ArrayList<String>();
	private HashMap<String,PrePage>	page_map = new HashMap<String,PrePage>();
	private ArrayList<String>	ex_file = new ArrayList<String>();
	private HashMap<String,String>	ex_map = new HashMap<String,String>();

	private PrePage crt_page = null;

	public static boolean flag = false;
	public static final Linker	readme = new Linker();

	Linker() {
		super();
		init();
		Tag	meta = new Tag( "meta", true );
		meta.setAttribute(
			new Attribute( "http-equiv", "Content-Type" ),
			new Attribute( "content", "text/html; charset=UTF-8" )
		);
		Tag	head = getFirstTagByType( "head" );
		if ( head != null ) {
			head.addHTMLPart( 0, meta );
		}
	}

	void entryPage( PrePage page ) {
		String	uri = page.getMyselfURI().toString();
		org_html.add( uri );
		page_map.put( uri, page );
	}

	void entryExclude( String path ) {
		path = Output.toHtmlPath( path );
		path = path.substring( Param.HTML_ROOT.get().length() );
		if ( path.charAt( 0 ) == '/' ) {
			path = path.substring( 1 );
		}
		ex_file.add( path );
		ex_map.put( path, path );
	}

	private void makeBase() throws Exception {
		Output[]    output = {
			new paraselene.mockup.output.source.base.PageLoader(),
			new paraselene.mockup.output.source.base.PageType(),
			new paraselene.mockup.output.source.base.Json(),
			new paraselene.mockup.output.source.base.FormJson(),
			new paraselene.mockup.output.source.base.Resource(),
		};
		String[]	name = {
			"PageLoader.java",
			"PageType.java",
			"Json.java",
			"FormJson.java",
			"Resource.java",
		};
		for ( int i = 0; i < name.length; i++ ) {
			if ( output[i] == null )	continue;
			StringBuilder    out_path = new StringBuilder( Make.SRC_PATH );
			out_path = out_path.append( File.separator );
			out_path = out_path.append( Param.PACKAGE_BASE.get() );
			out_path = out_path.append( File.separator );
			out_path = out_path.append( name[i] );
			String  p_name = out_path.toString();
			output[i].write( new File( p_name ), this, Long.MAX_VALUE );
			readme.echoln( p_name + " output ended." );
		}
	}

	private void makeWeb() throws Exception {
		Output[]	output = {
			new paraselene.mockup.output.build(),
			new paraselene.mockup.output.web(),
		};
		String[]	name = { "build.xml", "web.xml" };
		for ( int i = 0; i < name.length; i++ ) {
			StringBuilder	buf = new StringBuilder( Param.OUT_PATH.get() );
			buf = buf.append( File.separator );
			buf = buf.append( name[i] );
			File	file = new File( buf.toString() );
			if ( file.exists() ) {
				Linker.readme.echoln( file.toString() + " is not output, because it already exists." );
				continue;
			}
			output[i].write( file, this, Long.MAX_VALUE );
			readme.echoln( file.toString() + " output ended." );
		}
	}

	private void makeReadMe() throws Exception {
		int	cnt;
		getTitleTitle().setValueString( Param.PACKAGE.get() );
		getRootTd().setValueString( "<" + Param.OUT_PATH.get() + ">" );
		getSourceTd().setValueString( " <" + Param.OUT_SOURCE_PATH.get() + ">" );
		getBaseSpan().setValueString( Param.PACKAGE_BASE.get() );
		getViewSpan().setValueString( Param.PACKAGE_VIEW.get() );
		Tag[]	logic = getLogicTags();
		for ( int i = 0; i < logic.length; i++ ) {
			logic[i].setValueString( Param.PACKAGE_LOGIC.get() );
		}

		Table	table = getClassListTable();
		Line	line = table.getLineReplica( 1 );
		table.removeLine( 1, -1 );
		cnt = org_html.size();
		String	ex_path = null;
		for ( int i = 0; i< cnt; i++ ) {
			PrePage	page = page_map.get( org_html.get( i ) );
			Line	l = (Line)line.getReplica();
			String	path = page.getMyselfURI().getPath();
			l.setValueString( 0,
				Integer.toString( i + 1 ),
				Output.toOSPath( path ),
				page.getTitle(),
				page.getClassName(),
				page.getEnumName( false ),
				TransactionSequencer.makeURI( null, null, page.getID(), 0, 0, false )
			);
			String	note = page.getNote();
			if ( note == null )	note = "";
			Column[]	column = l.getColumnArray();
			if ( !note.isEmpty() ) {
				Column	note_column = column[column.length - 1];
				note_column.setValueString( note );
				note_column.setAttribute( new Attribute( "bgcolor", new Color( WebColor.YELLOW ) ) );
			}
			Column	path_clm = column[column.length - 2];
			String	path_str = path_clm.getValueString();
			if ( ex_path == null )	ex_path = path_str;
			Tag	tag = new Tag( "a", false );
			tag.setAttribute( new Attribute( "href", "javascript:load('" + path_str + "');" ) );
			tag.setValueString( path_str );
			path_clm.removeHTMLPart();
			path_clm.addHTMLPart( tag );
			table.addLine( l );
		}
		Tag[]	ex = getExTags();
		for ( int i = 0; i < ex.length; i++ ) {
			ex[i].setValueString( ex_path );
		}

		table = getExListTable();
		line = table.getLineReplica( 1 );
		table.removeLine( 1, -1 );
		cnt = ex_file.size();
		for ( int i = 0; i< cnt; i++ ) {
			Line	l = (Line)line.getReplica();
			String	path = ex_file.get( i );
			l.setValueString( 0, Integer.toString( i + 1 ),
				Output.toOSPath( path ),
				Param.OTHER.get() + "/" + ex_file.get( i ) );
			table.addLine( l );
		}

		StringBuilder	buf = new StringBuilder( Param.OUT_PATH.get() );
		buf = buf.append( File.separator );
		buf = buf.append( "Readme.html" );
		String  name = buf.toString();
		PrintWriter	pw = new PrintWriter( name, "UTF-8" );
		pw.print( "<!-- saved from url=(0033)http://paraselene.sourceforge.jp/ -->\r\n" );
		write( pw );
		pw.close();
		readme.echoln( name + " output ended." );
	}

	void output() throws Exception {
		Collections.sort( org_html );
		Collections.sort( ex_file );

		int	cnt = org_html.size();
		for ( int i = 0; i < cnt; i++ ) {
			PrePage	page = page_map.get( org_html.get( i ) );
			page.makeSource();
		}

		if ( Boolean.valueOf( Param.WEB_APP.get() ) ) {
			makeBase();
			makeWeb();
			makeReadMe();
		}
	}

	void setCurrentPage( PrePage p ) {
		crt_page = p;
	}

	public void addNote( String s ) {
		if ( crt_page == null )	return;
		crt_page.addNote( s );
	}

	private String fixPath( String path ) {
		String[]	part = path.split("[/\\\\]" );
		ArrayList<String>	list = new ArrayList<String>();
		for ( int i = 0; i < part.length; i++ ) {
			if ( part[i].length() == 0 )	continue;
			if ( ".".equals( part[i]) )	continue;
			if ( "..".equals( part[i] ) ) {
				int	size = list.size();
				if ( size == 0 )	return null;
				list.remove( size - 1 );
				continue;
			}
			list.add( part[i] );
		}
		int	size = list.size();
		StringBuilder	buf = new StringBuilder();
		for ( int i = 0; i < size; i++ ) {
			buf = buf.append( list.get( i ) );
			if ( i != (size - 1) )	buf = buf.append( "/" );
		}
		return buf.toString();
	}

	String resolvePath( URI uri ) throws Exception {
		String	uri_str = uri.toString();
		if ( !Boolean.valueOf( Param.WEB_APP.get() ) ) {
			uri_str = URLDecoder.decode( uri_str, URIValue.DEFAULT_ENC );
			return Make.escape( uri_str );
		}

		URIHandle	handle = new URIHandle( uri, URIHandle.URIProcess.FULL_PROCESS );
		if ( Param.uri_pp != null ) {
			URIHandle	handle_new = null;
			try {
				handle_new = Param.uri_pp.getHandle( crt_page.getClassName(), uri );
			}
			catch( Exception e ) {
				e.printStackTrace();
				throw e;
			}
			if ( handle_new != null ) {
				handle = handle_new;
				uri = handle.getURI();
				String	uri_str_new = uri.toString();
				if ( !uri_str_new.equals( uri_str ) ) {
					StringBuilder	note = new StringBuilder( "URIPreProcessにより " );
					note = note.append( uri_str );
					note = note.append( " が " );
					note = note.append( uri_str_new );
					note = note.append( " に変換されました。" );
					addNote( note.toString() );
					uri_str = uri_str_new;
				}
			}
		}
		URIHandle.URIProcess	process = handle.getURIProcess();
		if ( process == URIHandle.URIProcess.NO_PROCESS ) {
			StringBuilder	note = new StringBuilder( "URIPreProcessにより " );
			note = note.append( uri_str );
			note = note.append( " は URI解決しませんでした。" );
			addNote( note.toString() );
			uri_str = URLDecoder.decode( uri_str, URIValue.DEFAULT_ENC );
			return Make.escape( uri_str );
		}

		if ( uri_str.charAt( 0 ) == '#' ){
			uri_str = URLDecoder.decode( uri_str, URIValue.DEFAULT_ENC );
			return Make.escape( uri_str );
		}
		if ( uri.getScheme() != null ) {
			addNote( "絶対パスがありました(" + uri_str + ")" );
			uri_str = URLDecoder.decode( uri_str, URIValue.DEFAULT_ENC );
			return Make.escape( uri_str );
		}
		String	path = uri.getPath();
		if ( path.charAt( 0 ) == '/' ) {
			addNote( "ルートからのパスがありました(" + path + ")" );
			uri_str = URLDecoder.decode( uri_str, URIValue.DEFAULT_ENC );
			return Make.escape( uri_str );
		}
		uri = crt_page.getMyselfURI().resolve( uri );
		path = fixPath( uri.getPath() );
		PrePage	page = page_map.get( path );
		if ( page != null ) {
			StringBuilder	buf = new StringBuilder( "URIValue.pageToURI(" );
			buf = buf.append( page.getEnumName( false ) );
			buf = buf.append( "," );
			String	frag = uri.getFragment();
			if ( frag == null )	{
				buf = buf.append( "null" );
			}
			else {
				frag = URLDecoder.decode( frag, URIValue.DEFAULT_ENC );
				buf = buf.append( Make.escape( frag ) );
			}
			String	query = uri.getQuery();
			if ( query != null ) {
				String[]	q_part = query.split( "&" );
				for ( int i = 0; i < q_part.length; i++ ) {
					buf = buf.append( "," );
					String[]	q = q_part[i].split( "=" );
					for ( int j = 0; j < q.length; j++ ) {
						q[j] = URLDecoder.decode( q[j], URIValue.DEFAULT_ENC );
					}
					buf = buf.append( "new QueryItem(" );
					buf = buf.append( Make.escape( q[0] ) );
					if ( q.length > 1 ) {
						buf = buf.append( "," );
						buf = buf.append( Make.escape( q[1] ) );
					}
					buf = buf.append( ")" );
				}
			}
			buf = buf.append( ")" );
			int	port = uri.getPort();
			if ( port == -1 )	port = 0;
			String	sch = uri.getScheme();
			if ( sch != null ) {
				StringBuilder	sub = new StringBuilder( "URIValue.makeAbsolutePath(" );
				sub = sub.append( "http".equals( sch )?	"URIValue.Scheme.HTTP,":	"URIValue.Scheme.HTTPS," );
				sub = sub.append( port );
				sub = sub.append( "," );
				sub = sub.append( buf );
				buf = sub.append( ")" );
			}
			return buf.toString();
		}

		if ( process == URIHandle.URIProcess.NOT_USE_OTHER ) {
			StringBuilder	note = new StringBuilder( "URIPreProcessにより " );
			note = note.append( uri_str );
			note = note.append( " は " );
			note = note.append( Param.OTHER.key );
			note = note.append( " を適用しませんでした。" );
			addNote( note.toString() );
			uri_str = URLDecoder.decode( uri_str, URIValue.DEFAULT_ENC );
			return Make.escape( uri_str );
		}
		String	ex_path = Output.toHtmlPath( path );
		String	ex = ex_map.get( ex_path );
		if ( ex != null ) {
			String	str = Param.OTHER.get().trim();
			StringBuilder	buf = new StringBuilder( str );
			if ( str.charAt( str.length() - 1 ) != '/' ) {
				buf = buf.append( "/" );
			}
			buf = buf.append( ex_path );
			String	query = uri.getQuery();
			if ( query != null ) {
				buf = buf.append( "?" );
				buf = buf.append( query );
			}
			String	frag = uri.getFragment();
			if ( frag != null ) {
				buf = buf.append( "#" );
				buf = buf.append( frag );
			}
			return Make.escape( URLDecoder.decode( new URI( buf.toString() ).toString(), URIValue.DEFAULT_ENC ) );
		}
		addNote( "警告:" + path + "が解決できませんでした。" );
		uri_str = URLDecoder.decode( uri_str, URIValue.DEFAULT_ENC );
		return Make.escape( uri_str );
	}

	public void echo( String str ) {
		if ( !flag )	return;
		Tag	div = getEchoDiv();
		Text	text = null;
		if ( div.getHTMLPartCount() == 0 ) {
			text = new Text( "" );
			div.addHTMLPart( text );
		}
		else {
			text = (Text)div.getHTMLPart( 0 );
		}
		text.append( str );
		System.out.print( str );
	}

	public void echoln( String str ) {
		echo( str + "\n" );
	}

}

