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


import java.util.HashMap;
import java.util.ArrayList;
import java.util.Locale;
import java.net.*;
import paraselene.*;
import paraselene.tag.*;
import paraselene.tag.list.*;
import paraselene.tag.form.*;
import paraselene.tag.table.*;
import paraselene.dyna.*;
import paraselene.css.*;

abstract class TagNew {
	abstract String toString( Tag t );

	static String append( String ... s ) {
		StringBuilder	buf = new StringBuilder();
		for ( int i = 0; i < s.length; i++ ) {
			buf = buf.append( s[i] );
		}
		return buf.toString();
	}
}

abstract class DynaTag {
	static Tag setAttr( Tag t, ArrayList<Attribute> attr ) {
		int	cnt = attr.size();
		for ( int i = 0; i < cnt; i++ ) {
			t.setAttribute( attr.get( i ) );
		}
		return t;
	}

	static String seek( ArrayList<Attribute> attr, String name ) {
		int	cnt = attr.size();
		for ( int i = 0; i < cnt; i++ ) {
			Attribute	a = attr.get( i );
			if ( name.equals( a.getName() ) ) {
				return a.getString();
			}
		}
		return null;
	}

	abstract Tag getTag( String name, ArrayList<Attribute> attr );
}

@SuppressWarnings("unchecked")
public class TagMap {
	private static HashMap<String,Class<Tag>>	class_real = new HashMap<String,Class<Tag>>();
	private static HashMap<String,Object>	new_str = new HashMap<String,Object>();
	private static HashMap<String,DynaTag>	tag_str = new HashMap<String,DynaTag>();

	static final String TAG = "Tag";

	static private String getKey( Tag t ) throws TagException {
		String	name = t.getName();
		if ( "input".equals( name ) ) {
			Attribute	attr = t.getAttribute( "type" );
			if ( attr == null ) {
				throw new TagException( "type指定の無いINPUTタグがあります" );
			}
			AttributeValuable	txt = attr.get();
			if ( txt == null ) {
				throw new TagException( "type属性が不正です" );
			}
			name = txt.toString();
			if ( class_real.get( name ) == null ) {
				throw new TagException( "type属性が不正です" );
			}
			return name;
		}
		if ( class_real.get( name ) == null ) {
			return TAG;
		}
		return name;
	}

	static Class<Tag> getClass( Tag t ) throws TagException {
		String	name = getKey( t );
		return class_real.get( name );
	}

	static String getClassString( Tag t ) throws TagException {
		return getSource( t )[0];
	}

	static String[] getSource( Tag t ) throws TagException {
		if ( Param.provider != null ) {
			GrantTag	gt = null;
			try {
				for ( int i = 0; i < Param.provider.length; i++ ) {
					gt = Param.provider[i].getGrantTag( t );
					if ( gt != null )	break;
				}
				if ( gt != null ) {
					return new String[] {
						gt.getNewTag().getClass().getName(),
						gt.getNewString()
					};
				}
			}
			catch( Exception e ) {
				throw new TagException( e.getMessage() );
			}
		}

		String	ret[] = new String[] {
			getClass( t ).getName(), null
		};
		Object	o = new_str.get( getKey( t ) );
		if ( o instanceof TagNew ) {
			ret[1] = ((TagNew)o).toString( t );
		}
		else {
			ret[1] = (String)o;
		}
		return ret;
	}

	public static Tag makeTag( String n, ArrayList<Attribute> attr, String enc, URIResolver ur, GrantTagProvider[] ex ) throws Exception {
		GrantTag	gt = null;
		Tag	t = new Tag( n, DynamicPage.isSimpleTag( n ) );
		t.setAttribute( attr.toArray( new Attribute[0] ) );
		if ( ex != null ) {
			for ( int i = 0; i < ex.length; i++ ) {
				gt = ex[i].getGrantTag( t );
				if ( gt != null )	break;
			}
			if ( gt != null )	t = gt.getNewTag();
		}
		int	cnt = attr.size();
		for ( int i = 0; i < cnt; i++ ) {
			Attribute	a = attr.get( i );
			String	a_name = a.getName();
			String	a_val = a.getString();
			if ( Converter.isURI( a_name ) ) {
				a_val = ur.resolve( a_val );
			}
			if ( Converter.isColor( a_name ) ) {
				a = new Attribute( a_name, new Color( a_val ) );
				attr.set( i, a );
			}
			else if ( Converter.isURI( a_name ) ) {
				if ( a_val == null )	a_val = "#";
				else {
					a_val = a_val.trim();
					if ( a_val.isEmpty() )	a_val = "#";
				}
				try {
					URI u = new URI( a_val );
					a_val = URLDecoder.decode( u.toString(), enc );
				}
				catch( Exception e ) {}
				a = new Attribute( a_name, new URIValue( a_val, enc ) );
				attr.set( i, a );
			}
			else if ( Converter.isNumber( a_name ) ) {
				a = new Attribute( a_name, new NumberValue( a_val ) );
				attr.set( i, a );
			}
			else if ( Converter.isStyle( a_name ) ) {
				a = new Attribute( a_name, new Style( Property.create( a_val, enc ) ) );
				attr.set( i, a );
			}
			t.setAttribute( a );
		}
		if ( gt != null )	return ScanParam( t, ur, enc );

		String	t_name = getKey( t );
		if ( TAG.equals( t_name ) )	return t;
		Attribute[]	a = t.getAllAttribute();
		attr.clear();
		for ( int i = 0; i < a.length; i++ ) {
			attr.add( a[i] );
		}
		return ScanParam( tag_str.get( t_name ).getTag( n, attr ), ur, enc );
	}

