/*
 * Created on 2004/07/28
 *
 * To change the template for this generated file go to
 * Window>Preferences>Java>Code Generation>Code and Comments
 */
package jp.riken.brain.ni.samuraigraph.base;

import java.math.*;
import java.text.*;
import java.util.*;
import javax.swing.*;


/**
 * @author kuromaru
 *
 * To change the template for this generated type comment go to
 * Window>Preferences>Java>Code Generation>Code and Comments
 */
public class SGSpinner extends JSpinner
{


	/**
	 * 
	 */
	public static final String SPINNER_UNIT_CM = " cm";


	/**
	 * 
	 */
	public static final String SPINNER_UNIT_MM = " mm";


	/**
	 * 
	 */
	public static final String SPINNER_UNIT_PT = " pt";


	/**
	 * 
	 */
	public static final String SPINNER_UNIT_INCH = " inch";


	/**
	 * 
	 */
	public static final String SPINNER_UNIT_DEGREE = "°";



	/**
	 * 
	 */
	public static final int DEFAULT_DIGIT_FOR_ROUND_OUT = -3;


	/**
	 *
	 */
	private int mDigitForRoundOut = DEFAULT_DIGIT_FOR_ROUND_OUT;




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




	/**
	 * 
	 * @param model
	 * @param unit
	 * @param spinner
	 * @return
	 */
	public boolean initProperties(
		final SpinnerNumberModel model,
		final String unit )
	{

		// set the spinner model
		this.setModel(model);

		// set the suffix of unit
		if( unit!=null )
		{
			this.setSuffix( unit );
		}

		// set the property of the formatted-text-field
		JFormattedTextField ftf = this.getFormattedTextField();
		ftf.setFocusLostBehavior( JFormattedTextField.PERSIST );
		ftf.setHorizontalAlignment( JTextField.LEFT );

		//
		this.setMinimumFractionDigits(1);
		this.setMaximumFractionDigits(2);

		return true;
	}



	/**
	 * 
	 * @return
	 */
	public DecimalFormat getDecimalFormat()
	{
		final JSpinner.NumberEditor editor
			= (JSpinner.NumberEditor)this.getEditor();
		DecimalFormat df = editor.getFormat();
		return df;		
	}


	/**
	 * 
	 * @param newValue
	 */
	public boolean setMinimumFractionDigits( final int newValue )
	{
		this.getDecimalFormat().setMinimumFractionDigits(newValue);
		return true;
	}


	/**
	 * 
	 * @param newValue
	 */
	public boolean setMaximumFractionDigits( final int newValue )
	{
		this.getDecimalFormat().setMaximumFractionDigits(newValue);
		return true;
	}


	/**
	 * 
	 * @return
	 */
	public JFormattedTextField getFormattedTextField()
	{
		final JSpinner.DefaultEditor editor
			= (JSpinner.DefaultEditor)this.getEditor();
		final JFormattedTextField ftf = editor.getTextField();
		return ftf;
	}


	/**
	 * 
	 * @param spinner
	 * @param suffix
	 * @return
	 */
	public boolean setSuffix( final String suffix )
	{
		final JSpinner.NumberEditor editor
			= (JSpinner.NumberEditor)this.getEditor();
		final DecimalFormat df = editor.getFormat();
		df.setPositiveSuffix(suffix);
		return true;
	}


	/**
	 * 
	 * @return
	 */
	public String getSuffix()
	{
		final JSpinner.NumberEditor editor
			= (JSpinner.NumberEditor)this.getEditor();
		final DecimalFormat df = editor.getFormat();
		return df.getPositiveSuffix();
	}



	/**
	 * 
	 * @return
	 */
	public int getDigitForRoundingOut()
	{
		return this.mDigitForRoundOut;
	}



	/**
	 * 
	 * @param n
	 * @return
	 */
	public boolean setDigitForRoundingOut( final int n )
	{
		this.mDigitForRoundOut = n;
		return true;
	}



	/**
	 * 
	 * @return
	 */
	public boolean isInteger()
	{
		final DecimalFormat format = this.getDecimalFormat();
		final int min = format.getMinimumFractionDigits();
		final int max = format.getMaximumFractionDigits();
		if( min==0 && max==0 )
		{
			return true;
		}
		else
		{
			return false;
		}
	}



