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

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import jp.riken.brain.ni.samuraigraph.base.SGDrawingElement;
import jp.riken.brain.ni.samuraigraph.base.SGIConstants;
import jp.riken.brain.ni.samuraigraph.base.SGProperties;
import jp.riken.brain.ni.samuraigraph.base.SGTuple2f;
import jp.riken.brain.ni.samuraigraph.base.SGUtilityText;
import jp.riken.brain.ni.samuraigraph.figure.SGDrawingElementArrow;
import jp.riken.brain.ni.samuraigraph.figure.SGDrawingElementLine;
import jp.riken.brain.ni.samuraigraph.figure.SGIArrowConstants;
import jp.riken.brain.ni.samuraigraph.figure.SGISymbolConstants;
import org.w3c.dom.Element;


/**
 * 
 */
public abstract class SGElementGroupArrow extends SGElementGroupVXY
	implements SGIArrowConstants, SGIElementGroupConstants
{

	/**
	 *
	 */
	protected int mLineType;


	/**
	 * 倍率100%のときの線幅
	 */
	protected float mLineWidth;


	/**
	 * 全体の中での矢尻の長さ
	 */
	protected float mHeadSize;


	/**
	 * 矢尻の開き角度
	 */
	protected float mHeadOpenAngle;


	/**
	 * 矢尻の閉じ角度
	 */
	protected float mHeadCloseAngle;


	/**
	 * 
	 */
	protected float mHeadLineWidth;


//	/**
//	 * 
//	 */
//	protected Color mHeadLineColor;


	/**
	 * 
	 */
	protected int mStartHeadType;


	/**
	 * 
	 */
	protected int mEndHeadType;


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



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


	/**
	 * 
	 * @param unit
	 * @return
	 */
	public float getLineWidth( final String unit )
	{
		return (float)SGUtilityText.convertFromPoint( this.getLineWidth(), unit );
	}


	/**
	 * 
	 */
	public float getHeadSize()
	{
		return this.mHeadSize;
	}


	/**
	 * 
	 * @param unit
	 * @return
	 */
	public float getHeadSize( final String unit )
	{
		return (float)SGUtilityText.convertFromPoint( this.getHeadSize(), unit );
	}


	/**
	 * 
	 */
	public float getHeadOpenAngle()
	{
		return this.mHeadOpenAngle;
	}


	/**
	 * 
	 */
	public float getHeadCloseAngle()
	{
		return this.mHeadCloseAngle;
	}


//	/**
//	 * 
//	 */
//	public float getHeadLineWidth()
//	{
//		return this.mHeadLineWidth;
//	}


//	/**
//	 * 
//	 */
//	public Color getHeadLineColor()
//	{
//		return this.mHeadLineColor;
//	}


	/**
	 * 
	 */
	public int getStartHeadType()
	{
		return this.mStartHeadType;
	}


	/**
	 * 
	 */
	public int getEndHeadType()
	{
		return this.mEndHeadType;
	}


	/**
	 * 
	 */
	public boolean setLineType( final int type )
	{
		this.mLineType = type;
		SGDrawingElement[] array = this.mDrawingElementArray;
		if( array!=null )
		{
			for( int ii=0; ii<array.length; ii++ )
			{
				SGDrawingElementArrow el = (SGDrawingElementArrow)array[ii];
				el.setLineType(type);
			}
		}
		return true;
	}


	/**
	 * 
	 */
	public boolean setLineWidth( final float width )
	{
		this.mLineWidth = width;
		SGDrawingElement[] array = this.mDrawingElementArray;
		if( array!=null )
		{
			for( int ii=0; ii<array.length; ii++ )
			{
				SGDrawingElementArrow el = (SGDrawingElementArrow)array[ii];
				el.setLineWidth(width);
			}
		}
		return true;
	}


	/**
	 * 
	 * @param width
	 * @param unit
	 * @return
	 */
	public boolean setLineWidth( final float width, final String unit )
	{
		final double lw = SGUtilityText.convertToPoint( width, unit );
		if( this.setLineWidth( (float)lw ) == false )
		{
			return false;
		}

		return true;
	}


	/**
	 * 
	 */
	public boolean setHeadSize( final float size )
	{
		this.mHeadSize = size;
		SGDrawingElement[] array = this.mDrawingElementArray;
		if( array!=null )
		{
			for( int ii=0; ii<array.length; ii++ )
			{
				SGDrawingElementArrow el = (SGDrawingElementArrow)array[ii];
				el.setHeadSize( this.mHeadSize );
			}
		}
		return true;
	}


	/**
	 * 
	 * @param width
	 * @param unit
	 * @return
	 */
	public boolean setHeadSize( final float size, final String unit )
	{
		final double s = SGUtilityText.convertToPoint( size, unit );
		if( this.setHeadSize( (float)s ) == false )
		{
			return false;
		}

		return true;
	}


	/**
	 * 
	 */
	public boolean setHeadOpenAngle( final float angle )
	{
		this.mHeadOpenAngle = angle;
		SGDrawingElement[] array = this.mDrawingElementArray;
		if( array!=null )
		{
			for( int ii=0; ii<array.length; ii++ )
			{
				SGDrawingElementArrow el = (SGDrawingElementArrow)array[ii];
				el.setHeadOpenAngle(angle);
			}
		}
		return true;
	}


	/**
	 * 
	 */
	public boolean setHeadCloseAngle( final float angle )
	{
		this.mHeadCloseAngle = angle;
		SGDrawingElement[] array = this.mDrawingElementArray;
		if( array!=null )
		{
			for( int ii=0; ii<array.length; ii++ )
			{
				SGDrawingElementArrow el = (SGDrawingElementArrow)array[ii];
				el.setHeadCloseAngle(angle);
			}
		}
		return true;
	}



//	/**
//	 * 
//	 */
//	public boolean setHeadLineWidth( final float width )
//	{
//		this.mHeadLineWidth = width;
//		SGDrawingElement[] array = this.mDrawingElementArray;
//		if( array!=null )
//		{
//			for( int ii=0; ii<array.length; ii++ )
//			{
//				SGDrawingElementArrow el = (SGDrawingElementArrow)array[ii];
//				el.setHeadLineWidth(width);
//			}
//		}
//		return true;
//	}


	public Color getColor()
	{
		return this.getColor(0);
	}


	/**
	 * 
	 */
	public boolean setColor( final Color cl )
	{
		super.setColor( cl );
		SGDrawingElement[] array = this.mDrawingElementArray;
		if( array!=null )
		{
			for( int ii=0; ii<array.length; ii++ )
			{
				SGDrawingElementArrow el = (SGDrawingElementArrow)array[ii];
				el.setColor( cl );
			}
		}
		return true;
	}



	/**
	 * 
	 */
//	public boolean setHeadLineColor( final Color color )
//	{
//		this.mHeadLineColor = color;
//		SGDrawingElement[] array = this.mDrawingElementArray;
//		if( array!=null )
//		{
//			for( int ii=0; ii<array.length; ii++ )
//			{
//				SGDrawingElementArrow el = (SGDrawingElementArrow)array[ii];
//				el.setHeadLineColor(color);
//			}
//		}
//		return true;
//	}


	/**
	 * 
	 */
	public boolean setStartHeadType( final int type )
	{
		this.mStartHeadType = type;
		SGDrawingElement[] array = this.mDrawingElementArray;
		if( array!=null )
		{
			for( int ii=0; ii<array.length; ii++ )
			{
				SGDrawingElementArrow el = (SGDrawingElementArrow)array[ii];
				el.setStartHeadType(type);
			}
		}
		return true;
	}


	/**
	 * 
	 */
	public boolean setEndHeadType( final int type )
	{
		this.mEndHeadType = type;
		SGDrawingElement[] array = this.mDrawingElementArray;
		if( array!=null )
		{
			for( int ii=0; ii<array.length; ii++ )
			{
				SGDrawingElementArrow el = (SGDrawingElementArrow)array[ii];
				el.setEndHeadType(type);
			}
		}
		return true;
	}


	/**
	 * 
	 */
	public SGDrawingElement getDrawingElement()
	{
		SGDrawingElementArrow arrow
			= (SGDrawingElementArrow)this.createDrawingElementInstance();
		arrow.setVisible( this.isVisible() );
		arrow.setLineType( this.getLineType() );
		arrow.setLineWidth( this.getLineWidth() );
		arrow.setColorList( this.getColorList() );
		arrow.setHeadSize( this.getHeadSize() );
		arrow.setHeadOpenAngle( this.getHeadOpenAngle() );
		arrow.setHeadCloseAngle( this.getHeadCloseAngle() );
		arrow.setStartHeadType( this.getStartHeadType() );
		arrow.setEndHeadType( this.getEndHeadType() );

		return arrow;
	}


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

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

		SGDrawingElementArrow arrow = (SGDrawingElementArrow)element;

		this.setLineType( arrow.getLineType() );
		this.setLineWidth( arrow.getLineWidth() );
		this.setHeadSize( arrow.getHeadSize() );
		this.setHeadOpenAngle( arrow.getHeadOpenAngle() );
		this.setHeadCloseAngle( arrow.getHeadCloseAngle() );
//		this.setHeadLineWidth( arrow.getHeadLineWidth() );
//		this.setHeadLineColor( arrow.getHeadLineColor() );
		this.setStartHeadType( arrow.getStartHeadType() );
		this.setEndHeadType( arrow.getEndHeadType() );

		return true;
	}

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


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


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


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


	/**
	 * 
	 */
	public boolean paintElement(
		final Graphics2D g2d, final Rectangle2D clipRect )
	{
		SGDrawingElement[] array = this.mDrawingElementArray;
		if( array==null )
		{
			return true;
		}


		for( int ii=0; ii<array.length; ii++ )
		{
			SGDrawingElementArrow2D el
				= (SGDrawingElementArrow2D)array[ii];

			if( el.isVisible() == false )
			{
				continue;
			}

			el.paint( g2d, clipRect );
		}

		return true;
	}



	/**
	 * 
	 */
	public static String getSymbolTypeName( final int type )
	{
//System.out.println("type="+type);

		String typeName = null;
		switch( type )
		{
			case SGISymbolConstants.SYMBOL_TYPE_CIRCLE :
			{
				typeName = SYMBOL_NAME_CIRCLE;
				break;
			}

			case SGISymbolConstants.SYMBOL_TYPE_SQUARE :
			{
				typeName = SYMBOL_NAME_SQUARE;
				break;
			}

			case SGISymbolConstants.SYMBOL_TYPE_TRIANGLE :
			{
				typeName = SYMBOL_NAME_TRIANGLE;
				break;
			}

			case SGISymbolConstants.SYMBOL_TYPE_CROSS :
			{
				typeName = SYMBOL_NAME_CROSS;
				break;
			}

			case SGISymbolConstants.SYMBOL_TYPE_TRANSVERSELINE :
			{
				typeName = SYMBOL_NAME_TRANSVERSE_LINE;
				break;
			}

			case SGIArrowConstants.SYMBOL_TYPE_ARROW_HEAD :
			{
				typeName = SYMBOL_NAME_ARROW_HEAD;
				break;
			}

			case SGISymbolConstants.SYMBOL_TYPE_VOID :
			{
				typeName = NO_SYMBOL;
				break;
			}

			default :
			{
				
			}
		}

		return typeName;
	}






	/**
	 * 
	 * @return
	 */
	public String getTagName()
	{
		return TAG_NAME_ARROW;
	}


	/**
	 * 
	 */
	public boolean writeProperty( final Element el )
	{
		final float cp = SGIConstants.CM_POINT_RATIO;
		final float rd = SGIConstants.RADIAN_DEGREE_RATIO;

		el.setAttribute( KEY_LINE_WIDTH, Float.toString( this.getLineWidth() ) + SGIConstants.pt );
		el.setAttribute( KEY_LINE_TYPE, SGDrawingElementLine.getLineTypeName( this.getLineType() ) );
		el.setAttribute( KEY_HEAD_SIZE, Float.toString( this.getHeadSize()*cp ) + SGIConstants.cm );
		el.setAttribute( KEY_START_HEAD_TYPE, SGDrawingElementArrow.getSymbolTypeName( this.getStartHeadType() ) );
		el.setAttribute( KEY_END_HEAD_TYPE, SGDrawingElementArrow.getSymbolTypeName( this.getEndHeadType() ) );
		el.setAttribute( KEY_HEAD_OPEN_ANGLE, Double.toString( this.getHeadOpenAngle()/rd ) + SGIConstants.degree );
		el.setAttribute( KEY_HEAD_CLOSE_ANGLE, Double.toString( this.getHeadCloseAngle()/rd ) + SGIConstants.degree );
		el.setAttribute( KEY_COLOR_LIST, SGUtilityText.getColorListString( this.mColorList ) );

		return true;
	}


	
	/**
	 * 
	 * @param el
	 * @return
	 */
	public boolean readProperty( final Element el )
	{
		final float rd = SGIConstants.RADIAN_DEGREE_RATIO;

		String str = null;
		Number num = null;
//		Color cl = null;
//		Boolean b = null;
		List list = null;

		// line width
		str = el.getAttribute( KEY_LINE_WIDTH );
		if( str.length()!=0 )
		{
			StringBuffer uLineWidth = new StringBuffer();
			num = SGUtilityText.getNumber( str, uLineWidth );
			if( num==null )
			{
				return false;
			}
			final float lineWidth = num.floatValue();
			if( this.setLineWidth( lineWidth, uLineWidth.toString() ) == false )
			{
				return false;
			}
		}


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


		// head size
		str = el.getAttribute( KEY_HEAD_SIZE );
		if( str.length()!=0 )
		{
			StringBuffer uHeadSize = new StringBuffer();
			num = SGUtilityText.getNumber( str, uHeadSize );
			if( num==null )
			{
				return false;
			}
			final float headSize = num.floatValue();
			if( this.setHeadSize( headSize, uHeadSize.toString() ) == false )
			{
				return false;
			}
		}


		// start head type
		str = el.getAttribute( KEY_START_HEAD_TYPE );
		if( str.length()!=0 )
		{
			num = SGDrawingElementArrow.getSymbolTypeFromName(str);
			if( num==null )
			{
				return false;
			}
			final int startHeadType = num.intValue();
			if( this.setStartHeadType( startHeadType ) == false )
			{
				return false;
			}
		}


		// end head type
		str = el.getAttribute( KEY_END_HEAD_TYPE );
		if( str.length()!=0 )
		{
			num = SGDrawingElementArrow.getSymbolTypeFromName(str);
			if( num==null )
			{
				return false;
			}
			final int endHeadType = num.intValue();
			if( this.setEndHeadType( endHeadType ) == false )
			{
				return false;
			}
		}

		// open angle
		str = el.getAttribute( KEY_HEAD_OPEN_ANGLE );
		if( str.length()!=0 )
		{
			num = SGUtilityText.getFloat(str,SGIConstants.degree);
			if( num==null )
			{
				return false;
			}
			final float openAngle = num.floatValue()*rd;
			if( this.setHeadOpenAngle( openAngle ) == false )
			{
				return false;
			}
		}


		// close angle
		str = el.getAttribute( KEY_HEAD_CLOSE_ANGLE );
		if( str.length()!=0 )
		{
			num = SGUtilityText.getFloat(str,SGIConstants.degree);
			if( num==null )
			{
				return false;
			}
			final float closeAngle = num.floatValue()*rd;
			if( this.setHeadCloseAngle( closeAngle ) == false )
			{
				return false;
			}
		}


		// color
		str = el.getAttribute( KEY_COLOR_LIST );
		if( str.length()!=0 )
		{
			list = SGUtilityText.getColorList(str);
			if( list==null )
			{
				return false;
			}
			if( this.setColorList( list ) == false )
			{
				return false;
			}
		}
		

		return true;
	}

	
	/**
	 * 
	 * @return
	 */
	protected SGDrawingElement createDrawingElementInstance()
	{
		return new SGDrawingElementArrow2D();
	}


	/**
	 * 
	 * @return
	 */
	protected boolean initDrawingElement(
		final SGTuple2f[] startArray, final SGTuple2f[] endArray )
	{
		final int num = startArray.length;
		if( this.initDrawingElement(num) == false )
		{
			return false;
		}
		
		SGDrawingElement[] sArray = this.mDrawingElementArray;
		for( int ii=0; ii<num; ii++ )
		{
			((SGDrawingElementArrow)sArray[ii]).setTermPoints( startArray[ii], endArray[ii] );
		}
		return true;
	}


	/**
	 * 
	 */
	public boolean setLocation(
		final SGTuple2f[] startArray,
		final SGTuple2f[] endArray )
	{

		if( startArray==null || endArray==null )
		{
			throw new IllegalArgumentException("startArray==null || endArray==null");
		}

		if( startArray.length != endArray.length )
		{
			throw new IllegalArgumentException("startArray.length != endArray.length");
		}

		if( startArray.length != this.mDrawingElementArray.length )
		{
			throw new IllegalArgumentException("startArray.length != this.mDrawingElementArray.length");
		}


		SGDrawingElement[] array = this.mDrawingElementArray;
		for( int ii=0; ii<array.length; ii++ )
		{
			final boolean eff = !(
				startArray[ii].isInfinite() || startArray[ii].isNaN() ||
				endArray[ii].isInfinite() || endArray[ii].isNaN() );

			final SGDrawingElementArrow arrow = (SGDrawingElementArrow)array[ii];
			arrow.setVisible( eff );
			if( eff )
			{
				arrow.setTermPoints( startArray[ii], endArray[ii] );
			}
		}

		return true;
	}



	/**
	 * 
	 * @return
	 */
	public boolean setPropertiesOfDrawingElements()
	{
		SGDrawingElement[] array = this.mDrawingElementArray;
		for( int ii=0; ii<array.length; ii++ )
		{
			SGDrawingElementArrow arrow = (SGDrawingElementArrow)array[ii];

			arrow.setMagnification( this.mMagnification );
			arrow.setColorList( this.mColorList );

			arrow.setLineWidth( this.mLineWidth );
			arrow.setLineType( this.mLineType );

			arrow.setHeadSize( this.mHeadSize );
			arrow.setStartHeadType( this.mStartHeadType );
			arrow.setEndHeadType( this.mEndHeadType );
			arrow.setHeadOpenAngle( this.mHeadOpenAngle );
			arrow.setHeadCloseAngle( this.mHeadCloseAngle );
		}
		return true;
	}


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

		return p;
	}


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

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

		ArrowProperties ap = (ArrowProperties)p;
		ap.setLineWidth( this.getLineWidth() );
		ap.setLineType( this.getLineType() );
		ap.setHeadSize( this.getHeadSize() );
		ap.setStartHeadType( this.getStartHeadType() );
		ap.setEndHeadType( this.getEndHeadType() );
		ap.setHeadOpenAngle( this.getHeadOpenAngle() );
		ap.setHeadCloseAngle( this.getHeadCloseAngle() );

		return true;
	}


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

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

		ArrowProperties ap = (ArrowProperties)p;

		final Float lineWidth = ap.getLineWidth();
		if( lineWidth==null )
		{
			return false;
		}
		this.setLineWidth( lineWidth.floatValue() );

		final Integer lineType = ap.getLineType();
		if( lineType==null )
		{
			return false;
		}
		this.setLineType( lineType.intValue() );

		final Float headSize = ap.getHeadSize();
		if( headSize==null )
		{
			return false;
		}
		this.setHeadSize( headSize.floatValue() );

