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

import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;

import jp.riken.brain.ni.samuraigraph.figure.SGDrawingElementSymbol;


/**
 *
 */
public class SGDrawingElementSymbol2D extends SGDrawingElementSymbol
	implements SGIDrawingElementJava2D
{

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


	/**
	 * Minimum size of symbol.
	 */
	public static final float MIN_SIZE = 6.0f;


	/**
	 *
	 */
	public boolean contains( final int x, final int y )
	{
		Shape sh = this.getSymbolShape();
		if( sh==null )
		{
			return false;
		}

		Rectangle2D rect = sh.getBounds2D();
		final float min = MIN_SIZE;
		
		// if the symbol is too small, enlarge the rectangle
		if( rect.getWidth() < min | rect.getHeight() < min )
		{
			final float centerX = (float)rect.getCenterX();
			final float centerY = (float)rect.getCenterY();
			rect = new Rectangle2D.Float(
				centerX-min/2.0f, centerY-min/2.0f, min, min );
		}

		return rect.contains(x,y);
	}



	/**
	 * 
	 */
	public Rectangle2D getElementBounds()
	{
		Shape sh = this.getSymbolShape();
		if( sh==null )
		{
			return new Rectangle2D.Float(
				this.getX(), this.getY(), 0.0f, 0.0f );
		}
		return sh.getBounds2D();
	}



	/**
	 * 
	 */
	public Shape getSymbolShape()
	{
		final float x = this.getX();
		final float y = this.getY();
		final float size = this.getMagnification() * this.getSize();
		final float half = 0.50f * size;
		final int type = this.getType();

		Shape sh = null;
		{
			switch( type )
			{
				case SYMBOL_TYPE_CIRCLE :
				{
					sh = new Ellipse2D.Float(
						x-half, y-half, size, size );
					break;
				}
				case SYMBOL_TYPE_SQUARE :
				{
					sh = new Rectangle2D.Float(
						x-half, y-half, size, size );
					break;
				}
				case SYMBOL_TYPE_DIAMOND :
				{
					final float diff = size/(float)Math.sqrt(2.0);
					final float xm = x - diff;
					final float xp = x + diff;
					final float ym = y - diff;
					final float yp = y + diff;

					Line2D line12 = new Line2D.Float( x, yp, xp, y );
					Line2D line23 = new Line2D.Float( xp, y, x, ym );
					Line2D line34 = new Line2D.Float( x, ym, xm, y );
					Line2D line41 = new Line2D.Float( xm, y, x, yp );

					GeneralPath gp = new GeneralPath();
					gp.append( line12, true );
					gp.append( line23, true );
					gp.append( line34, true );
					gp.append( line41, true );
					gp.append( line12, true );
					sh = gp;

					break;
				}
				case SYMBOL_TYPE_TRIANGLE :
				{
					final float div = size/(float)Math.sqrt(3.0f);
					final float p1x = x;
					final float p1y = y-div;
					final float p2x = x+half;
					final float p2y = y+0.50f*div;
					final float p3x = x-half;
					final float p3y = p2y;
	
					Line2D line12 = new Line2D.Float( p1x, p1y, p2x, p2y );
					Line2D line23 = new Line2D.Float( p2x, p2y, p3x, p3y );
					Line2D line31 = new Line2D.Float( p3x, p3y, p1x, p1y );
	
					GeneralPath gp = new GeneralPath();
					gp.append( line12, true );
					gp.append( line23, true );
					gp.append( line31, true );
					gp.append( line12, true );
					sh = gp;

					break;
				}
				case SYMBOL_TYPE_INVERTEDTRIANGLE :
				{
					final float div = - size/(float)Math.sqrt(3.0f);
					final float p1x = x;
					final float p1y = y-div;
					final float p2x = x+half;
					final float p2y = y+0.50f*div;
					final float p3x = x-half;
					final float p3y = p2y;
	
					Line2D line12 = new Line2D.Float( p1x, p1y, p2x, p2y );
					Line2D line23 = new Line2D.Float( p2x, p2y, p3x, p3y );
					Line2D line31 = new Line2D.Float( p3x, p3y, p1x, p1y );
	
					GeneralPath gp = new GeneralPath();
					gp.append( line12, true );
					gp.append( line23, true );
					gp.append( line31, true );
					gp.append( line12, true );
					sh = gp;

					break;
				}
				case SYMBOL_TYPE_CROSS :
				{
					final float xm = x - half;
					final float xp = x + half;
					final float ym = y - half;
					final float yp = y + half;
	
					Line2D line1 = new Line2D.Float( xm, ym, xp, yp );
					Line2D line2 = new Line2D.Float( xp, ym, xm, yp );
	
					GeneralPath gp = new GeneralPath();
					gp.append( line1, false );
					gp.append( line2, false );
					sh = gp;

					break;
				}
				case SYMBOL_TYPE_PLUS :
				{
					Line2D line1 = new Line2D.Float(
						x-half, y, x+half, y );
					Line2D line2 = new Line2D.Float(
						x, y-half, x, y+half );
	
					GeneralPath gp = new GeneralPath();
					gp.append( line1, false );
					gp.append( line2, false );
					sh = gp;

					break;
				}
				case SYMBOL_TYPE_TRANSVERSELINE :
				{
					sh = new Line2D.Float(
						x-half, y, x+half, y );

					break;
				}
				case SYMBOL_TYPE_VOID :
				{
					break;
				}
			}
		}


		// rotate
		if( Math.abs( this.getAngle() ) > Float.MIN_VALUE )
		{
			if( sh!=null )
			{
				AffineTransform af = this.getAffineTransform();
				Shape shape = af.createTransformedShape( sh );
				sh = shape;
			}
		}

		return sh;
	}



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


		// 平行移動
		af.translate( this.getX(), this.getY() );

		// 回転
		af.rotate( this.getAngle() );

		// 平行移動
		af.translate( -this.getX(), -this.getY() );

//System.out.println(this.getAngle()/Math.PI);

		return af;		
	}


