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

import java.util.*;
import java.awt.*;
import java.io.*;
import java.awt.geom.*;
import jp.riken.brain.ni.samuraigraph.base.*;
import jp.riken.brain.ni.samuraigraph.data.*;
import jp.riken.brain.ni.samuraigraph.figure.*;


/**
 * 
 */
public abstract class SGElementGroupLine extends SGElementGroupSXY
{

	/**
	 *
	 */
	protected float mLineWidth;


	/**
	 *
	 */
	protected int mLineType;


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


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


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


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



	/**
	 * 
	 */
//	protected GeneralPath mConnectedPath = new GeneralPath();
	protected final ArrayList mConnectedPathList = new ArrayList();



	/**
	 * 
	 */
	public static final String PF_LINE_TITLE = "<Line>";
	public static final String KEY_LINE_WIDTH = "Width";
	public static final String KEY_LINE_TYPE = "Type";
	public static final String PF_LINE_COLOR_LIST = "Color List";




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



	/**
	 *
	 */
	public boolean setLineWidth( final float width )
	{
		if( width<0.0f )
		{
			throw new IllegalArgumentException("width<0.0f");
		}
		mLineWidth = width;

		if( this.mDrawingElementArray!=null )
		{
			for( int ii=0; ii<this.mDrawingElementArray.length; ii++ )
			{
				SGDrawingElementLine2D el = (SGDrawingElementLine2D)mDrawingElementArray[ii];
				el.setLineWidth( mLineWidth );
			}
		}

		return true;
	}


	/**
	 *
	 */
	public boolean setLineType( final int type )
	{
		mLineType = type;

		if( this.mDrawingElementArray!=null )
		{
			for( int ii=0; ii<this.mDrawingElementArray.length; ii++ )
			{
				SGDrawingElementLine2D el = (SGDrawingElementLine2D)mDrawingElementArray[ii];
				el.setType( mLineType );
			}
		}

		return true;
	}



	/**
	 * 
	 * @param cap
	 * @return
	 */
	public boolean setBasicStrokeProperty(
		final int cap, final int join, final float miterlimit, final float dash_phase )
	{

		if( cap!=BasicStroke.CAP_BUTT && cap!=BasicStroke.CAP_ROUND
			&& cap!=BasicStroke.CAP_SQUARE )
		{
			throw new IllegalArgumentException("The argument is invalid.");
		}

		if( join!=BasicStroke.JOIN_BEVEL && join!=BasicStroke.JOIN_MITER
			&& join!=BasicStroke.JOIN_ROUND )
		{
			throw new IllegalArgumentException("The argument is invalid.");
		}

		if( miterlimit<1.0f && join==BasicStroke.JOIN_MITER )
		{
			throw new IllegalArgumentException("The argument is invalid.");
		}

		if( dash_phase<0.0f )
		{
			throw new IllegalArgumentException("The argument is invalid.");
		}

		this.mCap = cap;
		this.mJoin = join;
		this.mMiterLimit = miterlimit;
		this.mDashPhase = dash_phase;

		return true;
	}



	/**
	 * 
	 */
	public float getLineWidth()
	{
		return this.mLineWidth;
	}


	/**
	 * 
	 */
	public int getLineType()
	{
		return this.mLineType;
	}


	/**
	 * 
	 * @return
	 */
	public int getCap()
	{
		return this.mCap;
	}



	/**
	 * 
	 */
	public int getJoin()
	{
		return this.mJoin;
	}


	/**
	 * 
	 * @return
	 */
	public float getMiterLimit()
	{
		return this.mMiterLimit;
	}


	/**
	 * 
	 */
	public SGDrawingElement getDrawingElement()
	{
		// line
		SGDrawingElementLine line = new SGDrawingElementLine2D();
		line.setVisible( this.mVisibleFlag );
		line.setLineWidth( mLineWidth );
		line.setType( mLineType );
		line.setColorList( mColorList );

		return line;		
	}


	/**
	 * 
	 */
	public boolean setProperty( final SGDrawingElement element )
	{
		if( !(element instanceof SGDrawingElementLine) )
		{
			return false;
		}

		if( super.setProperty( element ) == false )
		{
			return false;
		}

		SGDrawingElementLine line = (SGDrawingElementLine)element;

		this.setLineType( line.getLineType() );
		this.setLineWidth( line.getLineWidth() );
	
		return true;
	}





