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

import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;

import jp.riken.brain.ni.samuraigraph.base.SGTuple2f;
import jp.riken.brain.ni.samuraigraph.base.SGUtility;
import jp.riken.brain.ni.samuraigraph.figure.SGDrawingElementArrow;
import jp.riken.brain.ni.samuraigraph.figure.SGDrawingElementLine;
import jp.riken.brain.ni.samuraigraph.figure.SGDrawingElementSymbol;


/**
 * An arrow class using Java2D.
 *
 */
public class SGDrawingElementArrow2D extends SGDrawingElementArrow
	implements SGIDrawingElementJava2D
{


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


	protected SGDrawingElementLine getBodyInstance()
	{
		return new SGDrawingElementLine2D();
	}

	protected SGDrawingElementSymbol getHeadInstance()
	{
		return new SGDrawingElementSymbol2D();
	}

	private SGDrawingElementLine2D getLine2D()
	{
		return (SGDrawingElementLine2D)this.mLine;
	}
	
	private SGDrawingElementSymbol2D getStart2D()
	{
		return (SGDrawingElementSymbol2D)this.mStartHead;
	}

	private SGDrawingElementSymbol2D getEnd2D()
	{
		return (SGDrawingElementSymbol2D)this.mEndHead;
	}


	/**
	 * 
	 */
	public boolean contains(final int x, final int y)
	{
		final boolean lineFlag = this.mLine.contains(x,y);
		final boolean startHeadFlag = this.mStartHead.contains(x,y);
		final boolean endHeadFlag = this.mEndHead.contains(x,y);
		final boolean flag = lineFlag | startHeadFlag | endHeadFlag;
		return flag;
	}


	/**
	 * 
	 */
	public Rectangle2D getElementBounds()
	{
		SGDrawingElementLine2D line = this.getLine2D();
		SGDrawingElementSymbol2D start = this.getStart2D();
		SGDrawingElementSymbol2D end = this.getEnd2D();
		ArrayList list = new ArrayList();
		list.add( line.getElementBounds() );
		list.add( start.getElementBounds() );
		list.add( end.getElementBounds() );
		Rectangle2D rect = SGUtility.createUnion( list );
		return rect;
	}


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

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

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

		return af;		
	}


	public float getAngle()
	{
		SGTuple2f start = this.getStart();
		SGTuple2f end = this.getEnd();
		return SGUtilityJava2D.getAngle(
			start.x, start.y, end.x, end.y );
	}



	/**
	 * 
	 * @param g2d
	 * @param stroke
	 * @param clipRect
	 */
	void paint( Graphics2D g2d, Rectangle2D clipRect )
	{
		if( this.isVisible() == false )
		{
			return;
		}

		this.getLine2D().paint( g2d, clipRect );
		this.getStart2D().paint( g2d, clipRect );
		this.getEnd2D().paint( g2d, clipRect );
	}



	/**
	 * 
	 * @return
	 */
	public float getMagnitude()
	{
		final SGTuple2f start = this.getStart();
		final SGTuple2f end = this.getEnd();
		final float x = start.x - end.x;
		final float y = start.y - end.y;
		return (float)Math.sqrt( x*x + y*y );
	}



	/**
	 * 
	 */
	public Shape getBodyShape()
	{
		final float mag = this.getMagnification();
		final float len = this.getMagnitude();
		final float headSize = this.getHeadSize();
		final float diff = 0.50f*mag*headSize;

		final int sType = this.mStartHead.getType();
		final int eType = this.mEndHead.getType();

		float yStart = 0.0f;
		if( sType == SGDrawingElementArrow.SYMBOL_ARROW_HEAD )
		{
			yStart = diff;
		}
		float yEnd = len;
		if( eType == SGDrawingElementArrow.SYMBOL_ARROW_HEAD )
		{
			yEnd = len - diff;
		}

		Line2D line = new Line2D.Float( 0.0f, yStart, 0.0f, yEnd );
		AffineTransform af = this.getAffineTransform();
		Shape shape = af.createTransformedShape( line );

		return shape;
	}



	/**
	 * 
	 */
	public Shape getStartHeadShape()
	{
		Shape sh = this.getStart2D().getShape();
		if( sh==null )
		{
			return null;
		}

		SGTuple2f pos = this.getStart();
		this.mStartHead.setLocation( pos );
		AffineTransform af = new AffineTransform();


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

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

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

		Shape shape = af.createTransformedShape(sh);

		return shape;
	}


	/**
	 *
	 */
	public Shape getEndHeadShape()
	{
		Shape sh = this.getEnd2D().getShape();
		if( sh==null )
		{
			return null;
		}

		SGTuple2f pos = this.getEnd();
		this.mEndHead.setLocation( pos );
		AffineTransform af = new AffineTransform();

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

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

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

		Shape shape = af.createTransformedShape(sh);

		return shape;
	}



	/**
	 * 
	 */
	public Object copy()
	{
		SGDrawingElementArrow2D el = new SGDrawingElementArrow2D();
		this.setPropertiesForCopy(el);
		return el;
	}


	protected boolean setPropertiesForCopy( SGDrawingElementArrow2D el )
	{
		el.setProperties( this.getProperties() );
		SGTuple2f start = this.getStart();
		SGTuple2f end = this.getEnd();
		el.setTermPoints( start, end );
		el.setMagnification( this.mMagnification );
		return true;
	}



	/**
	 *
	 */
	public boolean setTermPoints( final SGTuple2f start, final SGTuple2f end )
	{
		super.setTermPoints( start, end );
		
		final float angle = this.getLine2D().getAngle();
		this.mStartHead.setAngle( angle );
		this.mEndHead.setAngle( angle );
		
		return true;
	}


}


