
package jp.riken.brain.ni.samuraigraph.figure.java2d;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dialog;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.swing.JOptionPane;

import jp.riken.brain.ni.samuraigraph.base.SGAxis;
import jp.riken.brain.ni.samuraigraph.base.SGData;
import jp.riken.brain.ni.samuraigraph.base.SGDefaultValues;
import jp.riken.brain.ni.samuraigraph.base.SGIAxisBreakElement;
import jp.riken.brain.ni.samuraigraph.base.SGIAxisElement;
import jp.riken.brain.ni.samuraigraph.base.SGIConstants;
import jp.riken.brain.ni.samuraigraph.base.SGIFigureElement;
import jp.riken.brain.ni.samuraigraph.base.SGIGraphElement;
import jp.riken.brain.ni.samuraigraph.base.SGIGridElement;
import jp.riken.brain.ni.samuraigraph.base.SGILegendElement;
import jp.riken.brain.ni.samuraigraph.base.SGIPropertySettingListener;
import jp.riken.brain.ni.samuraigraph.base.SGISelectable;
import jp.riken.brain.ni.samuraigraph.base.SGISignificantDifferenceElement;
import jp.riken.brain.ni.samuraigraph.base.SGIStringElement;
import jp.riken.brain.ni.samuraigraph.base.SGITimingLineElement;
import jp.riken.brain.ni.samuraigraph.base.SGProperties;
import jp.riken.brain.ni.samuraigraph.base.SGPropertyDialog;
import jp.riken.brain.ni.samuraigraph.base.SGTuple2d;
import jp.riken.brain.ni.samuraigraph.base.SGUtility;
import jp.riken.brain.ni.samuraigraph.base.SGUtilityNumber;
import jp.riken.brain.ni.samuraigraph.base.SGUtilityText;
import jp.riken.brain.ni.samuraigraph.figure.SGDrawingElementLine;
import jp.riken.brain.ni.samuraigraph.figure.SGFigureElement;
import jp.riken.brain.ni.samuraigraph.figure.SGILineConstants;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * A panel class to draw grid in figures.
 */
