/**
 * @(#)PaneledElementFactory.java
 * Copyright 2001 Nomura Research Institute, LTD. All rights reserved.
 * RID-3 : Paneled Element Factory
 * @author  Intelligent Technology Inc.
 * @version 1.00 2001/10/01-2002/03/25
 * @see 
 */
package	jp.co.nri.rid.xml;

/* W3C DOM */
import	org.w3c.dom.*;

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;

import	jp.co.nri.rid.*;
import	jp.co.nri.rid.table.editor.*;
import	jp.co.nri.rid.error.*;
import jp.co.nri.rid.environment.*;

import	jp.co.iti.fagot.doc.*;
import	jp.co.iti.fagot.util.*;

/**
 * プロパティパネルを作るファクトリ
 */

public class PaneledElementFactory{
	/* layout tag */
	public static final String BORDERLAYOUT	=	"BorderLayout";
	public static final String FLOWLAYOUT	=	"FlowLayout";
	public static final String BOXLAYOUT	=	"BoxLayout";
	public static final String GRIDLAYOUT	=	"GridLayout";
	public static final String TABLE		=	"Table";
	
	/* layout attribute */
	public static final String SCROLL	=	"scroll";
	public static final String ROWS		=	"rows";
	public static final String COLUMNS	=	"columns";
	public static final String AXIS		=	"axis";
	public static final String AXISX	=	"x";
	public static final String AXISY	=	"y";
	public static final String FILL		=	"fill";
	public static final String ALIGN	=	"align";
	public static final String VALIGN	=	"valign";
	public static final String COLSPAN	=	"colspan";
	public static final String ROWSPAN	=	"rowspan";
	public static final String EDITABLE	=	"editable";
	public static final String RESIZEBLE=	"resizeble";
	public static final String WIDTH	=	"width";
	public static final String CELLPADDING	=	"cellpadding";
	
	/* layout child tag */
	public static final String CENTER	=	"center";
	public static final String NORTH	=	"north";
	public static final String SOUTH	=	"south";
	public static final String EAST		=	"east";
	public static final String WEST		=	"west";
	public static final String TR		=	"tr";
	public static final String TD		=	"td";
	
	/* component tag */
	public static final String LABEL	=	"label";
	public static final String TEXT		=	"text";
	public static final String RIDEDITOR=	"RidEditor";
	
	/* component attribute */
	public static final String ACTION		=	"action";
	public static final String CHILD		=	"child";
	public static final String ATTRIBUTE	=	"attribute";
	public static final String STRING		=	"string";
	
	public static final String FALSE		=	"false";
	public static final String HORIZONTAL	=	"horizontal";
	public static final String VERTICAL		=	"vertical";
	public static final String BOTH			=	"both";
	public static final String CENTERs		=	"center";
	public static final String LEFTs		=	"left";
	public static final String RIGHTs		=	"right";
	public static final String MIDDLEs		=	"middle";
	public static final String TOPs			=	"top";
	public static final String BOTTOMs		=	"bottom";
	
	public static final int	CENTERi		=	0x00;
	public static final int	LEFTi		=	0x01;
	public static final int	RIGHTi		=	0x02;
	public static final int	MIDDLEi		=	0x00;
	public static final int	TOPi		=	0x10;
	public static final int	BOTTOMi		=	0x20;
	public static final int	TOPLEFTi	=	0x11;
	public static final int	TOPRIGHTi	=	0x12;
	public static final int	BOTTOMLEFTi	=	0x21;
	public static final int	BOTTOMRIGHTi=	0x22;
	
	public elementBooster myElement;
	
	/**
	 * 入力パネルを取得する。
	 * @param	element	データエレメント
	 * @param	layout	レイアウトデータ
	 * @return	入力パネル
	 * @since	1.00
	 */
	public JComponent PaneledElement(Element element ,Element layout){
		return	PaneledElement(new elementBooster(element) ,new elementBooster(layout));
	}
	
	/**
	 * 入力パネルを取得する。
	 * @param	layout	レイアウトデータ
	 * @return	入力パネル
	 * @since	1.00
	 */
	public JComponent PaneledElement(elementBooster layout){
		return PaneledElement(myElement ,layout);
	}
	