	/**
	 * 
	 */
	public boolean paintElement( final Graphics2D g2d )
	{

		if( g2d==null )
		{
			return false;
		}

		g2d.setPaint(this.getColor(0));

		final float width = this.mMagnification*this.mLineWidth;

		switch( this.mLineType )
		{
			case SGDrawingElementLine2D.LINE_TYPE_SOLID :
			{
				g2d.setStroke
				(
					new BasicStroke
					(
						width,
						this.mCap,
						this.mJoin
					)
				);
				break;
			}
					
			case SGDrawingElementLine2D.LINE_TYPE_BROKEN :
			{
				final float[] dash = {2.0f*width,width};
				g2d.setStroke
				(
					new BasicStroke
					(
						width,
						this.mCap,
						this.mJoin,
						this.mMiterLimit,
						dash,
						this.mDashPhase
					)
				);
				break;
			}

			case SGDrawingElementLine2D.LINE_TYPE_DOT :
			{
				final float[] dash = {width};
				g2d.setStroke
				(
					new BasicStroke
					(
						width,
						this.mCap,
						this.mJoin,
						this.mMiterLimit,
						dash,
						this.mDashPhase
					)
				);
				break;
			}
					
			default :
			{
						
			}

		}


		// •`‰æ
//		g2d.draw( this.mConnectedPath );
		for( int ii=0; ii<this.mConnectedPathList.size(); ii++ )
		{
			GeneralPath gp = (GeneralPath)this.mConnectedPathList.get(ii);
			g2d.draw( gp );
		}


		return true;
	}






	/**
	 * 
	 */
	public boolean writePropertyOfData( final Writer writer ) throws IOException
	{
		writer.write( PF_LINE_TITLE + "\n" );

		SGUtilityText.writePropertyLine( writer, KEY_VISIBLE, new Boolean( this.mVisibleFlag ) );
		SGUtilityText.writePropertyLine( writer, KEY_LINE_WIDTH, new Float( this.mLineWidth ) );
		SGUtilityText.writePropertyLine( writer, KEY_LINE_TYPE, SGDrawingElementLine.getLineTypeName( this.mLineType ) );
		SGUtilityText.writeColorListPropertyLine( writer, PF_LINE_COLOR_LIST, this.mColorList );

		writer.write("\n");

		return true;
	}



	/**
	 * 
	 */
	public SGProperties readProperties( final BufferedReader br ) throws IOException
	{

		LineProperties p = new LineProperties();

		String line = null;

		// visible
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}
		Boolean bVisible = SGUtilityText.getBooleanPropertyFromLine(line,KEY_VISIBLE);
		if( bVisible == null )
		{
			return null;
		}
		p.setVisible( bVisible.booleanValue() );