	/**
	 * 
	 * @param spinner
	 * @return
	 */
	public Number getNumber()
	{
		try
		{
			this.commitEdit2();
		}
		catch( Exception ex )
		{
//			ex.printStackTrace();
			return null;
		}
		Object obj = this.getValue();
		return (Number)obj;
	}



	/**
	 * 
	 * @param str
	 * @return
	 */
	public boolean setText( final String str )
	{
		this.getFormattedTextField().setText(str);
		return true;
	}



	/**
	 * 
	 */
	public void setValue( final Object value )
	{
		super.setValue(value);
		try
		{
			this.commitEdit2();
		}
		catch( ParseException ex )
		{
//			ex.printStackTrace();
			return;
		}
	}



	/**
     * Commits the value to the <code>SpinnerModel</code> after rounding up the value.
	 */
	public void commitEdit() throws ParseException
	{
		this.commit(MODE_ON_STEP);
		super.commitEdit();
	}



	/**
	 * 
	 * @return
	 */
	public void commitEdit2() throws ParseException
	{
		this.commit(MODE_DEFAULT);
		super.commitEdit();
	}


	private static final int MODE_DEFAULT = 0;
	private static final int MODE_ON_STEP = 1;



	/**
	 * 
	 * @return
	 */
	private boolean commit( final int mode ) throws ParseException
	{
		final JFormattedTextField ftf = this.getFormattedTextField();
		final String str = ftf.getText();
//System.out.println("TextFieldから取得した文字列 = "+str);

		// get value from the number model
		Object oldValue = this.getValue();
//System.out.println("NumberModelから取得した設定前の値 = "+oldValue);

		// parse the inputted string
		String strNew = this.parseString(str,mode);
//System.out.println("parse後の文字列："+strNew);
		if( strNew != null )
		{
			ftf.setText( strNew );
			try
			{
				ftf.commitEdit();
			}
			catch( ParseException ex )
			{
//				return false;
				throw ex;
			}
		}
		else
		{
			throw new ParseException("parseSting failed.",0);
		}

//System.out.println();

		return true;
	}



	/**
	 * 
	 * @param str
	 * @return
	 */
	private String parseString( final String str, final int mode )
	{
//System.out.println("<< parseString >>");
//System.out.println("@"+str+"@");
//System.out.println();

		if( str==null )
		{
			return null;
		}
		if( str.length()==0 )
		{
			return null;
		}

		final SpinnerNumberModel model = (SpinnerNumberModel)this.getModel();
		if( model==null )
		{
			return null;
		}

		final JFormattedTextField ftf = this.getFormattedTextField();
		final String unit = this.getUnit();
//System.out.println("unit="+unit);
		if( unit==null )
		{
			Number num = null;
			try
			{
				num = Float.valueOf( str );
			}
			catch( NumberFormatException ex )
			{
				return null;
			}

			if( this.isInteger() )
			{
				final float fValue = num.floatValue();
				int value = (int)Math.rint(fValue);
				final int min = ((Number)model.getMinimum()).intValue();
				final int max = ((Number)model.getMaximum()).intValue();
				if( value<min )
				{
					value = min;
				}
				if( value>max )
				{
					value = max;
				}
				String strNew = new Integer( value ).toString();
//System.out.println(min+"  "+max+"  "+strNew);
				return strNew;
			}

//System.out.println("***********  "+str+"  "+strValue);
			return num.toString();
		}


		String str_ = null;
		Number num = null;
		try
		{
			if( this.isInteger() )
			{
				num = Integer.valueOf( str );
			}
			else
			{
				num = Float.valueOf( str );
			}
			str_ = num.toString() + this.getSpinnerUnit(unit);
		}
		catch( NumberFormatException ex )
		{
			str_ = str;
		}

//System.out.println("str_="+str_);

		// in case of the unit of length
		String strValue = null;
		if( SGUtilityNumber.isLengthUnit( unit ) )
		{
			// convert the string to the number in the default unit
			strValue = SGUtilityNumber.convertString( str_, unit );
		}
		else
		{
			// remove the unit
			strValue = SGUtilityNumber.removeUnit( str_, unit );
		}
		if( strValue==null )
		{
			return null;
		}

//System.out.println("strValue="+strValue);

		// check the range
		if( this.isInteger() )
		{
			int value = Integer.valueOf( strValue ).intValue();
			final int min = ((Number)model.getMinimum()).intValue();
			final int max = ((Number)model.getMaximum()).intValue();
			if( value<min )
			{
				value = min;
			}
			if( value>max )
			{
				value = max;
			}
			String strNew = new Integer( value ).toString() + this.getSpinnerUnit(unit);
//System.out.println(min+"  "+max+"  "+strNew);
			return strNew;
		}


		// get the value
		float value = Float.valueOf(strValue).floatValue();
		final float min = ((Number)model.getMinimum()).floatValue();
		final float max = ((Number)model.getMaximum()).floatValue();
		if( value<min )
		{
			value = min;
		}
		if( value>max )
		{
			value = max;
		}


		// round up the number
		float valueNew = 0.0f;
		switch( mode )
		{
			case MODE_DEFAULT :
			{
				valueNew = (float)SGUtilityNumber.roundOffNumber(
					value, this.mDigitForRoundOut );
//System.out.println(value+"  "+this.mDigitForRoundOut+"   "+valueNew);
				break;
			}

			case MODE_ON_STEP :
			{
				valueNew = this.getValueOnStep(value);
				break;
			}
			
			default :
			{
				throw new IllegalArgumentException();
			}
		}

		String strNew = new Float( valueNew ).toString() + this.getSpinnerUnit(unit);

		return strNew;
	}


