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

import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.font.TextLayout;
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 {
	
	/**
	 * Text Layout information - immutable graphical representation of styled 
	 * character data.
	 */
	private TextLayout mTextLayout = null;

	/**
	 * Visual bounding box of this string element on zero angle and given
	 * magnification and font properties.
	 */
	private Rectangle2D mStringVisualRect = null;

	/**
	 * Line metrincs of this string element on zero angle and given
	 * magnification and font properties.
	 */
	private LineMetrics mStringMetrics = null;
	
	/**
	 * Font Render Context for the drawing string.
	 */
	private FontRenderContext mFontRenderContext = null;
	
	/**
	 *  
	 */
	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 void dispose() {
		super.dispose();
		this.mTextLayout = null;
		this.mStringVisualRect = null;
		this.mStringMetrics = null;
		this.mFontRenderContext = null;
	}

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

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

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

	/**
	 *  
	 */
	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() {
		AffineTransform af = this.getAffineTransform();
		Rectangle2D sRect = this.getStringRect();
		sRect.setRect(0, 0, this.getAdvance(), sRect.getHeight());
		Shape sh = af.createTransformedShape(sRect);
		return sh.getBounds2D();
	}


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

		if (this.mString.length() == 0) {
			this.mTextLayout = null;
			this.mStringVisualRect = new Rectangle2D.Float();
			this.mStringMetrics = null;
			this.mFontRenderContext = null;
			return true;
		}

		// create font recder context
		this.mFontRenderContext = new FontRenderContext(null, false, false);

		// create text layout
		this.mTextLayout = new TextLayout(this.mString, this.mFont, this.mFontRenderContext);

		// get a visual bounds rectangle from Font object
		this.mStringVisualRect = this.mTextLayout.getBounds();
		if( this.mStringVisualRect.isEmpty() )
			this.mStringVisualRect.setRect(0,0,0,0);
		
		// get a line metrics from the Font object
		this.mStringMetrics = this.mFont.getLineMetrics(this.mString, this.mFontRenderContext);

		return true;
	}

	/**
	 * 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.mStringVisualRect.clone();
	}

	/**
	 *  
	 */
	protected float getAscent() {
		if (mStringMetrics == null)
			return 0.0f;
		return mStringMetrics.getAscent();
	}

	/**
	 * 
	 * @return
	 */
	protected float getStrikethroughOffset() {
		if (mStringMetrics == null)
			return 0.0f;
		return mStringMetrics.getStrikethroughOffset();
	}

	/**
	 *  
	 */
	protected float getDescent() {
		if (mStringMetrics == null)
			return 0.0f;
		return mStringMetrics.getDescent();
	}

	/**
	 *  
	 */
	protected float getAdvance() {
		if (mTextLayout == null)
			return 0.0f;
		return mTextLayout.getAdvance();
	}


	
	/**
	 *  
	 */
	protected float getLeading() {
		if (mStringMetrics == null)
			return 0.0f;
		return mStringMetrics.getLeading();
	}
	
	
	/**
	 * get the affine transform with angle
	 */
	protected AffineTransform getAffineTransform() {
		AffineTransform af = new AffineTransform();

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

		// shift position
		af.translate(x, y);

		// rotate position
		af.rotate(-this.getAngle());

		return af;
	}

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

		String str = this.getString();

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

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

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

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

		g2d.drawString(str, 0, -(float)this.mStringVisualRect.getY());
//		g2d.drawString(str, -(float) mStringVisualRect.getX(),
//				-(float) mStringVisualRect.getY());
		g2d.setTransform(saveAT);

	}

	/**
	 *  
	 */
	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());
//	}

}
