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

import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;

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


/**
 * A class of drawing elements with a text.
 * Java2D API is used.
 */
public class SGDrawingElementString2D extends SGDrawingElementString
	implements SGIDrawingElementJava2D
{


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


	/**
	 * 
	 */
	public SGDrawingElementString2D( final String str )
	{
		super( str );
	}


	/**
	 * 
	 */
	public SGDrawingElementString2D( final SGDrawingElementString element )
	{
		super( element );
	}


	/**
	 *
	 */
	public SGDrawingElementString2D(
		final String str,
		final String fontName,
		final int fontStyle,
		final float fontSize )
	{
		super( str, fontName, fontStyle, fontSize );
	}
	

	/**
	 *
	 */
	public SGDrawingElementString2D(
		final String str,
		final String fontName,
		final int fontStyle,
		final float fontSize,
		final float x,
		final float y )
	{
		super( str, fontName, fontStyle, fontSize, x, y );
	}


	/**
	 * 
	 */
	public boolean setMagnification( final float mag )
	{
		super.setMagnification(mag);
		this.updateStringRect();
		return true;
	}


	/**
	 * 
	 */
	public boolean setFont( final String name, final int style, final float size )
	{
		super.setFont( name, style, size );
		this.updateStringRect();
		return true;
	}


	/**
	 * 
	 */
	public boolean setString( final String str )
	{
		super.setString( str );
		this.updateStringRect();
		return true;
	}


	/**
	 *
	 */
	public boolean contains( final int x, final int y )
	{
		Shape shape = this.getElementBounds();
		return shape.contains(x,y);
	}



	/**
	 * Returns the bounding box with the given magnification,
	 * location, font and angle properties.
	 */
	public Rectangle2D getElementBounds()
	{
		Rectangle2D sRect = this.getStringRect();
		AffineTransform af = this.getAffineTransform();
		Shape sh = af.createTransformedShape( sRect );
		return sh.getBounds2D();
	}




	/**
	 * Bounding box of this string element on zero angle
	 * and given magnification and font properties.
	 */
	private Rectangle2D mStringRect;


	/**
	 * Update the attribute of bounding box.
	 * @return
	 */
	protected boolean updateStringRect()
	{
		if( this.mFont==null | this.mString==null )
		{
			return false;
		}

		String str = this.mString;
		if( str.length() == 0 )
		{
			this.mStringRect = new Rectangle2D.Float();
			return true;
		}

		// get a rectangle from the Font object
		FontRenderContext frc = new FontRenderContext( null, false, false );
		Rectangle2D rect = this.mFont.getStringBounds( str, frc );
		rect.setRect( 0.0, 0.0, rect.getWidth(), rect.getHeight() );

		// set to the attribute
		this.mStringRect = rect;

		return true;
	}


	
	/**
	 * 
	 * @param str
	 * @return
	 */
	protected Rectangle2D getVisualBounds()
	{
		FontRenderContext frc = new FontRenderContext( null, false, false );
		Rectangle2D rect = this.mFont.createGlyphVector(frc,this.mString).getVisualBounds();
		return rect;
	}


	/**
	 * Returns the bounding box with the given magnification and font properties.
	 * The angle does not affect the returned value.
	 * The x- and y-coordinate of this rectangle always equals to zero.
	 */
	public Rectangle2D getStringRect()
	{
		return (Rectangle2D)this.mStringRect.clone();
	}


	/**
	 * 
	 */
	protected float getAscent()
	{
		LineMetrics lm = this.mFont.getLineMetrics(
			this.getString(),
			new FontRenderContext( null, false, false )
		);
		return lm.getAscent();
	}



	/**
	 * 
	 */
	protected float getDescent()
	{
		LineMetrics lm = this.mFont.getLineMetrics(
			this.getString(),
			new FontRenderContext( null, false, false )
		);
		return lm.getDescent();
	}



	/**
	 * この文字列のアフィン変換を取得
	 */
	protected AffineTransform getAffineTransform()
	{
		AffineTransform af = new AffineTransform();

		final double x = this.getX();
		final double y = this.getY();

		// 平行移動
		af.translate( x, y );

		// 回転
		af.rotate( - this.getAngle() );
		
		return af;		
	}



	/**
	 * 
	 */
	public void paintElement( final Graphics2D g2d )
	{
		String str = this.getString();

		g2d.setPaint(this.getColor(0));

		// set the font
		g2d.setFont( this.mFont );

		// if the string element is horizontal
		if( Math.abs(this.getAngle()) < Float.MIN_VALUE )
		{
			this.drawStringHorizontally( str, g2d );
			return;
		}


		// create an affine transformation matrix
		AffineTransform af = this.getAffineTransform();

		// transform
		AffineTransform saveAT = g2d.getTransform();
		g2d.transform(af);

		Rectangle2D rect = this.getVisualBounds();

		g2d.drawString(
			str,
0.0f, this.getAscent()
//			- (float)rect.getX(),
//			(float)rect.getHeight()
		);
		g2d.setTransform(saveAT);

	}



	/**
	 * 
	 * @return
	 */
	protected boolean drawStringHorizontally(
		final String str, final Graphics2D g2d )
	{
		Rectangle2D rect = this.getVisualBounds();

		g2d.drawString(
			str,
this.getX(), this.getY() + this.getAscent()
//			this.getX() - (float)rect.getX(),
//			this.getY() + (float)rect.getHeight()
		);
		return true;
	}



	/**
	 * 
	 */
	public void paint( final Graphics2D g2d, final Rectangle2D clipRect )
	{
		this.paintElement(g2d);
	}



	/**
	 * 
	 * @param args
	 */
	public static void main( String[] args )
	{
		SGDrawingElementString2D el = new SGDrawingElementString2D(
			"ABCDEFG",
			"Serif",
			Font.PLAIN,
			15.0f, 0.0f, 0.0f
		);

		System.out.println(el.getStringRect());

		el.setMagnification(1.0f);
		System.out.println(el.getStringRect());
		el.setMagnification(2.0f);
		System.out.println(el.getStringRect());
		el.setMagnification(4.0f);
		System.out.println(el.getStringRect());
		el.setMagnification(2.0f);
		System.out.println(el.getStringRect());
	}


}

