
package jp.riken.brain.ni.samuraigraph.base;

import java.awt.Color;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JComponent;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.border.MatteBorder;
import javax.swing.colorchooser.ColorSelectionModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.JTextComponent;


/**
 * The base class of the property dialogs.
 */
public abstract class SGPropertyDialog extends SGDialog
	implements ChangeListener, SGIDrawingElementConstants
{

	/**
	 *
	 */
//	protected JButton mOKButton = new JButton("OK");


	/**
	 *
	 */
//	protected JButton mCancelButton = new JButton("Cancel");


	/**
	 *
	 */
//	protected JButton mPreviewButton = new JButton("Preview");


	/**
	 * 
	 */
//	protected JPanel mButtonPanel = new JPanel();



	/**
	 * List of the property setting listeners.
	 */
	protected List mPropertyDialogObserverList = new ArrayList();



	/**
	 * 
	 */
	public SGPropertyDialog()
	{
		super();
	}


	/**
	 * 
	 */
	public SGPropertyDialog( Dialog owner )
	{
		super( owner );
	}


	/**
	 * 
	 */
	public SGPropertyDialog( Dialog owner, boolean modal )
	{
		super( owner, modal );
	}


	/**
	 * 
	 */
	public SGPropertyDialog( Dialog owner, String title )
	{
		super( owner, title );
	}


	/**
	 * 
	 */
	public SGPropertyDialog( Dialog owner, String title, boolean modal )
	{
		super( owner, title, modal );
	}


	/**
	 * 
	 */
	public SGPropertyDialog( Dialog owner, String title, boolean modal, GraphicsConfiguration gc )
	{
		super( owner, title, modal, gc );
	}



	/**
	 * 
	 */
	public SGPropertyDialog( Frame owner )
	{
		super( owner );
	}


	/**
	 * 
	 */
	public SGPropertyDialog( Frame owner, boolean modal )
	{
		super( owner, modal );
	}


	/**
	 * 
	 */
	public SGPropertyDialog( Frame owner, String title )
	{
		super( owner, title );
	}


	/**
	 * 
	 */
	public SGPropertyDialog( Frame owner, String title, boolean modal )
	{
		super( owner, title, modal );
	}


	/**
	 * 
	 */
	public SGPropertyDialog( Frame owner, String title, boolean modal, GraphicsConfiguration gc )
	{
		super( owner, title, modal, gc );
	}



	/**
	 * 
	 */
	private Icon mColorSelectionButtonEmphasisIcon = null;



	/**
	 * Initialization method to be called in the constructor of sub classes.
	 * This method must be called after the call of the constructor of the super class.
	 */
	protected boolean initialize()
	{

		// create a color dialog
		this.mColorDialog = new SGColorDialog(
			this, "Color Dialog", false );
		this.mColorDialog.pack();
		this.mColorDialog.setResizable(false);

		// set this as a ChangeListener of the color selection model
		JColorChooser cc = this.mColorDialog.getColorChooser();
		ColorSelectionModel cModel = cc.getSelectionModel();
		cModel.addChangeListener(this);
		
		this.mColorDialog.addWindowListener(new WindowAdapter(){
			public void windowClosing( WindowEvent e )
			{
				setColorButtonBorder( false );
			}
		} );

		// create background icon for the button
		this.mColorSelectionButtonEmphasisIcon
			= this.createIcon(SGIConstants.RESOURCES_DIRNAME+"Lines.gif");


		//
		// add this dialog as an actin event listener
		//

		// color selection buttons
		List cList = this.getColorSelectionButtonsList();
		for( int ii=0; ii<cList.size(); ii++ )
		{
			Object obj = cList.get(ii);
			if( obj instanceof SGColorSelectionButton )
			{
				JButton btn = (JButton)obj;
				btn.addActionListener( this );
			}
		}

		// text field components		
		List tList = this.getTextFieldComponentsList();
		for( int ii=0; ii<tList.size(); ii++ )
		{
			Object obj = tList.get(ii);
			if( obj instanceof JTextComponent )
			{
				JTextField com = (JTextField)obj;
				com.addActionListener(this);
			}
			
		}

		return true;

	}

	public void dispose()
	{
	    this.mColorDialog = null;
	    super.dispose();
	}

	/**
	 * name : name of resource file
	 */
	private ImageIcon createIcon( final String name )
	{
		if( name==null )
		{
			return null;
		}

		Class inClass = getClass();
		URL url = inClass.getResource(name);

//System.out.println("URL:"+url);

		ImageIcon icon;
		if( url!=null )
		{
			icon = new ImageIcon(url);
		}
		else
		{
			icon = new ImageIcon(name);
		}

//System.out.println("icon:"+icon);		
//System.out.println();

		return icon;
	}
	


	/**
	 * 
	 */
	protected String getString( final String key )
	{
		ResourceBundle bundle = ResourceBundle.getBundle("res",Locale.JAPAN);
//		ResourceBundle bundle = ResourceBundle.getBundle("res",Locale.ENGLISH);
		String val = bundle.getString(key);

		return val;
	}




	/**
	 * 
	 */
	protected SGColorDialog mColorDialog = null;


	/**
	 * 
	 */
	protected SGColorSelectionButton mFocusedColorSetButton = null;


	/**
	 * 
	 */
	public SGColorDialog getColorDialog()
	{
		return this.mColorDialog;
	}


	/**
	 * 
	 */
	public void stateChanged( final ChangeEvent e )
	{

		Object source = e.getSource();

		if( this.mFocusedColorSetButton!=null
			&& ( source instanceof ColorSelectionModel ) )
		{
			Color cl = this.mColorDialog.getColorChooser().getColor();
			this.mFocusedColorSetButton.setColor( cl );
			this.mColorDialog.getColorChooser().getSelectionModel().setSelectedColor( null );
		}

	}


	/**
	 * 
	 */
	public void setVisible( final boolean b )
	{
		super.setVisible(b);

		if( !b )
		{
			if( this.mColorDialog != null )
				this.mColorDialog.setVisible(false);
		}
	}


	/**
	 * Returns a list of buttons used for the color selection.
	 */
	public abstract List getColorSelectionButtonsList();


	/**
	 * Returns a list of text field components.
	 */
	public abstract List getTextFieldComponentsList();


	/**
	 * Returns a list of text fields to set number.
	 * @return
	 */
	public abstract List getNumberTextFieldList();


	/**
	 * Returns a list of spinners.
	 */
	public abstract List getSpinnerList();


	/**
	 * Returns a list of JFormattedTextField from spinners in the dialog.
	 */
	protected List getFormattedTextFieldsListFromSpinners()
	{
		final List list = new ArrayList();

		final List sList = this.getSpinnerList();
		for( int ii=0; ii<sList.size(); ii++ )
		{
			SGSpinner spinner = (SGSpinner)sList.get(ii);
			list.add( spinner.getFormattedTextField() );
		}

		return list;
	}



	/**
	 * 
	 */
	private boolean setColorButtonBorder( final JButton btn, final boolean flag )
	{
		List list = this.getColorSelectionButtonsList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			JComponent com = (JComponent)list.get(ii);
			if( com.equals(btn) && flag )
			{
				// draw emphasis border
				Border border = new MatteBorder(this.mColorSelectionButtonEmphasisIcon);
				btn.setBorder(border);
			}
			else
			{
				com.setBorder( new LineBorder( Color.BLACK, 1 ) );
			}
		}

		return true;
	}


	/**
	 * 
	 */
	public boolean setColorButtonBorder( final boolean flag )
	{
		if ( this.mColorDialog.isVisible() )
			this.setColorButtonBorder( this.mFocusedColorSetButton, flag );

		return true;
	}


	/**
	 * 
	 */
	public void actionPerformed( final ActionEvent e )
	{
		Object source = e.getSource();
		String command = e.getActionCommand();

//System.out.println(source);

		if( this.getColorSelectionButtonsList().contains(source) )
		{
			SGColorSelectionButton btn = (SGColorSelectionButton)source;

			// set current focused button
			this.mFocusedColorSetButton = btn;

			// show color dialog
			this.mColorDialog.setVisible(true);

			// draw focusing border
			this.setColorButtonBorder( true );
		}


		List comList = this.getTextFieldComponentsList();
		if( command.equals("OK") || comList.contains(source) )
		{
			this.onOK();
		}
		else if( command.equals("Cancel") )
		{
			this.onCanceled();
		}
		else if( command.equals("Preview") )
		{
			this.onPreviewed();
		}

	}




	/**
	 * 
	 */
	public static SpinnerNumberModel getLineWidthSpinnerNumberModel()
	{
		return new SpinnerNumberModel(
			LINE_WIDTH_MIN_VALUE,
			LINE_WIDTH_MIN_VALUE,
			LINE_WIDTH_MAX_VALUE,
			LINE_WIDTH_STEP_SIZE
		);
	}


	/**
	 * 
	 */
	public static SpinnerNumberModel getFontSizeSpinnerNumberModel()
	{
		return new SpinnerNumberModel(
			FONT_SIZE_MIN_VALUE,
			FONT_SIZE_MIN_VALUE,
			FONT_SIZE_MAX_VALUE,
			FONT_SIZE_STEP_VALUE
		);
	}



	/**
	 * 
	 * @param l
	 * @return
	 */
	public boolean addPropertyDialogObserver(
		final SGIPropertyDialogObserver l )
	{
		this.mPropertyDialogObserverList.add(l);
		return true;
	}


	/**
	 * 
	 * @param l
	 * @return
	 */
	public boolean removePropertyDialogObserver(
		final SGIPropertyDialogObserver l )
	{
		return this.mPropertyDialogObserverList.remove(l);
	}



	/**
	 * 
	 * @param l
	 * @return
	 */
	public boolean removeAllPropertyDialogObserver()
	{
		this.mPropertyDialogObserverList.clear();
		return true;
	}



	/**
	 * 
	 * @return
	 */
	private boolean onOK()
	{
		if( this.commit()==false )
		{
			return false;
		}

		this.setCloseOption( OK_OPTION );

		this.setVisible(false);
		this.clearAllSpinners();

		return true;
	}



	/**
	 * 
	 * @return
	 */
	private boolean onCanceled()
	{
		if( this.cancel() == false )
		{
			return false;
		}

		this.setCloseOption( CANCEL_OPTION );
		
		this.setVisible(false);
		this.clearAllSpinners();

		return true;
	}



	/**
	 * 
	 * @return
	 */
	private boolean onPreviewed()
	{
		return this.preview();
	}



	/**
	 * 
	 *
	 */
	protected void clearAllSpinners()
	{
		List list = this.getSpinnerList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGSpinner sp = (SGSpinner)list.get(ii);
			sp.clearTemporaryValues();
		}
	}
	
	
	/**
	 * 
	 * @return
	 */
	protected boolean commit()
	{
		if( !this.checkInputValues() )
		{
			return false;
		}

		List list = this.mPropertyDialogObserverList;
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGIPropertyDialogObserver l
				= (SGIPropertyDialogObserver)list.get(ii);
			if( this.setPropertiesToListeners(l) == false )
			{
				String msg;
				String err = this.getInputErrorMessage();
				if( err!=null )
				{
					msg = err;
				}
				else
				{
					msg = DEFAULT_INPUT_ERROR_MESSAGE;
				}
				SGUtility.showIllegalInputErrorMessageDialog( this, msg );
				return false;
			}
			if( l.commit() == false )
			{
				return false;
			}
		}

		this.setVisible(false);

		return true;
	}



	/**
	 * 
	 * @return
	 */
	protected boolean cancel()
	{
		List list = this.mPropertyDialogObserverList;
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGIPropertyDialogObserver l
				= (SGIPropertyDialogObserver)list.get(ii);
			if( l.cancel() == false )
			{
				return false;
			}
		}

		return true;
	}


	/**
	 * 
	 * @return
	 */
	protected boolean preview()
	{
		if( !this.checkInputValues() )
		{
			return false;
		}

		List list = this.mPropertyDialogObserverList;
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGIPropertyDialogObserver l
				= (SGIPropertyDialogObserver)list.get(ii);
			if( this.setPropertiesToListeners(l) == false )
			{
				String msg;
				String err = this.getInputErrorMessage();
				if( err!=null )
				{
					msg = err;
				}
				else
				{
					msg = DEFAULT_INPUT_ERROR_MESSAGE;
				}
				SGUtility.showIllegalInputErrorMessageDialog( this, msg );
				return false;
			}
			if( l.preview() == false )
			{
				return false;
			}
		}
		return true;
	}



	/**
	 * Check input values.
	 * @return
	 */
	private boolean checkInputValues()
	{
		// values in dialog
		if( !this.hasValidInputValues() )
		{
			// show a message dialog
			String msg = this.getInputErrorMessage();
			SGUtility.showIllegalInputErrorMessageDialog( this, msg );

			return false;
		}

		return true;
	}


	/**
	 * Override this method to check the input values.
	 * @return
	 */
	protected boolean hasValidInputValues()
	{
		boolean ret = true;
		
		this.mInputErrorComponentNameList.clear();
		
		// spinners
		List sList = this.getSpinnerList();
		for( int ii=0; ii<sList.size(); ii++ )
		{
			SGSpinner s = (SGSpinner)sList.get(ii);
			if( s.hasValidValue() == false )
			{
				this.addInputErrorDescription( s.getDescription() );
				ret = false;
			}
		}
		
		// text fields to set number
		List tList = this.getNumberTextFieldList();
		for( int ii=0; ii<tList.size(); ii++ )
		{
			SGTextField tf = (SGTextField)tList.get(ii);
			if( this.getNumber(tf) == null )
			{
				if( tf.isIndeterminate() == false )
				{
					this.addInputErrorDescription( tf.getDescription() );
					ret = false;
				}
			}
		}

		return ret;
	}



	private String mInputErrorMessage = "";


	// The list of name of components set erroneous input value.
	private final ArrayList mInputErrorComponentNameList = new ArrayList();


	/**
	 * Returns a string used as an error message on input.
	 * @return
	 */
	protected String getInputErrorMessage()
	{
		if( this.mInputErrorComponentNameList.size()!=0 )
		{
			String str = "The input values are illegal in the following components:\n";
			ArrayList list = this.mInputErrorComponentNameList;
			for( int ii=0; ii<list.size(); ii++ )
			{
				String name = (String)list.get(ii);
				str += " - " + name + "\n";
			}

			return str;
		}
		return this.mInputErrorMessage;
	}


	/**
	 * Set the error message used as an error message on input.
	 * @param str
	 * @return
	 */
	protected boolean addInputErrorDescription( final String name )
	{
		this.mInputErrorComponentNameList.add( name );
		return true;
	}


	/**
	 * 
	 * @param msg
	 * @return
	 */
	protected boolean setInputErrorMessage( final String msg )
	{
		this.mInputErrorMessage = msg;
		return true;
	}


	/**
	 * 
	 */
	protected static final String DEFAULT_INPUT_ERROR_MESSAGE
		= "The input value is illegal.";


	public static final String ERRMSG_AXIS_VALUE_INVALID
		= "Axis value is invalid.";


	/**
	 * Set properties to all observers.
	 * @param obs
	 * @return
	 */
	public abstract boolean setPropertiesToListeners( SGIPropertyDialogObserver obs );


	/**
	 * Set parameters to the components in the dialog.
	 * @return
	 */
	public abstract boolean setDialogProperty();


	/**
	 * 
	 */
	public void windowClosing(final WindowEvent e)
	{
		super.windowClosing(e);
		this.onCanceled();
	}



	/**
	 * 
	 * @param obj
	 * @return
	 */
	protected boolean setValue(
		final SGTextField tf, final Object obj )
	{
		return SGUtility.setValue(tf,obj);
	}


	/**
	 * 
	 * @param obj
	 * @return
	 */
	protected boolean setValue(
		final SGSpinner sp, final Object obj )
	{
		return SGUtility.setValue(sp,obj);
	}


}