//		final Float headLineWidth = ap.getHeadLineWidth();
//		if( headLineWidth==null )
//		{
//			return false;
//		}
//		this.setHeadLineWidth( headLineWidth.floatValue() );

//		Color headLineColor = ap.getHeadLineColor();
//		if( headLineColor==null )
//		{
//			return false;
//		}
//		this.setHeadLineColor( headLineColor );

		final Integer startHeadType = ap.getStartHeadType();
		if( startHeadType==null )
		{
			return false;
		}
		this.setStartHeadType( startHeadType.intValue() );

		final Integer endHeadType = ap.getEndHeadType();
		if( endHeadType==null )
		{
			return false;
		}
		this.setEndHeadType( endHeadType.intValue() );

		final Float headOpenAngle = ap.getHeadOpenAngle();
		if( headOpenAngle==null )
		{
			return false;
		}
		this.setHeadOpenAngle( headOpenAngle.floatValue() );

		final Float headCloseAngle = ap.getHeadCloseAngle();
		if( headCloseAngle==null )
		{
			return false;
		}
		this.setHeadCloseAngle( headCloseAngle.floatValue() );

		return true;
	}





	/**
	 * @author  okumura
	 */
	public static class ArrowProperties extends ElementGroupProperties
	{

		private SGDrawingElementArrow.ArrowProperties mArrowProperties
			= new SGDrawingElementArrow.ArrowProperties();

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

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

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

			ArrowProperties p = (ArrowProperties)obj;
			if( this.mArrowProperties.equals( p.mArrowProperties ) == false ) return false;

			return true;
		}

		public Float getLineWidth()
		{
			return this.mArrowProperties.getLineWidth();
		}

		public Integer getLineType()
		{
			return this.mArrowProperties.getLineType();
		}

		public Float getHeadLineWidth()
		{
			return this.mArrowProperties.getLineWidth();
		}

		public Float getHeadSize()
		{
			return this.mArrowProperties.getHeadSize();
		}

		public Integer getStartHeadType()
		{
			return this.mArrowProperties.getStartHeadType();
		}

		public Integer getEndHeadType()
		{
			return this.mArrowProperties.getEndHeadType();
		}

		public Float getHeadOpenAngle()
		{
			return this.mArrowProperties.getHeadOpenAngle();
		}

		public Float getHeadCloseAngle()
		{
			return this.mArrowProperties.getHeadCloseAngle();
		}


		public void setLineWidth( final float width )
		{
			this.mArrowProperties.setLineWidth( width );
		}

		public void setLineType( final int num )
		{
			this.mArrowProperties.setLineType( num );
		}

		public void setStartHeadType( final int num )
		{
			this.mArrowProperties.setStartHeadType( num );
		}

		public void setEndHeadType( final int num )
		{
			this.mArrowProperties.setEndHeadType( num );
		}

		public void setHeadSize( final float size )
		{
			this.mArrowProperties.setHeadSize( size );
		}

		public void setHeadOpenAngle( final float value )
		{
			this.mArrowProperties.setHeadOpenAngle( value );
		}

		public void setHeadCloseAngle( final float value )
		{
			this.mArrowProperties.setHeadCloseAngle( value );
		}

		public void setColor( final Color cl )
		{
			super.setColor(cl);
			this.mArrowProperties.setColor(cl);
		}

		public void setColor( final ArrayList cList )
		{
			super.setColor(cList);
			this.mArrowProperties.setColor(cList);
		}

	}



}
