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

import java.util.*;
import java.awt.*;
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 class SGDrawingElementArrow2D extends SGDrawingElementArrow
	implements SGIDrawingElementJava2D
{


	/**
	 * 
	 */
	protected SGDrawingElementSymbol2D mStartHead = null;


	/**
	 * 
	 */
	protected SGDrawingElementSymbol2D mEndHead = null;



	/**
	 * 
	 */
	public SGDrawingElementArrow2D()
	{
		super();


//this.setEndHeadType( SGDrawingElementSymbol.SYMBOL_TRIANGLE );
//this.setStartHeadType( SGDrawingElementSymbol.SYMBOL_CIRCLE );

//mEndHead = this.createHead( SYMBOL_ARROW_HEAD );
//mStartHead = this.createHead( SGDrawingElementSymbol.SYMBOL_TRIANGLE );

	}


	/**
	 *
	 */
	public boolean setTermPoints( final SGTuple2f start, final SGTuple2f end )
	{
		super.setTermPoints(start,end);

		if( this.mStartHead != null )
		{
			mStartHead.setLocation( mStart );
		}

		if( this.mEndHead != null )
		{
			mEndHead.setLocation( mEnd );
		}

		return true;
	}


	/**
	 * 
	 */
	public boolean setHeadSize( final float size )
	{
		super.setHeadSize(size);

		if( this.mStartHead != null )
		{
			mStartHead.setSize( size );
		}
		if( this.mEndHead != null )
		{
			mEndHead.setSize( size );
		}

		return true;
	}



	/**
	 * 
	 */
	public boolean setHeadEdgeLineWidth( final float width )
	{
		super.setHeadEdgeLineWidth(width);

		if( this.mStartHead != null )
		{
			mStartHead.setLineWidth( width );
		}
		if( this.mEndHead != null )
		{
			mEndHead.setLineWidth( width );
		}

		return true;
	}


	/**
	 * 
	 */
	public boolean setHeadEdgeLineColor( final Color color )
	{
		super.setHeadEdgeLineColor(color);

		if( this.mStartHead != null )
		{
			mStartHead.setLineColor( color );
		}
		if( this.mEndHead != null )
		{
			mEndHead.setLineColor( color );
		}

		return true;
	}



	/**
	 * 
	 */
	public boolean contains(final int x, final int y)
	{
		SGDrawingElementLine2D line = getLine();
		final boolean lineFlag = line.contains(x,y);
		boolean startHeadFlag = false;
		boolean endHeadFlag = false;
		if( this.mStartHead != null )
		{
			startHeadFlag = mStartHead.contains(x,y);
		}
		if( this.mEndHead != null )
		{
			endHeadFlag = mEndHead.contains(x,y);
		}

		final boolean flag = lineFlag || startHeadFlag || endHeadFlag;

		return flag;
	}


	/**
	 * 
	 */
	public Rectangle2D getElementBounds()
	{
		SGDrawingElementLine2D line = this.getLine();
		return line.getElementBounds().getBounds2D();
	}



	/**
	 * 
	 */
	public boolean setMagnification( final float ratio )
	{
		super.setMagnification(ratio);

		if( mStartHead != null )
		{
			mStartHead.setMagnification( ratio );
		}
		if( mEndHead != null )
		{
			mEndHead.setMagnification( ratio );
		}

		return true;
	}



	/**
	 *
	 */
	public boolean zoom( final int w, final int h, final float ratio )
	{
		super.zoom(ratio);

		if( mStartHead != null )
		{
			mStartHead.setMagnification( ratio );
		}
		if( mEndHead != null )
		{
			mEndHead.setMagnification( ratio );
		}

		return true;
	}


	/**
	 * 
	 */
	private SGDrawingElementLine2D getLine()
	{
		SGDrawingElementLine2D line
			= new SGDrawingElementLine2D( mStart, mEnd );
		line.setLineWidth( mLineWidth );
		line.setMagnification(mMagnification);
		return line;		
	}



	/**
	 *
	 */
	private AffineTransform getAffineTransform()
	{
		AffineTransform af = new AffineTransform();

		// 平行移動
		af.translate( this.getStart().x, this.getStart().y );


		// 回転
		final double angle = this.getLineAngle() - 0.50*Math.PI;
		af.rotate(angle);
//System.out.println(angle);


		return af;		
	}



	/**
	 * 
	 */
	public Shape getBodyShape()
	{
//System.out.println("<< SGDrawingElementArrow2D::getBodyShape >>");

		// 矢印の長さ
		final float mag = this.getMagnitude();


		Line2D line = null;

		if( mStartHead==null && mEndHead!=null )
		{
			float y=0.0f;
			if( mEndHead instanceof ArrowHead )
			{
				y = mag - 0.50f*mMagnification*mHeadSize;
			}
			else
			{
				y = mag;
			}

			line = new Line2D.Float(
				0.0f, 0.0f,
				0.0f, y
			);
		}
		else if( mStartHead!=null && mEndHead==null )
		{
			float y=0.0f;
			if( mStartHead instanceof ArrowHead )
			{
				y = 0.50f*mMagnification*mHeadSize;
			}
			else
			{
				y = 0.0f;
			}

			line = new Line2D.Float(
				0.0f, y,
				0.0f, mag
			);
		}
		else if( mStartHead!=null && mEndHead!=null )
		{
			float yStart=0.0f;
			float yEnd=0.0f;
			if( mEndHead instanceof ArrowHead )
			{
				yEnd = mag - 0.50f*mMagnification*mHeadSize;
			}
			else
			{
				yEnd = mag;
			}

			if( mStartHead instanceof ArrowHead )
			{
				yStart = 0.50f*mMagnification*mHeadSize;
			}
			else
			{
				yStart = 0.0f;
			}

			line = new Line2D.Float(
				0.0f, yStart,
				0.0f, yEnd
			);
		}
		else
		{
			line = new Line2D.Float(
				0.0f, 0.0f,
				0.0f, mag
			);
		}

		AffineTransform af = this.getAffineTransform();
		Shape shape = af.createTransformedShape( line );

		return shape;
	}


	/**
	 * 
	 */
	public Shape getStartHeadShape()
	{

		this.createStartHead( this.mStartHeadType );

		if( mStartHead == null )
		{
			return null;
		}

		mStartHead.setLocation( this.mStart );

		AffineTransform af = new AffineTransform();

		// 平行移動
		af.translate( this.getStart().x, this.getStart().y );

		// 回転
		final double angle = this.getLineAngle() - 0.50*Math.PI;
		af.rotate(angle);

		// 平行移動
		af.translate( -this.getStart().x, -this.getStart().y );


		Shape shape = af.createTransformedShape( mStartHead.getShape() );

		return shape;
	}


	/**
	 *
	 */
	public Shape getEndHeadShape()
	{

		this.createEndHead( this.mEndHeadType );

		if( mEndHead == null )
		{
			return null;
		}

		mEndHead.setLocation( this.mEnd );

		AffineTransform af = new AffineTransform();

		// 平行移動
		af.translate( this.getEnd().x, this.getEnd().y );

		// 回転
		double angle = this.getLineAngle() + 0.50*Math.PI;
		af.rotate(angle);
//System.out.println(angle);

		// 平行移動
		af.translate( -this.getEnd().x, -this.getEnd().y );


		Shape shape = af.createTransformedShape( mEndHead.getShape() );

		return shape;
	}


	/**
	 * 
	 */
	public boolean createStartHead( final int type )
	{
		this.mStartHead = this.createHead(type);
		return true;
	}


	/**
	 * 
	 */
	public boolean createEndHead( final int type )
	{
		this.mEndHead = this.createHead(type);
		return true;
	}


	/**
	 * 
	 */
	private SGDrawingElementSymbol2D createHead( final int type )
	{

		SGDrawingElementSymbol2D head = null;
		if( type == SGDrawingElementSymbol.SYMBOL_TYPE_VOID )
		{
			return null;
		}
		else if( type == SYMBOL_ARROW_HEAD )
		{
			head = new ArrowHead();
		}
		else
		{
			head = new SGDrawingElementSymbol2D();
			head.setType( type );
		}

		head.setSize( this.mHeadSize );
		head.setMagnification( this.mMagnification );
		head.setColorList( this.mColorList );

		return head;
	}




	/**
	 * 
	 */
	class ArrowHead extends SGDrawingElementSymbol2D
	{

		/**
		 * 
		 */
		protected ArrowHead()
		{
			super();
		}


		/**
		 * 
		 */
		public Shape getShape()
		{
			final float x = this.getX();
			final float y = this.getY();

			Point2D[] pointArray = new Point2D[4];
			pointArray[0] = new Point2D.Float(
				x,
				y
			);
			pointArray[1] = new Point2D.Float(
				x + mMagnification*mHeadSize*(float)Math.tan(mHeadOpenAngle),
				y + mMagnification*mHeadSize
			);
			pointArray[2] = new Point2D.Float(
				x,
				y + mMagnification*mHeadSize*( 1.0f - (float)Math.tan(mHeadOpenAngle)/(float)Math.tan(mHeadCloseAngle) )
			);
			pointArray[3] = new Point2D.Float(
				x - mMagnification*mHeadSize*(float)Math.tan(mHeadOpenAngle),
				y + mMagnification*mHeadSize
			);

			Line2D[] lineArray = new Line2D[pointArray.length];
			for( int ii=0; ii<lineArray.length; ii++ )
			{
				lineArray[ii] = new Line2D.Float( pointArray[ii], pointArray[(ii+1)%pointArray.length] );
//System.out.println(pointArray[ii]);
			}
//System.out.println();

			GeneralPath gp = new GeneralPath();
			for( int ii=0; ii<lineArray.length; ii++ )
			{
				gp.append( lineArray[ii], true );
			}

			AffineTransform af = this.getAffineTransform();
			Shape shape = af.createTransformedShape( gp );

			return gp;
		}


	}



}