	/**
	 * 入力パネルを取得する。
	 * @param	element	データエレメント
	 * @param	layout	レイアウトデータ
	 * @return	入力パネル
	 * @since	1.00
	 */
	public JComponent PaneledElement(elementBooster element ,elementBooster layout){
		if(null==element)	return null;
		if(null==layout)	return null;
		
		myElement = element;
		
		JComponent component = null;
		
		// TAGNAME == BorderLayout | FrowLayout | TEXT
		String name = layout.tagname();
		if(BORDERLAYOUT.equals(name))		component = caseBorderLayout(layout);
		else if(TABLE.equals(name))			component = caseGridBagLayout(layout);
		else if(FLOWLAYOUT.equals(name))	component = caseFlowLayout(layout);
		else if(BOXLAYOUT.equals(name))		component = caseBoxLayout(layout);
		else if(GRIDLAYOUT.equals(name))	component = caseGridLayout(layout);
		else if(LABEL.equals(name))			component = caseLabel(layout);
		else if(TEXT.equals(name))			component = caseText(layout);
		else if(RIDEDITOR.equals(name))		component = caseRidEditor(layout);
		
		return component;
	}
	
	// GridBagLayout Panel
	protected JPanel caseGridBagLayout(elementBooster layout){
		GridBagLayout gridbag = new GridBagLayout();
		JPanel panel = new JPanel(gridbag);
		
		// child
		elementBooster[] tr = layout.children(TR);
		if(null!=tr){
			int cellpadding = 0;
			try{ cellpadding = Integer.parseInt(layout.attribute(CELLPADDING)); }
			catch(NumberFormatException e){}
			int mx = tr.length;
			for(int i=0;i<mx;i++)	addTR(panel ,gridbag ,tr[i] ,cellpadding);
		}
		
		JPanel spacer = new JPanel();
		GridBagConstraints spacerconstraints = new GridBagConstraints();
		spacerconstraints.weightx = 0.001;
		spacerconstraints.weighty = 0.001;
		spacerconstraints.gridwidth = GridBagConstraints.REMAINDER;
		gridbag.setConstraints(spacer ,spacerconstraints);
		panel.add(spacer);
		return panel;
	}
	
	protected void addTR(JPanel panel ,GridBagLayout gridbag ,elementBooster tr ,int cellpadding){
		elementBooster[] td = tr.children(TD);
		int mx = td.length;
		for(int i=0;i<mx;i++){
			Component component = PaneledElement(td[i].children()[0]);
			if(null==component)	continue;
			gridbag.setConstraints(component ,constraints(td[i] ,cellpadding));
			panel.add(component);
		}
		
		JPanel spacer = new JPanel();
		GridBagConstraints spacerconstraints = new GridBagConstraints();
//		spacerconstraints.weightx = 0.001;
//		spacerconstraints.weighty = 0.001;
		spacerconstraints.gridwidth = GridBagConstraints.REMAINDER;
		gridbag.setConstraints(spacer ,spacerconstraints);
		panel.add(spacer);
	}
	
	protected GridBagConstraints constraints(elementBooster td ,int cellpadding){
		GridBagConstraints c = new GridBagConstraints();
//		if(1==(count-ix))	c.gridwidth = GridBagConstraints.REMAINDER;
		
		c.fill = GridBagConstraints.HORIZONTAL;
		c.insets = new Insets(cellpadding ,cellpadding ,cellpadding ,cellpadding);
		
		// align
		int aligns;
		String align = td.attribute(ALIGN);
		if(null==align)					aligns = CENTERi;
		else if(CENTERs.equals(align))	aligns = CENTERi;
		else if(LEFTs.equals(align))	aligns = LEFTi;
		else if(RIGHTs.equals(align))	aligns = RIGHTi;
		else							aligns = CENTERi;
		
		String valign = td.attribute(VALIGN);
		if(null==valign)				aligns += MIDDLEi;
		else if(MIDDLEs.equals(valign))	aligns += MIDDLEi;
		else if(TOPs.equals(valign))	aligns += TOPi;
		else if(BOTTOMs.equals(valign))	aligns += BOTTOMi;
		else							aligns += MIDDLEi;
		
		switch(aligns){
		case CENTERi:
			c.anchor = GridBagConstraints.CENTER;
			break;
		case LEFTi:
			c.anchor = GridBagConstraints.WEST;
			break;
		case RIGHTi:
			c.anchor = GridBagConstraints.EAST;
			break;
		case TOPi:
			c.anchor = GridBagConstraints.NORTH;
			break;
		case BOTTOMi:
			c.anchor = GridBagConstraints.SOUTH;
			break;
		case TOPLEFTi:
			c.anchor = GridBagConstraints.NORTHWEST;
			break;
		case TOPRIGHTi:
			c.anchor = GridBagConstraints.NORTHEAST;
			break;
		case BOTTOMLEFTi:
			c.anchor = GridBagConstraints.SOUTHWEST;
			break;
		case BOTTOMRIGHTi:
			c.anchor = GridBagConstraints.SOUTHEAST;
			break;
		}
		
		// colspan
		try{ c.gridwidth = Integer.parseInt(td.attribute(COLSPAN)); }
		catch(NumberFormatException e){}
		
		// rowspan
		try{ c.gridheight = Integer.parseInt(td.attribute(ROWSPAN)); }
		catch(NumberFormatException e){}
		
		// width
		try{ c.weightx = Double.parseDouble(td.attribute(WIDTH)); }
		catch(NullPointerException e){}
		
		return c;
	}
	
