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

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;

import jp.riken.brain.ni.samuraigraph.base.SGUtility;
import jp.riken.brain.ni.samuraigraph.figure.SGILineConstants;


/**
 * A class with static methods for Java2D.
 *
 */
public class SGUtilityJava2D implements SGILineConstants
{


	/**
	 * 
	 * @param list
	 * @return
	 */
	public static Rectangle2D getBoundingBox(
		final ArrayList list )
	{
		ArrayList rectList = new ArrayList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			Object obj = list.get(ii);
			if( obj==null )
			{
				throw new NullPointerException("obj==null");
			}
			if( !(obj instanceof SGIDrawingElementJava2D) )
			{
				throw new IllegalArgumentException(
					"!(obj instanceof SGIDrawingElementJava2D)"
				);
			}

			SGIDrawingElementJava2D el
				= (SGIDrawingElementJava2D)obj;
			rectList.add( el.getElementBounds() );
		}

		Rectangle2D rectAll = SGUtility.createUnion( rectList );

		return rectAll;
	}



	/**
	 * 
	 * @param itr
	 * @return
	 */
	public static ArrayList getSegmentList( final PathIterator itr )
	{
		ArrayList list = new ArrayList();
		while( !itr.isDone() )
		{
			final float[] array = new float[6];
			final int type = itr.currentSegment( array );

			if( type==PathIterator.SEG_MOVETO || type==PathIterator.SEG_LINETO )
			{
				list.add( new Point2D.Float( array[0], array[1] ) );
			}
			else if( type==PathIterator.SEG_QUADTO )
			{
				list.add( new Point2D.Float( array[0], array[1] ) );
				list.add( new Point2D.Float( array[2], array[3] ) );
			}
			else if( type==PathIterator.SEG_CUBICTO )
			{
				list.add( new Point2D.Float( array[0], array[1] ) );
				list.add( new Point2D.Float( array[2], array[3] ) );
				list.add( new Point2D.Float( array[4], array[5] ) );
			}

			itr.next();
		}

		return list;
	}



	/**
	 * Draw a circle.
	 * @param pos	location
	 * @param size size of circle
	 * @param lineWidth width of line
	 * @param lineColor color of line
	 * @param color color of body
	 * @param g2d	graphics context
	 */
	public static void drawCircle(
		final Point2D pos,
		final float size,
		final float lineWidth,
		final Color lineColor,
		final Color color,
		final Graphics2D g2d )
	{
		final float x = (float)pos.getX();
		final float y = (float)pos.getY();
		Shape circle = new Ellipse2D.Float(
			x-0.5f*size, y-0.5f*size, size, size
		);
		drawSymbol( circle, lineWidth, lineColor, color, g2d );
	}
		


	/**
	 * Draw a square.
	 * @param pos location
	 * @param size size of square
	 * @param lineWidth width of line
	 * @param lineColor color of line
	 * @param color color of body
	 * @param g2d	graphics context
	 */
	public static void drawSquare(
		final Point2D pos,
		final float size,
		final float lineWidth,
		final Color lineColor,
		final Color color,
		final Graphics2D g2d )
	{
		final float x = (float)pos.getX();
		final float y = (float)pos.getY();
		Shape circle = new Rectangle2D.Float(
			x-0.5f*size, y-0.5f*size, size, size
		);
		drawSymbol( circle, lineWidth, lineColor, color, g2d );
	}



	/**
	 * Draw a symbol.
	 * @param sh
	 * @param lineWidth
	 * @param lineColor
	 * @param color
	 * @param g2d
	 */
	public static void drawSymbol(
		final Shape sh,
		final float lineWidth,
		final Color lineColor,
		final Color color,
		final Graphics2D g2d )
	{
		g2d.setPaint( color );
		g2d.fill( sh );
		g2d.setPaint( lineColor );
		g2d.setStroke( new BasicStroke( lineWidth ) );
		g2d.draw( sh );
	}



	/**
	 * Returns a new Stroke object.
	 * @param type - the line type of a BasicStroke
	 * @param width - the width of a BasicStroke
	 * @param cap - the decoration of the ends of a BasicStroke
	 * @param join - the decoration applied where path segments meet
	 * @param miterLimit - the limit to trim the miter join. The miterlimit must be greater than or equal to 1.0f.
	 * @param dashPhase - the offset to start the dashing pattern
	 * @return a BasicStroke object
	 */
	public static BasicStroke getBasicStroke(
		final int type,
		final float width,
		final int cap,
		final int join,
		final float miterLimit,
		final float dashPhase )
	{
		BasicStroke bs = null;
		switch( type )
		{
			// solid line
			case LINE_TYPE_SOLID :
			{
				bs = new BasicStroke( width, cap, join );
				break;
			}
			
			// broken line
			case LINE_TYPE_BROKEN :
			{
				final float[] dash = {2.0f*width,width};
				bs = new BasicStroke(
					width, cap, join, miterLimit, dash, dashPhase );
				break;
			}

			// dotted line
			case LINE_TYPE_DOT :
			{
				final float[] dash = {width};
				bs = new BasicStroke(
					width, cap, join, miterLimit, dash, dashPhase );
				break;
			}

			// dashed line
			case LINE_TYPE_DASHED :
			{
				final float[] dash = {4.0f*width,width,width,width};
				bs = new BasicStroke(
					width, cap, join, miterLimit, dash, dashPhase );
				break;
			}

			// dashed line
			case LINE_TYPE_DOUBLE_DASHED :
			{
				final float[] dash = {4.0f*width,width,width,width,width,width};
				bs = new BasicStroke(
					width, cap, join, miterLimit, dash, dashPhase );
				break;
			}

			default :
			{
				throw new IllegalArgumentException("Line type is invalid.");
			}
		}

		return bs;
	}


	/**
	 * 
	 * @return
	 */
	public static float getAngle(
		final float startX, final float startY,
		final float endX, final float endY )
	{

		float angle;
		if( Math.abs( endX - startX ) < Float.MIN_VALUE )
		{
			if( endY > startY )
			{
				angle = 0.50f*(float)Math.PI;
			}
			else
			{
				angle = 1.50f*(float)Math.PI;
			}
		}
		else if( Math.abs( endY - startY ) < Float.MIN_VALUE )
		{
			if( endX > startX )
			{
				angle = 0.0f;
			}
			else
			{
				angle = (float)Math.PI;
			}
		}
		else
		{
			final float tangent = (endY-startY)/(endX-startX);
			angle = (float)Math.atan(tangent);
		}

		return angle;
	}


}