		// line width
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}
		Double dLineWidth = SGUtilityText.getDoublePropertyFromLine(line,KEY_LINE_WIDTH);
		if( dLineWidth == null )
		{
			return null;
		}
		p.setLineWidth( dLineWidth.floatValue() );


		// line type
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}
		String strLineType = SGUtilityText.getPropertyFromLine(line,KEY_LINE_TYPE);
		if( strLineType == null )
		{
			return null;
		}
		Integer nLineType = SGDrawingElementLine.getLineTypeFromName(strLineType);
		if( nLineType == null )
		{
			return null;
		}
		p.setLineType( nLineType.intValue() );


		// color list
		if( ( line = SGUtilityText.readLine(br) ) == null )
		{
			return null;
		}
		p.setColorList( SGUtilityText.getColorListFromLine(line,PF_LINE_COLOR_LIST) );


		return p;
	}




	/**
	 * 
	 * @return
	 */
	protected boolean initDrawingElement( final int num )
	{
		final int lNum = num-1;
		SGDrawingElementLine[] array = new SGDrawingElementLine[lNum];
		for( int ii=0; ii<lNum; ii++ )
		{
			array[ii] = new SGDrawingElementLine2D();
		}
		this.mDrawingElementArray = array;
		return true;
	}


	/**
	 * 
	 * @return
	 */
	protected boolean initDrawingElement( final SGTuple2f[] array )
	{
		final int lNum = array.length - 1;
		SGDrawingElementLine[] lArray = new SGDrawingElementLine[lNum];
		for( int ii=0; ii<lNum; ii++ )
		{
			lArray[ii] = new SGDrawingElementLine2D();
			lArray[ii].setTermPoints( array[ii], array[ii+1] );
		}
		this.mDrawingElementArray = lArray;
		return true;
	}


	/**
	 * 
	 */
	public boolean setLocation(
		final SGTuple2f[] pointArray )
	{

		if( this.mDrawingElementArray==null )
		{
			return true;
		}

		if( pointArray.length - 1 != this.mDrawingElementArray.length )
		{
			throw new Error("pointArray.length != this.mDrawingElementArray.length + 1");
		}


		// clear the list
		this.mConnectedPathList.clear();


		// check the effectivity of points
		boolean allEffectiveFlag = true;
		final boolean[] effArray = new boolean[pointArray.length];
		for( int ii=0; ii<effArray.length; ii++ )
		{
			effArray[ii] = !( pointArray[ii].isInfinite() || pointArray[ii].isNaN()	);
			if( effArray[ii] == false )
			{
				allEffectiveFlag = false;
			}
		}


		// if all points are effective
		if( allEffectiveFlag )
		{
/*
			// set location to the drawing elements
			for( int ii=0; ii<pointArray.length-1; ii++ )
			{
				final SGDrawingElementLine line
					= (SGDrawingElementLine)this.mDrawingElementArray[ii];
				line.setTermPoints( pointArray[ii], pointArray[ii+1] );
			}
*/
			// create a general path
			GeneralPath gp = new GeneralPath();
			Line2D line = new Line2D.Float(
				pointArray[0].x, pointArray[0].y, pointArray[1].x, pointArray[1].y );
			gp.append( line, true );
			if( pointArray.length > 2 )
			{
				for( int ii=2; ii<pointArray.length; ii++ )
				{
					gp.lineTo( pointArray[ii].x, pointArray[ii].y );
				}
			}
			this.mConnectedPathList.add( gp );			

		}
		else
		{
			//
			ArrayList lineListList = new ArrayList();
			ArrayList list = new ArrayList();
			for( int ii=0; ii<pointArray.length-1; ii++ )
			{
				final SGDrawingElementLine line
					= (SGDrawingElementLine)this.mDrawingElementArray[ii];
				final boolean eff = effArray[ii] && effArray[ii+1];
				line.setVisible( eff );
				if( eff )
				{
//					line.setTermPoints( pointArray[ii], pointArray[ii+1] );
					list.add( line );
				}
				else
				{
					if( list.size()!=0 )
					{
						lineListList.add( list );
						list = new ArrayList();
					}
				}
			}
			if( list.size()!=0 )
			{
				lineListList.add( list );
			}

//System.out.println(lineListList);
//System.out.println();

			// create a general path
			for( int ii=0; ii<lineListList.size(); ii++ )
			{
				ArrayList lineList = (ArrayList)lineListList.get(ii);
				GeneralPath gp = new GeneralPath();
				SGDrawingElementLine el
					= (SGDrawingElementLine)lineList.get(0);
				Line2D line = SGDrawingElementLine2D.getLine( el );
				gp.append( line, true );
				if( lineList.size() > 2 )
				{
					for( int jj=1; jj<lineList.size(); jj++ )
					{
						el = (SGDrawingElementLine)lineList.get(jj);
						SGTuple2f next = el.getEnd();
						gp.lineTo( next.x, next.y );
					}
				}

				this.mConnectedPathList.add(gp);
			}
		}


		return true;
	}



	/**
	 * 
	 * @return
	 */
	public boolean setPropertiesOfDrawingElements()
	{
		for( int ii=0; ii<this.mDrawingElementArray.length; ii++ )
		{
			final SGDrawingElementLine line
				= (SGDrawingElementLine)this.mDrawingElementArray[ii];
			line.setType( this.mLineType );
			line.setColorList( this.mColorList );
			line.setLineWidth( this.mLineWidth );
			line.setMagnification( this.mMagnification );
		}
		return true;
	}



	/**
	 * 
	 */
	public SGProperties getProperties()
	{
		LineProperties p = new LineProperties();
		this.getProperties(p);
		return p;
	}


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

		super.getProperties(p);

		LineProperties lp = (LineProperties)p;
		lp.setLineWidth( this.getLineWidth() );
		lp.setLineType( this.getLineType() );

		return true;
	}




	/**
	 * 
	 */
	public boolean setProperties( SGProperties p )
	{

		if( ( p instanceof LineProperties ) == false ) return false;

		if( super.setProperties(p) == false ) return false;

		LineProperties lp = (LineProperties)p;
		Float width = lp.getLineWidth();
		if( width==null )
		{
			return false;
		}
		Integer type = lp.getLineType();
		if( type==null )
		{
			return false;
		}

		this.setLineWidth( width.floatValue() );
		this.setLineType( type.intValue() );

		return true;
	}





	/**
	 * 
	 */
	public static class LineProperties extends ElementGroupProperties
	{
//		int type;
//		float width;

		public static final String[] keys = {
			KEY_LINE_WIDTH, KEY_LINE_TYPE
		};


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


		/**
		 * 
		 */
		public boolean equals( final Object obj )
		{
			if( ( obj instanceof LineProperties ) == false )
			{
				return false;
			}

			if( super.equals(obj) == false ) return false;

			LineProperties p = (LineProperties)obj;
			for( int ii=0; ii<keys.length; ii++ )
			{
				if( this.getProperty(keys[ii]).equals(p.getProperty(keys[ii])) == false )
				{
					return false;
				}
			}

			return true;
		}


		/**
		 * 
		 */
/*		public String toString()
		{
			String str = new String("[");
			str += new String("visible="+visible+", ");
			str += new String("colorList="+colorList+", ");
			str += new String("type="+type+", ");
			str += new String("width="+width+", ");
			str += new String("]");

			return str;
		}
*/

		public Float getLineWidth()
		{
			final String value = this.getProperty(KEY_LINE_WIDTH);
			return SGUtilityText.getFloat(value);
		}


		public Integer getLineType()
		{
			final String value = this.getProperty(KEY_LINE_TYPE);
			return SGUtilityText.getInteger(value);
		}


		public boolean setLineWidth( final float w )
		{
			if( w<0.0f )
			{
				return false;
			}
			this.setProperty( KEY_LINE_WIDTH, new Float(w).toString() );
			return true;
		}

		public boolean setLineType( final int num )
		{
			this.setProperty( KEY_LINE_TYPE, new Integer(num).toString() );
			return true;
		}


	}


}