	// BorderLayout Panel
	protected JPanel caseBorderLayout(elementBooster layout){
		JPanel panel = new JPanel(new BorderLayout());
		
		// child
		elementBooster element = layout.child(CENTER);
		add(panel ,BorderLayout.CENTER ,element);
		
		element = layout.child(NORTH);
		add(panel ,BorderLayout.NORTH ,element);
		
		element = layout.child(SOUTH);
		add(panel ,BorderLayout.SOUTH ,element);
		
		element = layout.child(EAST);
		add(panel ,BorderLayout.EAST ,element);
		
		element = layout.child(WEST);
		add(panel ,BorderLayout.WEST ,element);
		
		return panel;
	}
	
	// FlowLayout Panel
	protected JPanel caseFlowLayout(elementBooster layout){
		JPanel panel = new JPanel(new FlowLayout());
		
		// child
		add(panel ,null ,layout);
		
		return panel;
	}
	
	// BoxLayout Panel
	protected JPanel caseBoxLayout(elementBooster layout){
		JPanel panel = new JPanel();
		String axis = layout.attribute(AXIS);
		if(AXISY.equals(axis))	panel.setLayout(new BoxLayout(panel ,BoxLayout.Y_AXIS));
		else					panel.setLayout(new BoxLayout(panel ,BoxLayout.X_AXIS));
		
		// child
		add(panel ,null ,layout);
		
		return panel;
	}
	
	// GridLayout Panel
	protected JPanel caseGridLayout(elementBooster layout){
		int rows = layout.attributeInt(ROWS);
		int cols = layout.attributeInt(COLUMNS);
		JPanel panel = new JPanel(new GridLayout(rows ,cols));
		
		// child
		add(panel ,null ,layout);
		
		return panel;
	}
	
	protected JTextField caseText(elementBooster layout){
		JTextField text = new JTextField();
		String string = layout.attribute(CHILD);
		if(null!=string){
			ElementListener listener = new ElementListener(myElement ,string ,text);
			text.addFocusListener(listener);
			text.addAncestorListener(listener);
		}
		
		string = layout.attribute(ATTRIBUTE);
//		if(null!=string)	text.getDocument().addDocumentListener(new AttributeListener(myElement ,string ,text));
		if(null!=string){
			AttributeListener listener = new AttributeListener(myElement ,string ,text);
			text.addFocusListener(listener);
			text.addAncestorListener(listener);
		}
/*
		string = layout.attribute(ACTION);
		if(null!=string){
			try{
				ActionListener listener = (ActionListener)Class.forName(string).newInstance();
				text.getDocument().addDocumentListener(new DocumentActionListener(listener));
			
			}catch(Exception exp){  }
		}
*/
		return text;
	}
	
	protected JComponent caseRidEditor(elementBooster layout){
		String attribute = layout.attribute(ATTRIBUTE);
		if(null==attribute)	return null;
		
		ZProperties	properties = DocManager.loadProperty( DocManager.ELEMENT );
		String type = properties.getProperty("attributes."+attribute+".type");
		if(null==type)	return null;
		
		boolean editable = true;
		if(FALSE.equals(layout.attribute(EDITABLE)))	editable = false;
		
		iCellEditorComponent component = new RCellEditor(type ,editable ,new CECAdapter(myElement ,attribute)).CEC();
		String text = myElement.attribute(attribute);
		if(component instanceof iReloadable)	((iReloadable)component).reload();
		if(component instanceof iChildReloadable)	((iChildReloadable)component).reload(myElement);
		component.text(text);
		if(component instanceof TextCEC){
//			((TextCEC)component).getDocument().addDocumentListener(new AttributeListener(myElement ,attribute ,(TextCEC)component));
			AttributeListener listener = new AttributeListener(myElement ,attribute ,(TextCEC)component);
			((TextCEC)component).addFocusListener(listener);
			((TextCEC)component).addAncestorListener(listener);
		}
		else if(component instanceof ColorCEC){
//			((ColorCEC)component).getTextField().getDocument().addDocumentListener(new ColorAttributeListener(myElement ,attribute ,(ColorCEC)component));
			ColorAttributeListener listener = new ColorAttributeListener(myElement ,attribute ,(ColorCEC)component);
			((ColorCEC)component).getTextField().addActionListener(listener);
			((ColorCEC)component).getTextField().addFocusListener(listener);
			((ColorCEC)component).getTextField().addAncestorListener(listener);
		}
		
		return component.component();
	}
	
	
	protected JLabel	 caseLabel(elementBooster	layout){ return new JLabel(layout.attribute(STRING)); }
	
