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

import java.io.IOException;
import java.io.Writer;

import jp.riken.brain.ni.samuraigraph.base.SGData;
import jp.riken.brain.ni.samuraigraph.base.SGUtility;
import jp.riken.brain.ni.samuraigraph.base.SGUtilityNumber;


/**
 * Two dimensional vector data.
 * 
 */
public class SGVXYData extends SGData
	implements SGIVXYTypeData, Cloneable
{

	// array of x-coordinates of the start points
	private double[] mXCoordinateArray = null;

	// array of y-coordinates of the start points
	private double[] mYCoordinateArray = null;

	// array of components
	// in polar mode, used as the magnitude of vectors
	// otherwise, used as x component values
	private double[] mComponentArray1 = null;

	// array of components
	// in polar mode, used as the angle of vectors
	// otherwise, used as y component values
	private double[] mComponentArray2 = null;

	// flag of the mode
	private boolean mPolarFlag = false;


	/**
	 * The default constructor.
	 */
	public SGVXYData()
	{
		super();
	}


	/**
	 * Constructor.
	 */
	public SGVXYData( final double[] xArray, final double[] yArray, final double[] array1, final double[] array2, final boolean isPolar )
	{
		super();

		if( xArray==null || yArray==null || array1==null || array2==null )
		{
			throw new IllegalArgumentException("xArray==null || yArray==null || array1==null || array2==null");
		}

		if( xArray.length!=yArray.length || xArray.length!=array1.length || xArray.length!=array2.length )
		{
			throw new IllegalArgumentException("xArray.length!=yArray.length || xArray.length!=array1.length || xArray.length!=array2.length");
		}


		if( isPolar )
		{
			for( int ii=0; ii<array1.length; ii++ )
			{
				if( array1[ii] < 0.0 )
				{
					throw new IllegalArgumentException("Negative values for the magnitude are unacceptable.");
				}
			}
		}


		// set to attributes
		this.mXCoordinateArray = SGUtility.copyDoubleArray( xArray );
		this.mYCoordinateArray = SGUtility.copyDoubleArray( yArray );
		this.mComponentArray1 = SGUtility.copyDoubleArray( array1 );
		this.mComponentArray2 = SGUtility.copyDoubleArray( array2 );
		this.mPolarFlag = isPolar;
	}


	/**
	 * 
	 * @return
	 */
	public SGVXYData( final SGData data )
	{
		super();
		this.setData( data );
	}


	/**
	 * 
	 */
	public boolean setData( final SGData data )
	{
		if( !(data instanceof SGVXYData) )
		{
			return false;
		}

		SGVXYData vxyData = (SGVXYData)data;
		this.mXCoordinateArray = vxyData.getXCoordinateArray();
		this.mYCoordinateArray = vxyData.getYCoordinateArray();
		this.mComponentArray1 = this.copyArray( vxyData.mComponentArray1 );
		this.mComponentArray2 = this.copyArray( vxyData.mComponentArray2 );
		this.mPolarFlag = vxyData.isPolar();

		return true;
	}


	/**
	 * 
	 * @return
	 */
	public int getPointsNumber()
	{
		if( this.mXCoordinateArray==null )
		{
			return -1;
		}
		return this.mXCoordinateArray.length;
	}


	/**
	 * Returns the name of data type.
	 * @return
	 */
	public String getDataType()
	{
		return SGDataTypeConstants.VXY_DATA;
	}



	/**
	 * 
	 * @return
	 */
	public double getMinValueX()
	{
		return SGUtilityNumber.min( this.mXCoordinateArray );
	}


	/**
	 * 
	 * @return
	 */
	public double getMinValueY()
	{
		return SGUtilityNumber.min( this.mYCoordinateArray );
	}


	/**
	 * 
	 * @return
	 */
	public double getMaxValueX()
	{
		return SGUtilityNumber.max( this.mXCoordinateArray );
	}


	/**
	 * 
	 * @return
	 */
	public double getMaxValueY()
	{
		return SGUtilityNumber.max( this.mYCoordinateArray );
	}


	/**
	 * 
	 * @return
	 */
	public double[] getXCoordinateArray()
	{
		return SGUtility.copyDoubleArray( this.mXCoordinateArray );
	}


	/**
	 * 
	 * @return
	 */
	public double[] getYCoordinateArray()
	{
		return SGUtility.copyDoubleArray( this.mYCoordinateArray );
	}


	/**
	 * 
	 * @return
	 */
	public double[] getXComponentArray()
	{
		if( this.isPolar() )
		{
			final int len = this.getPointsNumber();
			final double[] array = new double[len];
			for( int ii=0; ii<len; ii++ )
			{
				// r*com(theta)
				array[ii] = this.mComponentArray1[ii]*Math.cos( this.mComponentArray2[ii] );
			}
			return array;
		}
		else
		{
			return this.copyArray( this.mComponentArray1 );
		}
	}


	public double[] getYComponentArray()
	{
		if( this.isPolar() )
		{
			final int len = this.getPointsNumber();
			final double[] array = new double[len];
			for( int ii=0; ii<len; ii++ )
			{
				// r*com(theta)
				array[ii] = this.mComponentArray1[ii]*Math.sin( this.mComponentArray2[ii] );
			}
			return array;
		}
		else
		{
			return this.copyArray( this.mComponentArray2 );
		}
	}



	/**
	 * 
	 * @return
	 */
	public double[] getMagnitudeArray()
	{
		if( this.isPolar() )
		{
			return this.copyArray( this.mComponentArray1 );
		}
		else
		{
			final int len = this.getPointsNumber();
			final double[] array = new double[len];
			for( int ii=0; ii<len; ii++ )
			{
				// r = sqrt( x^2 + y^2 )
				final double x = this.mComponentArray1[ii];
				final double y = this.mComponentArray2[ii];
				array[ii] = Math.sqrt( x*x + y*y );
			}
			return array;
		}
	}


	/**
	 * 
	 * @return
	 */
	public double[] getAngleArray()
	{
		if( this.isPolar() )
		{
			return this.copyArray( this.mComponentArray2 );
		}
		else
		{
			final int len = this.getPointsNumber();
			final double[] array = new double[len];
			for( int ii=0; ii<len; ii++ )
			{
				// theta = atan(y/x)
				final double x = this.mComponentArray1[ii];
				final double y = this.mComponentArray2[ii];
				array[ii] = Math.atan2(y,x);
			}
			return array;
		}
	}


	private double[] copyArray( final double[] array )
	{
		return SGUtility.copyDoubleArray( array );
	}


	/**
	 * 
	 * @return
	 */
	public boolean isPolar()
	{
		return this.mPolarFlag;
	}


	/**
	 * 
	 */
	public Object clone()
	{
		try
		{
			return super.clone();
		}
		catch( CloneNotSupportedException ex )
		{
			// this shouldn't happen, since we are Cloneable
			throw new InternalError();
		}
	}


	/**
	 * Calling the clone method.
	 */
	public Object copy()
	{
		return this.clone();
	}


	public void dispose()
	{
		super.dispose();
		this.mXCoordinateArray = null;
		this.mYCoordinateArray = null;
		this.mComponentArray1 = null;
		this.mComponentArray2 = null;
	}

	
	/*
	 * write data to file
	 * @see jp.riken.brain.ni.samuraigraph.base.SGData#writeData(java.io.Writer)
	 */
	protected boolean writeData( Writer fw ) throws IOException
	{
		if( this.mXCoordinateArray == null )
			return false;
		if( this.mXCoordinateArray.length == 0 )
			return false;
		for(int ii=0; ii<this.mXCoordinateArray.length; ii++){
			String line = this.mXCoordinateArray[ii] + "," + this.mYCoordinateArray[ii] + "," + 
				this.mComponentArray1[ii] + "," + this.mComponentArray2[ii] + "\n";
			fw.write(line);
		}
		return true;
	}
}