public class SGGridElement extends SGFigureElement
	implements SGIGridElement, SGILineConstants, SGIPropertySettingListener
{

	/**
	 * An SGIAxisElement object.
	 */	
	private SGIAxisElement mAxisElement;


	/**
	 * X-axis.
	 */
	private SGAxis mXAxis;


	/**
	 * Y-axis.
	 */
	private SGAxis mYAxis;


	/**
	 * The stepping value for X-axis.
	 */
	private double mStepValueX = 1.0;


	/**
	 * The baseline value for X-axis.
	 */	
	private double mBaselineValueX = 0.0;


	/**
	 * The stepping value for Y-axis.
	 */
	private double mStepValueY = 1.0;


	/**
	 * The baseline value for Y-axis.
	 */	
	private double mBaselineValueY = 0.0;


	/**
	 * Visible flag.
	 */
	private boolean mVisibleFlag;


	/**
	 * A flag whether to set location of grid lines automatically.
	 */
	private boolean mAutoRangeFlag = true;


	/**
	 * Line width.
	 */
	private float mLineWidth;


	/**
	 * Line type.
	 */
	private int mLineType;


	/**
	 * Line color.
	 */
	private Color mColor;


	/**
	 * 
	 */
	private int mCap = BasicStroke.CAP_BUTT;


	/**
	 * 
	 */
	private int mJoin = BasicStroke.JOIN_ROUND;


	/**
	 * 
	 */
	private float mMiterLimit = 1.0f;


	/**
	 * 
	 */
	private float mDashPhase = 0.0f;


	/**
	 * Set of grid lines.
	 */
	private HashSet mLineSet;


	/**
	 * 
	 */
	private SGGridDialog mDialog = null;


	/**
	 * 
	 */
	protected SGProperties mTemporaryProperties = null;


	
	/**
	 * Default constructor.
	 */
	public SGGridElement()
	{
		super();
		this.init();
	}


	private void init()
	{
		this.mVisibleFlag = SGDefaultValues.FIGURE_GRID_VISIBLE;
		this.mLineWidth = SGDefaultValues.FIGURE_GRID_LINE_WIDTH;
		Integer type = SGDrawingElementLine.getLineTypeFromName(
			SGDefaultValues.FIGURE_GRID_LINE_TYPE);
		if( type==null )
		{
			throw new Error();
		}
		this.mLineType = type.intValue();
		this.mColor = SGDefaultValues.FIGURE_GRID_COLOR;
	}


	/**
	 * Set the SGIAxisElement object.
	 * @param el - an SGIAxisElement object
	 */
	public void setAxisElement( SGIAxisElement el )
	{
		this.mAxisElement = el;
	}


	/**
	 * 
	 */
	public boolean addData( SGData data, String name )
	{
		if( super.addData( data, name ) == false )
		{
			return false;
		}

		SGIAxisElement el = this.mAxisElement;
		if( this.mXAxis==null )
		{
			this.mXAxis = el.getAxis( SGDefaultValues.FIGURE_GRID_HORIZONTAL_AXIS );
		}
		if( this.mYAxis==null )
		{
			this.mYAxis = el.getAxis( SGDefaultValues.FIGURE_GRID_PERPENDICULAR_AXIS );
		}
		this.createAll();

		return true;
	}


	/**
	 * 
	 */
	public boolean setGraphRect(
		final float x, final float y, final float width, final float height )
	{
		super.setGraphRect( x, y, width, height );
		this.createAll();
		return true;
	}


	/**
	 * 
	 */
	public void showPropertyDialog( Dialog owner )
	{
		SGGridDialog dg = new SGGridDialog( owner, false );
		this.mDialog = dg;
		dg.addPropertySettingListener( this );
		this.createTemporaryPropertyObject();
		if( this.setDialogProperties() == false )
		{
			return;
		}
		dg.setLocation( this.mDialogOwner.getLocation() );
		dg.setVisible(true);
		this.notifyToRoot();
	}


	/**
	 * 
	 */
	public void paintGraphics( Graphics g, boolean clip )
	{
		if( !this.isGridVisible() )
		{
			return;
		}

		final Graphics2D g2d = (Graphics2D)g;

		// set properties
		final float width = this.mMagnification*this.mLineWidth;
		g2d.setPaint( this.getColor() );

		// set stroke
		Stroke stroke = SGUtilityJava2D.getBasicStroke(
			this.mLineType, width, this.mCap,
			this.mJoin, this.mMiterLimit, this.mDashPhase );
		g2d.setStroke( stroke );


		// clip the rectangle
		if( clip )
		{
			SGUtilityForFigureElement.clipGraphRect(this,g2d);
		}


		// draw lines
		HashSet set = this.mLineSet;
		if( set!=null )
		{
			Iterator itr = set.iterator();
			if( clip )
			{
				while( itr.hasNext() )
				{
					Shape sh = (Shape)itr.next();
					g2d.draw(sh);
				}
			}
			else
			{
				Area clipArea = new Area( this.getGraphRect() );
				while( itr.hasNext() )
				{
					Shape sh = (Shape)itr.next();
					Area shArea = new Area( stroke.createStrokedShape(sh) );
					shArea.intersect( clipArea );
					g2d.fill(shArea);
				}
			}
		}

		if( clip )
		{
			g2d.setClip( this.getGraphRect() );
		}

	}



	/**
	 * 
	 */
	public boolean synchronize(SGIFigureElement element)
	{

		boolean flag = true;
		if( element instanceof SGIGraphElement )
		{
			
		}
		else if( element instanceof SGIStringElement )
		{
			
		}
		else if( element instanceof SGILegendElement )
		{
			
		}
		else if( element instanceof SGIAxisElement )
		{
			this.createAll();
		}
		else if( element instanceof SGIAxisBreakElement )
		{
			
		}
		else if( element instanceof SGISignificantDifferenceElement )
		{
			
		}
		else if( element instanceof SGITimingLineElement )
		{
			
		}
		else if( element instanceof SGIGridElement )
		{

		}
		else
		{
			flag = element.synchronizeArgument( this );
		}

		return true;
	}


	/**
	 * Synchronize the element given by the argument.
	 * @param element An object to be synchronized.
	 */
	public boolean synchronizeArgument(SGIFigureElement element)
	{
		// this shouldn't happen
		throw new Error();
	}


	/**
	 * Create all drawing elements.
	 *
	 */
	private void createAll()
	{
		HashSet set = new HashSet();
		if( this.isGridVisible() )
		{
			// create line objects for X and Y axes
			if( this.mXAxis!=null )
			{
				Set s = this.create( this.mXAxis, this.mBaselineValueX, true );
				set.addAll(s);
			}
			if( this.mYAxis!=null )
			{
				Set s = this.create( this.mYAxis, this.mBaselineValueY, false );
				set.addAll(s);
			}
		}
		this.mLineSet = set;
		this.repaint();
	}


	/**
	 * 
	 * @param axis
	 * @param baseline
	 * @param horizontal
	 * @return
	 */
	private Set create(
		SGAxis axis,
		final double baseline,
		final boolean horizontal )
	{
		final int type = axis.getScaleType();
		float[] array = null;
		if( type == SGAxis.LINEAR_TYPE )
		{
			array = this.calcLocationLinear( axis, baseline, horizontal );
		}
		else if( type == SGAxis.LOG_TYPE )
		{
			array = this.calcLocationLog( axis, baseline, horizontal );
		}
		else
		{
			throw new Error();
		}

		final int size = array.length;


		// create drawing elements
		final float gx = this.getGraphRectX();
		final float gy = this.getGraphRectY();
		final float gw = this.getGraphRectWidth();
		final float gh = this.getGraphRectHeight();
		HashSet lineSet = new HashSet();
		if( horizontal )
		{
			for( int ii=0; ii<size; ii++ )
			{
				final float x = array[ii];
				Line2D line = new Line2D.Float( x, gy, x, gy+gh );
				lineSet.add( line );
			}
		}
		else
		{
			for( int ii=0; ii<size; ii++ )
			{
				final float y = array[ii];
				Line2D line = new Line2D.Float( gx, y, gx+gw, y );
				lineSet.add( line );
			}
		}
		
		return lineSet;
	}



	/**
	 * 
	 * @param axis
	 * @param baseline
	 * @param horizontal
	 * @return
	 */
	private float[] calcLocationLinear(
		SGAxis axis,
		final double baseline,
		final boolean horizontal )
	{
		double step;
		if( this.mAutoRangeFlag )
		{
			step = this.calcStepValue( axis );
		}
		else
		{
			if( horizontal )
			{
				step = this.mStepValueX;
			}
			else
			{
				step = this.mStepValueY;
			}
		}


		final double min = axis.getMinValue();
		final double max = axis.getMaxValue();

		HashSet set = new HashSet();
		set.add( new Double(baseline) );

		double value = baseline;
		while( true )
		{
			value += step;
			set.add( new Double(value) );
			if( value>max )
			{
				break;
			}
		}

		value = baseline;
		while( true )
		{
			value -= step;
			set.add( new Double(value) );
			if( value<min )
			{
				break;
			}
		}

		final int size = set.size();
		float[] array = new float[size];
		Iterator itr = set.iterator();
		int cnt=0;
		while( itr.hasNext() )
		{
			final Double d = (Double)itr.next();
			array[cnt] = this.calcLocation( d.doubleValue(), axis, horizontal );
			cnt++;
		}

		return array;
	}



	/**
	 * 
	 * @param axis
	 * @param baseline
	 * @param horizontal
	 * @return
	 */
	private float[] calcLocationLog(
		SGAxis axis,
		final double baseline,
		final boolean horizontal )
	{
		// minimum and maximum values of axis range
		final double min = axis.getRange().x;
		final double max = axis.getRange().y;

		final int minOrder = SGUtilityNumber.getOrder( (1.0-1.0E-14)*min );
		final int maxOrder = SGUtilityNumber.getOrder(max);

		ArrayList list = new ArrayList();
		for( int ii=minOrder+1; ii<=maxOrder; ii++ )
		{
			list.add( new Integer(ii) );
		}
		final int len = list.size();

		// array of values for scale
		double[] axisValueArray = new double[len];
		for( int ii=0; ii<len; ii++ )
		{
			final int num = ((Integer)list.get(ii)).intValue();
			axisValueArray[ii] = SGUtilityNumber.getPowersOfTen( num );
		}

		final float[] locationArray = new float[len];
		for( int ii=0; ii<len; ii++ )
		{
			locationArray[ii] = this.calcLocation( axisValueArray[ii], axis, horizontal );
		}

		return locationArray;
	}



	/**
	 * Only returns true.
	 * @param list - a list to be added the focused objects
	 */
	public boolean getFocusedObjectsList( ArrayList list )
	{
		return true;
	}


	/**
	 * 
	 */
	public SGProperties getProperties()
	{
		GridProperties p = new GridProperties();
		if( this.getProperties(p) == false )
		{
			return null;
		}
		return p;
	}


	/**
	 * 
	 */
	public boolean getProperties( SGProperties p )
	{
		if( ( p instanceof GridProperties ) == false ) return false;

		GridProperties gp = (GridProperties)p;
		gp.mXAxis = this.mXAxis;
		gp.mYAxis = this.mYAxis;
		gp.mVisibleFlag = this.mVisibleFlag;
		gp.mBaselineValueX = this.mBaselineValueX;
		gp.mBaselineValueY = this.mBaselineValueY;
		gp.mStepValueX = this.mStepValueX;
		gp.mStepValueY = this.mStepValueY;
		gp.mAutoRangeFlag = this.mAutoRangeFlag;
		gp.mLineWidth = this.mLineWidth;
		gp.mLineType = this.mLineType;
		gp.mColor = this.mColor;
			
		return true;
	}


	/**
	 * 
	 */
	public boolean setProperties(SGProperties p)
	{
		if( ( p instanceof GridProperties ) == false ) return false;

		GridProperties gp = (GridProperties)p;
		this.mXAxis = gp.mXAxis;
		this.mYAxis = gp.mYAxis;
		this.mVisibleFlag = gp.mVisibleFlag;
		this.mBaselineValueX = gp.mBaselineValueX;
		this.mBaselineValueY = gp.mBaselineValueY;
		this.mStepValueX = gp.mStepValueX;
		this.mStepValueY= gp.mStepValueY;
		this.mAutoRangeFlag = gp.mAutoRangeFlag;
		this.mLineWidth = gp.mLineWidth;
		this.mLineType = gp.mLineType;
		this.mColor = gp.mColor;
		return true;
	}


	/**
	 * Only returns true.
	 * @param s - a selectable object to be hidden
	 */
	public boolean hideSelectedObject( SGISelectable s )
	{
		return true;
	}


	/**
	 * 
	 */
	public String getTagName()
	{
		return TAG_NAME_GRID_ELEMENT;
	}


	/**
	 * 
	 */
	public boolean writeProperty(Element el)
	{
		final String cm = SGUtilityNumber.cm;
		final String pt = SGUtilityNumber.pt;

		el.setAttribute( KEY_GRID_VISIBLE, Boolean.toString( this.mVisibleFlag ) );
		el.setAttribute( KEY_X_AXIS_POSITION, mAxisElement.getAxisLocation( this.mXAxis ) );
		el.setAttribute( KEY_Y_AXIS_POSITION, mAxisElement.getAxisLocation( this.mYAxis ) );
		el.setAttribute( KEY_AUTO_CALC, Boolean.toString( this.mAutoRangeFlag ) );
		el.setAttribute( KEY_STEP_VALUE_X, Double.toString( this.mStepValueX ) );
		el.setAttribute( KEY_BASELINE_VALUE_X, Double.toString( this.mBaselineValueX ) );
		el.setAttribute( KEY_STEP_VALUE_Y, Double.toString( this.mStepValueY ) );
		el.setAttribute( KEY_BASELINE_VALUE_Y, Double.toString( this.mBaselineValueY ) );
		el.setAttribute( KEY_LINE_WIDTH, Float.toString( this.mLineWidth ) + pt );
		el.setAttribute( KEY_LINE_TYPE, SGDrawingElementLine.getLineTypeName( this.mLineType ) );
		el.setAttribute( KEY_GRID_COLOR, SGUtilityText.getColorString( this.mColor ) );

		return true;
	}


	/**
	 * 
	 * @param e
	 */
	public boolean onMouseClicked( MouseEvent e )
	{
		return false;
	}


	/**
	 * 
	 * @param e
	 */
	public boolean onMousePressed( MouseEvent e )
	{
		return false;
	}


	/**
	 * 
	 * @param e
	 */
	public boolean onMouseDragged( MouseEvent e )
	{
		return true;
	}



	/**
	 * 
	 * @param e
	 */
	public boolean onMouseReleased( MouseEvent e )
	{
		return true;
	}



	/**
	 * 
	 */
	public boolean onDrawingElement(int x, int y)
	{
		return false;
	}


	/**
	 * 
	 */
	public Element createElement(Document document)
	{
		Element el = this.createThisElement( document );
		if( el==null )
		{
			return null;
		}

		return el;
	}



	/**
	 * 
	 */
	public boolean readProperty( Element element )
	{
		GridProperties p = this.readProperty_( element );
		if( p==null ) return false;
		if( this.setProperties(p) == false ) return false;
		return true;
	}


	/**
	 * 
	 * @param el
	 * @return
	 */
	private GridProperties readProperty_( final Element el )
	{
		final String cm = SGUtilityNumber.cm;
		final String pt = SGUtilityNumber.pt;
		final float ratio = SGIConstants.CM_POINT_RATIO;

		String str = null;
		Number num = null;
		Color cl = null;
		Boolean b = null;

		
		// grid visible
		str = el.getAttribute( SGIGridElement.KEY_GRID_VISIBLE );
		if( str.length()==0 )
		{
			return null;
		}
		b = SGUtilityText.getBoolean(str);
		if( b==null )
		{
			return null;
		}
		final boolean gridVisible = b.booleanValue();

		
		// set axes
		str = el.getAttribute( KEY_X_AXIS_POSITION );
		if( str.length()==0 )
		{
			return null;
		}
		SGAxis xAxis = mAxisElement.getAxis(str);
		
		str = el.getAttribute( KEY_Y_AXIS_POSITION );
		if( str.length()==0 )
		{
			return null;
		}
		SGAxis yAxis = mAxisElement.getAxis(str);


		// auto calc
		str = el.getAttribute( SGIGridElement.KEY_AUTO_CALC );
		if( str.length()==0 )
		{
			return null;
		}
		b = SGUtilityText.getBoolean(str);
		if( b==null )
		{
			return null;
		}
		final boolean autoCalc = b.booleanValue();


		// for x-axis
		str = el.getAttribute( KEY_BASELINE_VALUE_X );
		if( str.length()==0 )
		{
			return null;
		}
		num = SGUtilityText.getDouble(str);
		if( num==null )
		{
			return null;
		}
		final double baselineValueX = num.doubleValue();

		str = el.getAttribute( KEY_STEP_VALUE_X );
		if( str.length()==0 )
		{
			return null;
		}
		num = SGUtilityText.getDouble(str);
		if( num==null )
		{
			return null;
		}
		final double stepValueX = num.doubleValue();


		// for y-axis
		str = el.getAttribute( KEY_BASELINE_VALUE_Y );
		if( str.length()==0 )
		{
			return null;
		}
		num = SGUtilityText.getDouble(str);
		if( num==null )
		{
			return null;
		}
		final double baselineValueY = num.doubleValue();

		str = el.getAttribute( KEY_STEP_VALUE_Y );
		if( str.length()==0 )
		{
			return null;
		}
		num = SGUtilityText.getDouble(str);
		if( num==null )
		{
			return null;
		}
		final double stepValueY = num.doubleValue();


		// line width
		str = el.getAttribute( KEY_LINE_WIDTH );
		if( str==null )
		{
			return null;
		}
		num = SGUtilityText.getFloat( str, pt );
		if( num==null )
		{
			return null;
		}
		final float lineWidth = num.floatValue();


		// line type
		str = el.getAttribute( KEY_LINE_TYPE );
		if( str==null )
		{
			return null;
		}
		num = SGDrawingElementLine.getLineTypeFromName(str);
		if( num==null )
		{
			return null;
		}
		final int lineType = num.intValue();


		// grid color
		str = el.getAttribute( KEY_GRID_COLOR );
		if( str.length()==0 )
		{
			return null;
		}
		cl = SGUtilityText.getColorFromString(str);
		if( cl==null )
		{
			return null;
		}
		final Color gridColor = cl;


		// create a property object
		GridProperties p = new GridProperties();
		p.mVisibleFlag = gridVisible;
		p.mXAxis = xAxis;
		p.mYAxis = yAxis;
		p.mAutoRangeFlag = autoCalc;
		p.mBaselineValueX = baselineValueX;
		p.mStepValueX = stepValueX;
		p.mBaselineValueY = baselineValueY;
		p.mStepValueY = stepValueY;
		p.mLineWidth = lineWidth;
		p.mLineType = lineType;
		p.mColor = gridColor;

		return p;
	}



	/**
	 * 
	 */
	public boolean setTemporaryPropertiesOfFocusedObjects()
	{
		return true;
	}


	/**
	 * 
	 */
	public boolean setChangedFocusedObjects()
	{
		return true;
	}


	/**
	 * 
	 */
	public boolean createTemporaryPropertyObject()
	{
		this.mTemporaryProperties = this.getProperties();
		return true;
	}



	/**
	 * 
	 */
	public boolean commit()
	{
		return this.commit( mDialog );
	}



	/**
	 * 
	 */
	public boolean commit( SGPropertyDialog dg )
	{
		final boolean flag = this.setPropertiesFromDialog( (SGGridDialog)dg );
		if( !flag )
		{
			return false;
		}

		SGProperties pTemp = this.mTemporaryProperties;
		SGProperties pPresent = this.getProperties();
		if( pTemp.equals(pPresent) == false )
		{
			this.mChangedFlag = true;
		}
		this.mTemporaryProperties = null;
		this.createAll();
		this.mDialog.removeAllPropertySettingListeners();
		notifyChange();
		this.notifyToRoot();

		return true;
	}


	/**
	 * 
	 */
	public boolean cancel()
	{
		if( this.setProperties( this.mTemporaryProperties ) == false )
		{
			return false;
		}
		this.mTemporaryProperties = null;
		this.createAll();
		this.mDialog.removeAllPropertySettingListeners();
		notifyChange();

		return true;
	}



	/**
	 * 
	 */
	public boolean preview()
	{
		return this.preview( mDialog );
	}


	/**
	 * 
	 */
	public boolean preview( SGPropertyDialog dg )
	{
		if( this.setPropertiesFromDialog( (SGGridDialog)dg ) == false )
		{
			return false;
		}
		this.createAll();
		notifyChange();

		return true;
	}



	/**
	 * 
	 */
	public boolean undo()
	{
		if( !super.undo() )
		{
			return false;
		}
		this.createAll();
		this.notifyChange();
		return true;
	}




	/**
	 * 
	 */
	public boolean redo()
	{
		if( !super.redo() )
		{
			return false;
		}
		this.createAll();
		this.notifyChange();
		return true;
	}



	/**
	 * Returns the baseline value for x-axis.
	 * @return baseline value
	 */
	public double getBaselineValueX()
	{
		return mBaselineValueX;
	}


	/**
	 * Returns the baseline value for y-axis.
	 * @return baseline value
	 */
	public double getBaselineValueY()
	{
		return mBaselineValueY;
	}


	/**
	 * Returns the color of grid.
	 * @return color of grid
	 */
	public Color getColor()
	{
		return mColor;
	}


	/**
	 * Returns the line type.
	 * @return line type
	 */
	public int getLineType()
	{
		return mLineType;
	}


	/**
	 * Returns the line width.
	 * @return line width
	 */
	public float getLineWidth()
	{
		return mLineWidth;
	}


	/**
	 * Returns the step value for x-axis.
	 * @return step value
	 */
	public double getStepValueX()
	{
		return mStepValueX;
	}


	/**
	 * Returns the step value for y-axis.
	 * @return step value
	 */
	public double getStepValueY()
	{
		return mStepValueY;
	}


	/**
	 * Set axes.
	 * @param xAxis - x axis
	 * @param yAxis - y axis
	 */
	public void setAxes( final SGAxis xAxis, final SGAxis yAxis )
	{
		this.mXAxis = xAxis;
		this.mYAxis = yAxis;
	}


	/**
	 * Returns the x-axis.
	 * @return x-axis
	 */
	public SGAxis getXAxis()
	{
		return mXAxis;
	}


	/**
	 * Returns the y-axis.
	 * @return y-axis
	 */
	public SGAxis getYAxis()
	{
		return mYAxis;
	}


	/**
	 * Set the baseline value for x-axis.
	 * @param d - baseline value
	 */
	public void setBaselineValueX( final double d )
	{
		mBaselineValueX = d;
	}


	/**
	 * Set the baseline value for y-axis.
	 * @param d - baseline value
	 */
	public void setBaselineValueY( final double d )
	{
		mBaselineValueY = d;
	}


	/**
	 * @param color
	 */
	public void setColor(Color color)
	{
		mColor = color;
	}


	/**
	 * Set the line type
	 * @param i - line type
	 */
	public void setLineType( final int i )
	{
		mLineType = i;
	}


	/**
	 * Set the line width.
	 * @param f - line width
	 */
	public void setLineWidth( final float f )
	{
		mLineWidth = f;
	}


	/**
	 * Set the step value of x-axis.
	 * @param d - value
	 */
	public void setStepValueX( final double d )
	{
		if( d<=0.0 )
		{
			throw new IllegalArgumentException("d<=0.0");
		}
		mStepValueX = d;
	}


	/**
	 * Set the step value of y-axis.
	 * @param d - value
	 */
	public void setStepValueY( final double d )
	{
		if( d<=0.0 )
		{
			throw new IllegalArgumentException("d<=0.0");
		}
		mStepValueY = d;
	}


	/**
	 * Returns whether the grid is visible.
	 * @return visible flag
	 */
	public boolean isGridVisible()
	{
		return mVisibleFlag;
	}


	/**
	 * Set visible the grid.
	 * @param b visible flag
	 */
	public void setGridVisible( final boolean b )
	{
		this.mVisibleFlag = b;
		this.createAll();
	}


	/**
	 * Returns the flag whether to set range of lines automatically.
	 * @return auto-range flag
	 */
	public boolean isAutoRange()
	{
		return mAutoRangeFlag;
	}


	/**
	 * Set whether to set range of lines automatically.
	 * @param b auto-range flag
	 */
	public void setAutoRangeFlag( final boolean b )
	{
		mAutoRangeFlag = b;
	}



	/**
	 * 
	 * @return
	 */
	private boolean setDialogProperties()
	{
		SGGridDialog dg = this.mDialog;

		// visible
		dg.setGridVisible( this.mVisibleFlag );

		// axes
		final int xAxisLocation = this.mAxisElement.getConfigurationInPlane( this.mXAxis );
		if( xAxisLocation==-1 )
		{
			throw new Error();
		}
		final int yAxisLocation = this.mAxisElement.getConfigurationInPlane( this.mYAxis );
		if( yAxisLocation==-1 )
		{
			throw new Error();
		}
		dg.setAxisRadioButton( xAxisLocation, yAxisLocation );

		// auto
		dg.setAutoFlag( this.isAutoRange() );

		// for the x-axis
		dg.setBaseXValue( this.mBaselineValueX );
		dg.setStepXValue( this.mStepValueX );
		
		// for the y-axis
		dg.setBaseYValue( this.mBaselineValueY );
		dg.setStepYValue( this.mStepValueY );

		// line width
		dg.setLineWidth( this.mLineWidth );
		dg.setLineType( this.mLineType );
		dg.setLineColor( this.mColor );

		return true;
	}


	/**
	 * Set properties from the property dialog.
	 * @return
	 */
	private boolean setPropertiesFromDialog( SGGridDialog dg )
	{

		// visible
		final boolean visible = dg.getGridVisible();
		this.mVisibleFlag = visible;

		// axes
		String xAxisLocation = dg.getXAxisLocation();
		if( xAxisLocation==null )
		{
			return false;
		}
		String yAxisLocation = dg.getYAxisLocation();
		if( yAxisLocation==null )
		{
			return false;
		}
		SGAxis xAxis = this.mAxisElement.getAxis( xAxisLocation );
		SGAxis yAxis = this.mAxisElement.getAxis( yAxisLocation );
		this.setAxes( xAxis, yAxis );


		// auto
		this.setAutoRangeFlag( dg.getAutoFlag() );


		// for the x-axis
		Number baseX = dg.getBaseXValue();
		if( baseX==null )
		{
			return false;
		}
		Number stepX = dg.getStepXValue();
		if( stepX==null )
		{
			return false;
		}
		if( this.checkStepValue( stepX.doubleValue(), xAxis ) == false )
		{
			return false;
		}

		// for the y-axis
		Number baseY = dg.getBaseYValue();
		if( baseY==null )
		{
			return false;
		}
		Number stepY = dg.getStepYValue();
		if( stepY==null )
		{
			return false;
		}
		if( this.checkStepValue( stepY.doubleValue(), yAxis ) == false )
		{
			return false;
		}

		this.setBaselineValueX( baseX.doubleValue() );
		this.setStepValueX( stepX.doubleValue() );
		this.setBaselineValueY( baseY.doubleValue() );
		this.setStepValueY( stepY.doubleValue() );


		// line width
		final Number lineWidth = dg.getLineWidth();
		if( lineWidth==null )
		{
			return false;
		}
		this.setLineWidth( lineWidth.floatValue() );

		// line type
		final int type = dg.getLineType();
		if( type==-1 )
		{
			return false;
		}
		this.setLineType( type );

		// line color
		Color cl = dg.getLineColor();
		this.setColor(cl);
		
		return true;
	}



	private boolean checkStepValue( final double value, final SGAxis axis )
	{
		if( value <= 0.0 )
		{
			SGUtility.showMessageDialog(
				this,
				"Set positive value.",
				"Error",
				JOptionPane.ERROR_MESSAGE
			);
			return false;
		}
		else
		{
			SGTuple2d range = axis.getRange();
			final double diff = range.y - range.x;
			final int order = SGUtilityNumber.getOrder(diff);
			final double power = SGUtilityNumber.getPowersOfTen( order -2 );
			if( value < power )
			{
				SGUtility.showMessageDialog(
					this,
					"Set value is too small.",
					"Error",
					JOptionPane.ERROR_MESSAGE
				);
				return false;
			}

		}

		return true;
	}



	/**
	 * Properties for grid.
	 *
	 */
	public static class GridProperties extends SGProperties
	{
		private SGAxis mXAxis;
		private SGAxis mYAxis;
		private double mStepValueX;
		private double mBaselineValueX;
		private double mStepValueY;
		private double mBaselineValueY;
		private boolean mVisibleFlag;
		private boolean mAutoRangeFlag;
		private float mLineWidth;
		private int mLineType;
		private Color mColor;

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

		/**
		 * 
		 */
		public boolean equals( Object obj )
		{
			if( ( obj instanceof GridProperties ) == false ) return false;
			
			GridProperties p = (GridProperties)obj;

			if( this.mXAxis.equals(p.mXAxis) == false ) return false;
			if( this.mYAxis.equals(p.mYAxis) == false ) return false;
			if( this.mBaselineValueX!=p.mBaselineValueX ) return false;
			if( this.mStepValueX!=p.mStepValueX ) return false;
			if( this.mBaselineValueY!=p.mBaselineValueY ) return false;
			if( this.mStepValueY!=p.mStepValueY ) return false;
			if( this.mVisibleFlag!=p.mVisibleFlag ) return false;
			if( this.mAutoRangeFlag!=p.mAutoRangeFlag ) return false;
			if( this.mLineWidth!=p.mLineWidth ) return false;
			if( this.mLineType!=p.mLineType ) return false;
			if( this.mColor.equals(p.mColor) == false ) return false;
			
			return true;
		}

	}


}