	protected void add(JPanel panel ,String layout ,elementBooster element){
		if(null==panel)		return;
		if(null==element)	return;
		
		elementBooster[] children = element.children();
		int mx = children.length;
		for(int i=0;i<mx;i++){
			Component component = PaneledElement(children[i]);
			if(null!=component)	panel.add(component ,layout);
		}
	}
	
//	protected class ElementListener implements FocusListener ,DocumentListener{
	protected class ElementListener implements FocusListener ,AncestorListener{
		protected elementBooster	myElement;
		protected String			myChildName;
		protected JTextField		myText;
		public ElementListener(elementBooster element ,String name ,JTextField text){
			myElement	= element;
			myChildName	= new String(name);
			myText		= text;
			
			read();
		}
		
		public void ancestorAdded(AncestorEvent event){}
		public void ancestorMoved(AncestorEvent event){}
		public void ancestorRemoved(AncestorEvent event){ write(); }
//		public void insertUpdate(DocumentEvent e){	write(); }
//		public void removeUpdate(DocumentEvent e){	write(); }
//		public void changedUpdate(DocumentEvent e){	write(); }
		public void focusGained(FocusEvent evt){ read(); }
		public void focusLost(FocusEvent evt){ write(); }

		protected void write(){
			String value = myText.getText();
			
			if(null!=value){
				if(0>=value.length())	value = null;
			}
			
			// (改)abe attribute分の変更をelementにも反映
			// *** fuku(STA) *** 20020702 設定前チェック
			// ダイアログが表示されていない場合は設定しない
			// ガベージコレクション前でまだオブジェクトが残っている?
			if ( !myText.isShowing() ) {
				return;
			}

			// ダイアログが表示されたとたんにfireAncestorRemoved()が発生して2重Tran
			// になるので、値が同じ場合は設定しないよう修正
			String oldvalue = null;
			try{
				oldvalue = myElement.child(myChildName).text();
			}catch(NullPointerException e){}
			
			if(null==value){
				if(null==oldvalue){
					return;
				}
			}
			else{
				if(value.equalsIgnoreCase(oldvalue)){
					return;
				}
			}
			
			// 新規作成直後のundoではoldvalueがnullになっているので設定しない
			if ( myElement.element().getParentNode() == null ) {
				return;
			}
			// *** fuku(END) *** 20020702 設定前チェック
			
			boolean isTran = ((DocDocument)myElement.document().document()).isTransaction();
			if ( !isTran ) {
				((DocDocument)myElement.document().document()).beginTransaction();
			}
			
			elementBooster element = myElement.child(myChildName);
			if(null==element)	element = myElement.add(myChildName);
			element.text(value);
			
			if ( !isTran ) {
				((DocDocument)myElement.document().document()).commit();
			}
			
			String newvalue = element.text();
			
			if(null==value){
				if(null==newvalue){
					RUpdateManager.update(RUpdateManager.PROPERTY_CHANGED);
					return;
				}
			}
			else{
				if(value.equalsIgnoreCase(newvalue)){
					RUpdateManager.update(RUpdateManager.PROPERTY_CHANGED);
					return;
				}
			}
		}
		protected void read(){
			elementBooster element = myElement.child(myChildName);
			if(null==element)	return;
			String value = element.text();
			if(null==value)	return;
			
			myText.setText(value);
		}
	}
	
//	protected class AttributeListener implements FocusListener,DocumentListener{
//	protected class AttributeListener implements DocumentListener{
	protected class AttributeListener implements FocusListener ,AncestorListener{
		protected elementBooster	myElement;
		protected String			myAttributeName;
		protected JTextField		myText;
		public AttributeListener(elementBooster element ,String name ,JTextField text){
			myElement		= element;
			myAttributeName	= new String(name);
			myText			= text;
			read();
		}
		
//		public void insertUpdate(DocumentEvent e){	write(); }
//		public void removeUpdate(DocumentEvent e){	write(); }
//		public void changedUpdate(DocumentEvent e){	write(); }
		public void ancestorAdded(AncestorEvent event){}
		public void ancestorMoved(AncestorEvent event){}
		public void ancestorRemoved(AncestorEvent event){	write(); }
		public void focusLost(FocusEvent evt){				write(); }
		public void focusGained(FocusEvent evt){			read(); }
		protected void write(String value){
			if(null!=value){
				if(0>=value.length())	value = null;
			}
			
			// *** fuku(STA) *** 20020702 設定前チェック
			// ダイアログが表示されていない場合は設定しない
			// ガベージコレクション前でまだオブジェクトが残っている?
			if ( !myText.isShowing() ) {
				return;
			}

			// *** fuku(STA) *** 20020927 設定前チェック追加
			// actionListenerなどからsetAttributeしてエラーとなった場合、
			// エラーメッセージを表示した瞬間focusをlostしてしまい、ここにはいって
			// エラーメッセージが２度表示されることがあるので、エラーメッセージ表示
			// 中は setAttributeしないよう修正
			if ( ZInstance.prop().getBoolean("messagedialog.isShowing",false) ) {
				return;
			}
			// *** fuku(END) *** 20020927

			// ダイアログが表示されたとたんにfireAncestorRemoved()が発生して2重Tran
			// になるので、値が同じ場合は設定しないよう修正
			String oldvalue = myElement.attribute(myAttributeName);
			if(null==value){
				if(null==oldvalue){
					return;
				}
			}
			else{
				if(value.equalsIgnoreCase(oldvalue)){
					return;
				}
			}
			// 新規作成直後のundoではoldvalueがnullになっているので設定しない
			if ( myElement.element().getParentNode() == null ) {
				return;
			}
			// *** fuku(END) *** 20020702 設定前チェック

			boolean isTran = ((DocDocument)myElement.document().document()).isTransaction();
			if ( !isTran ) {
				((DocDocument)myElement.document().document()).beginTransaction();
			}
			myElement.attribute(myAttributeName ,value);
			if ( !isTran ) {
				((DocDocument)myElement.document().document()).commit();
			}
			String newvalue = myElement.attribute(myAttributeName);
			
			if(null==value){
				if(null==newvalue){
					RUpdateManager.update(RUpdateManager.PROPERTY_CHANGED);
					return;
				}
			}
			else{
				if(value.equalsIgnoreCase(newvalue)){
					RUpdateManager.update(RUpdateManager.PROPERTY_CHANGED);
					return;
				}
			}

			// *** fuku(STA) *** 20020927 setAttribute後チェック変更
			// 20020702のコメント
			// ファイルアトリビュートの変更で、setAttributeしても反映されないし
			// 他のタブシートに移動したときにダイアログが表示されるのもうっとうしいので
			// エラーは表示しない
			// 20020927のコメント
			// ファイルアトリビュートは変更不可となっている
			// エラーメッセージを表示して欲しいとリクエストがあったので表示するよう修正
			RidException re = (RidException)ZInstance.map().get("setAttribute");
			MessageDialog.showError(re.getMessage());
			// *** fuku(END) *** 20020927
			
//			myText.setText(newvalue);
		}
		protected void write(){	write(myText.getText()); }
		
		protected void read(){
			String value = myElement.attribute(myAttributeName);
			if(null==value)	return;
			myText.setText(value);
		}
	}
	
	private class ColorAttributeListener extends AttributeListener implements ActionListener{
		private ColorCEC cec;
		public ColorAttributeListener(elementBooster element ,String name ,ColorCEC editor){
			super(element ,name ,editor.getTextField());
			cec = editor;
		}
		
		private	boolean chgColor(){
			String value	= myText.getText();
			if(null==value)	return false;
			
			int		color = 0;
			try{	color = cec.hex(value); }
			catch(java.lang.NumberFormatException e){ return false; }
			
			cec.ButtonColor(color);
			
			return true;
		}
		
		public void actionPerformed(ActionEvent evt){ chgColor(); }
		
		protected void write(){
			if(chgColor())	super.write();
		}
	}
	
/*
	protected class CECAdapter implements iCECAdapter{
		protected elementBooster		myElement;
		protected String				myAttributeName;
		public CECAdapter(elementBooster element ,String name){
			myElement		= element;
			myAttributeName	= new String(name);
		}
		public void action(String text){ myElement.attribute(myAttributeName ,text); }
	}
*/	
}