	public static Tag ScanParam( Tag t, URIResolver ur, String enc ) throws Exception {
		if ( !"param".equals( t.getName() ) )	return t;
		Attribute	name_atr = t.getAttribute( "name" );
		Attribute	value_atr = t.getAttribute( "value" );
		if ( name_atr == null || value_atr == null )	return t;
		String	name = name_atr.getString();
		String	value = value_atr.getString();
		if ( name == null || value == null )	return t;
		if ( !ur.isParamURIName( name.toLowerCase( Locale.ENGLISH ) ) )	return t;
		t.setAttribute( new Attribute( name, new URIValue( ur.resolve( value ), enc ) ) );
		return t;
	}

	private static final Object[][]	dim = new Object[][]{
	// input
		{ "hidden", paraselene.tag.form.Input.class,
			new TagNew() {
				public String toString( Tag t ) {
					return append( "Input.createHidden( ", Make.escape( t.getNameAttribute() ), ",", Make.escape( t.getValueString() ), ")" );
				}
			},
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr(
						Input.createHidden( seek( attr, "name" ),
							seek( attr, "value" ) ),
						attr
					);
				}
			}
		},
		{ "file", paraselene.tag.form.UploadFile.class,
			"new UploadFile()",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new UploadFile(), attr );
				}
			}
		},
		{ "button", paraselene.tag.form.Button.class,
			"new Button(Button.Type.BUTTON)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new Button( Button.Type.BUTTON ), attr );
				}
			}
		},
		{ "image", paraselene.tag.form.Button.class,
			"new Button(Button.Type.IMAGE)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new Button( Button.Type.IMAGE ), attr );
				}
			}
		},
		{ "reset", paraselene.tag.form.Button.class,
			"new Button(Button.Type.RESET)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new Button( Button.Type.RESET ), attr );
				}
			}
		},
		{ "submit", paraselene.tag.form.Button.class,
			"new Button(Button.Type.SUBMIT)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new Button( Button.Type.SUBMIT ), attr );
				}
			}
		},
		{ "radio", paraselene.tag.form.CheckBox.class,
			"new CheckBox(true)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new CheckBox( true ), attr );
				}
			}
		},
		{ "checkbox", paraselene.tag.form.CheckBox.class,
			"new CheckBox(false)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new CheckBox( false ), attr );
				}
			}
		},
		{ "password", paraselene.tag.form.SingleTextBox.class,
			"new SingleTextBox(true)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new SingleTextBox( true ), attr );
				}
			}
		},
		{ "text", paraselene.tag.form.SingleTextBox.class,
			"new SingleTextBox(false)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new SingleTextBox( false ), attr );
				}
			}
		},

	// 通常
		{ "a", paraselene.tag.Anchor.class,
			"new Anchor()",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new Anchor(), attr );
				}
			}
		},
		{ "form", paraselene.tag.form.Form.class,
			"new Form()",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new Form(), attr );
				}
			}
		},
		{ "textarea", paraselene.tag.form.MultiTextBox.class,
			"new MultiTextBox()",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new MultiTextBox(), attr );
				}
			}
		},
		{ "select", paraselene.tag.form.Select.class,
			"new Select()",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new Select(), attr );
				}
			}
		},
		{ "option", paraselene.tag.form.SelectItem.class,
			"new SelectItem()",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new SelectItem(), attr );
				}
			}
		},
		{ "ul", paraselene.tag.list.List.class,
			"new List(List.Type.UNORDERED)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new List( List.Type.UNORDERED ), attr );
				}
			}
		},
		{ "ol", paraselene.tag.list.List.class,
			"new List(List.Type.ORDERED)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new List( List.Type.ORDERED ), attr );
				}
			}
		},
		{ "dl", paraselene.tag.list.List.class,
			"new List(List.Type.DEFINITION)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new List( List.Type.DEFINITION ), attr );
				}
			}
		},
		{ "li", paraselene.tag.list.ListItem.class,
			"new ListItem(ListItem.Type.LIST_ITEM)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new ListItem( ListItem.Type.LIST_ITEM ), attr );
				}
			}
		},
		{ "dt", paraselene.tag.list.ListItem.class,
			"new ListItem(ListItem.Type.DEFINITION_TERM)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new ListItem( ListItem.Type.DEFINITION_TERM ), attr );
				}
			}
		},
		{ "dd", paraselene.tag.list.ListItem.class,
			"new ListItem(ListItem.Type.DEFINITION_DESCRIPTION)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new ListItem( ListItem.Type.DEFINITION_DESCRIPTION ), attr );
				}
			}
		},
		{ "table", paraselene.tag.table.Table.class,
			"new Table()",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new Table(), attr );
				}
			}
		},
		{ "tr", paraselene.tag.table.Line.class,
			"new Line()",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new Line(), attr );
				}
			}
		},
		{ "th", paraselene.tag.table.Column.class,
			"new Column(Column.Type.HEADER)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new Column( Column.Type.HEADER ), attr );
				}
			}
		},
		{ "td", paraselene.tag.table.Column.class,
			"new Column(Column.Type.DATA)",
			new DynaTag() {
				Tag getTag( String name, ArrayList<Attribute> attr ) {
					return setAttr( new Column( Column.Type.DATA ), attr );
				}
			}
		},
	};

	static {
		for ( int i = 0; i < dim.length; i++ ) {
			class_real.put( (String)dim[i][0], (Class<Tag>)dim[i][1] );
			new_str.put( (String)dim[i][0], dim[i][2] );
			tag_str.put( (String)dim[i][0], (DynaTag)dim[i][3] );
		}
		class_real.put( TAG, paraselene.tag.Tag.class );
		new_str.put( TAG, new TagNew() {
				public String toString( Tag t ) {
					return append( "new Tag(", Make.escape( t.getName() ), ",", Boolean.toString( t.isSimpleTag() ), ")" );
				}
			}
		);
	}

}

