
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.base.SGTuple2f;
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.getShape();
		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.getShape();
		if( sh==null )
		{
			return new Rectangle2D.Float();
		}
		return sh.getBounds2D();
	}



	/**
	 * 
	 */
	public Shape getShape()
	{
		final float x = this.getX();
		final float y = this.getY();
		final float size = mMagnification*mSize;
		final float half = 0.50f*size;
		final int type = this.mType;

		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;
				}
				default :
				{
					throw new Error("symbol-type is not defined");
				}

			}
		}

		return sh;
	}



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


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

		// 回転
		af.rotate(mAngle);

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

		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;
	}



	/**
	 * 
	 * @param g2d
	 * @param clipRect
	 */
	void paint( Graphics2D g2d, Rectangle2D clipRect )
	{
		Shape shape = this.getShape();
		if( shape==null )
		{
			return;
		}

		Stroke stroke = new BasicStroke(
			mMagnification*this.mLineWidth,
			BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER );


		if( clipRect==null )
		{
			g2d.setPaint( this.getColor(0) );
			g2d.fill(shape);

			g2d.setStroke( stroke );
			g2d.setPaint( this.getLineColor() );
			g2d.draw(shape);
		}
		else
		{
			Area clipArea = new Area( clipRect );

			Shape edge = stroke.createStrokedShape( shape );
			Area sh = new Area( edge );
			sh.intersect( clipArea );
					
			Area inner = new Area( shape );
			inner.intersect( clipArea );					
			g2d.setPaint( this.getColor(0) );
			g2d.fill(inner);

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

	}

}

