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) {
		final float dx = endX - startX;
		final float dy = endY - startY;

		float angle;
		if (Math.abs(dx) < Float.MIN_VALUE) {
			if (dy > 0.0f) {
				angle = 0.50f * (float) Math.PI;
			} else {
				angle = 1.50f * (float) Math.PI;
			}
		} else if (Math.abs(dy) < Float.MIN_VALUE) {
			if (dx > 0.0f) {
				angle = 0.0f;
			} else {
				angle = (float) Math.PI;
			}
		} else {
			final float tangent = dy / dx;
			if (dx > 0.0f) {
				if (dy > 0.0f) {
					angle = (float) Math.atan(tangent);
				} else {
					angle = (float) (2.0 * Math.PI + Math.atan(tangent));
				}
			} else {
				angle = (float) (Math.PI + Math.atan(tangent));
			}
		}

		return angle;
	}

	//
	// public static void main( String[] args )
	// {
	// final float[] xArray = { 1, -1, -1, 1 };
	// final float[] yArray = { 1, 1, -1, -1 };
	// for( int ii=0; ii<xArray.length; ii++ )
	// {
	// final float _angle = getAngle( 0, 0, xArray[ii], yArray[ii] );
	// final float _degree = _angle/(float)Math.PI;
	// System.out.println(ii+" "+_degree);
	// }
	// System.out.println();
	// }

}