	/**
	 * 
	 * @return
	 */
	public String getSpinnerUnit( final String unit )
	{
		if( unit==null )
		{
			return null;
		}

		String sUnit = null;
		if( unit.equals( SGUtilityNumber.cm ) )
		{
			sUnit = SPINNER_UNIT_CM;
		}
		else if( unit.equals( SGUtilityNumber.mm ) )
		{
			sUnit = SPINNER_UNIT_MM;
		}
		else if( unit.equals( SGUtilityNumber.pt ) )
		{
			sUnit = SPINNER_UNIT_PT;
		}
		else if( unit.equals( SGUtilityNumber.inch ) )
		{
			sUnit = SPINNER_UNIT_INCH;
		}
		else if( unit.equals( SGUtilityNumber.degree ) )
		{
			sUnit = SPINNER_UNIT_DEGREE;
		}

		return sUnit;
	}


	/**
	 * 
	 * @return
	 */
	public String getUnit()
	{
		final JFormattedTextField ftf = this.getFormattedTextField();
		String suffix = this.getSuffix();
		String unit = null;
		if( suffix.equals( SPINNER_UNIT_CM ) )
		{
			unit = SGUtilityNumber.cm;
		}
		else if( suffix.equals( SPINNER_UNIT_MM ) )
		{
			unit = SGUtilityNumber.mm;
		}
		else if( suffix.equals( SPINNER_UNIT_INCH ) )
		{
			unit = SGUtilityNumber.inch;
		}
		else if( suffix.equals( SPINNER_UNIT_PT ) )
		{
			unit = SGUtilityNumber.pt;
		}
		else if( suffix.equals( SPINNER_UNIT_DEGREE ) )
		{
			unit = SGUtilityNumber.degree;
		}

		return unit;
	}



	/**
	 * 
	 * @param value
	 * @return
	 */
	private float getValueOnStep( final float value )
	{
		final SpinnerNumberModel model = (SpinnerNumberModel)this.getModel();
		if( model==null )
		{
			return value;
		}

		final float min = ((Number)model.getMinimum()).floatValue();
		final float max = ((Number)model.getMaximum()).floatValue();
		final float step = (model.getStepSize()).floatValue();
		if( step < Float.MIN_VALUE )
		{
			throw new Error();
		}

		final int n = (int)((value-min)/step);
		final float nValue = min + n*step;
		final float v = ( value < nValue + step/2.0f ? nValue : nValue + step );
		return v;
	}


}