//	/**
//	 * 
//	 */
//	public Object copy()
//	{
//		SGDrawingElementSymbol2D el = new SGDrawingElementSymbol2D();
//		return el;
//	}
//
//
//	public boolean setPropertiesForCopy( SGDrawingElementSymbol2D el )
//	{
//		el.setProperties( this.getProperties() );
//		SGTuple2f pos = this.getLocation();
//		el.setLocation( pos.x, pos.y );
//		el.setMagnification( this.mMagnification );
//		return true;
//	}



	/**
	 * 
	 */
	public void paintElement( Graphics2D g2d )
	{
		if( this.isVisible() == false )
		{
			return;
		}

		Shape shape = this.getSymbolShape();
		if( shape==null )
		{
			return;
		}

		// paint the inner of symbol
		this.paintInner( g2d, shape );

		// paint the line
		this.paintLine( g2d, shape );
	}


	/**
	 * 
	 * @param g2d
	 * @param shape
	 */
	protected void paintInner( Graphics2D g2d, Shape shape )
	{
		g2d.setPaint( this.getColor(0) );
		g2d.fill(shape);
	}


	/**
	 * 
	 * @param g2d
	 * @param shape
	 */
	protected void paintLine( Graphics2D g2d, Shape shape )
	{
		Stroke stroke = new BasicStroke( 
				this.getMagnification()*this.getLineWidth(),
				BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER );
		g2d.setStroke( stroke );
		g2d.setPaint( this.getLineColor() );
		g2d.draw(shape);
	}


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

			Shape shape = this.getSymbolShape();
			if( shape==null )
			{
				return;
			}

			// Area to be clipped
			Area clipArea = new Area( clipRect );

			// paint the inner of symbol
			this.paintInner( g2d, clipArea, shape );

			// paint the line
			this.paintLine( g2d, clipArea, shape );
		}

	}


	/**
	 * 
	 * @param g2d
	 * @param clipArea
	 * @param shape
	 */
	protected void paintInner( Graphics2D g2d, Area clipArea, Shape shape )
	{
		Area inner = new Area( shape );
		inner.intersect( clipArea );					
		g2d.setPaint( this.getColor(0) );
		g2d.fill(inner);
	}


	/**
	 * 
	 * @param g2d
	 * @param clipArea
	 * @param shape
	 */
	protected void paintLine( Graphics2D g2d, Area clipArea, Shape shape )
	{
		Stroke stroke = new BasicStroke(
			this.getMagnification()*this.getLineWidth(),
			BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER );

		Shape edge = stroke.createStrokedShape( shape );
		Area sh = new Area( edge );
		sh.intersect( clipArea );

		g2d.setPaint( this.getLineColor() );
		g2d.fill(sh);
	}




}


