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

import java.util.*;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import jp.riken.brain.ni.samuraigraph.base.*;
import jp.riken.brain.ni.samuraigraph.data.*;
import jp.riken.brain.ni.samuraigraph.figure.*;

/**
 *
 */

public class SGDrawingElementString2D extends SGDrawingElementString
	implements SGIDrawingElementJava2D
{

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


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

		mString = str;
	}


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

		this.mString = element.getString();
		this.mFontFamilyName = element.getFontFamilyName();
		this.mX = element.getX();
		this.mY = element.getY();
		this.mFontSize = element.getFontSize();
		this.mFontStyle = element.getFontStyle();
		this.mAngle = element.getAngle();
		this.mColorList = new ArrayList( element.getColorList() );

	}


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

		mString = str;
		mFontStyle = fontStyle;
		mFontSize = fontSize;
	}
	

	/**
	 *
	 */
	public SGDrawingElementString2D(
		final String str,
		final int fontStyle,
		final float fontSize,
		final float x,
		final float y )
	{
		super();
		
		mString = str;
		mFontStyle = fontStyle;
		mFontSize = fontSize;
		mX = x;
		mY = y;
	}


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

		return flagShape;
	}



	/**
	 * 
	 */
	public Rectangle2D getElementBounds()
	{
		Rectangle2D sRect = this.getStringRect();
		AffineTransform af = this.getAffineTransform();
		Shape sh = af.createTransformedShape( sRect );

		return sh.getBounds2D();
	}



	/**
	 * 
	 */
	public Rectangle2D getStringRect()
	{
//System.out.println("<< SGDrawingElementString2D::getStringRect >>");

		if( this.mString.indexOf("^")==-1 && this.mString.indexOf("_")==-1 )
		{
			return this.getStringRectDirectly();
		}


		// get information of the input line
		String line = this.getString();
		ArrayList charList = new ArrayList();
		ArrayList superList = new ArrayList();
		ArrayList subList = new ArrayList();
		if( SGUtilityText.getSubscriptAndSuperscriptInfo(line,charList,superList,subList) == false )
		{
			return this.getStringRectDirectly();
		}
		if( charList.size()!=superList.size() || superList.size()!=subList.size() )
		{
			return this.getStringRectDirectly();
		}

		// create font objects
		Font font = new Font(
			this.getFontFamilyName(), this.getFontStyle(),
			(int)(this.getFontSize()) );
		Font fontHalf = new Font(
			this.getFontFamilyName(), this.getFontStyle(),
			(int)(this.getFontSize()/2.0f) );


		//
		float y = 0.0f;
		float width = 0.0f;
		float height = 0.0f;
		for( int ii=0; ii<charList.size(); ii++ )
		{
			// characters
			{
				// create a string
				Character c = (Character)charList.get(ii);
				char[] array = new char[1];
				array[0] = c.charValue();
				String str = new String(array);


				// set the font

				// get a bounding box
				Rectangle2D stringRect = font.getStringBounds(
					str, new FontRenderContext( null, false, false ) );
				width += (float)stringRect.getWidth();
				height = (float)stringRect.getHeight();
			}


			// superscript characters
			Object sp = superList.get(ii);
			float widthSuper = 0.0f;
			if( sp!=null )
			{
				String str = (String)sp;

				// get a bounding box
				Rectangle2D stringRect = fontHalf.getStringBounds(
					str, new FontRenderContext( null, false, false ) );
				widthSuper += (float)stringRect.getWidth();
			}


			// subscript characters
			Object sb = subList.get(ii);
			float widthSub = 0.0f;
			if( sb!=null )
			{
				String str = (String)sb;

				// get a bounding box
				Rectangle2D stringRect = fontHalf.getStringBounds(
					str, new FontRenderContext( null, false, false ) );
				widthSub += (float)stringRect.getWidth();
			}


			if( sp!=null || sb!=null )
			{
				width += Math.max(widthSuper,widthSub);
			}

		}

		// create a rectangle
		Rectangle2D rect = new Rectangle2D.Float(
			0.0f, 0.0f,
			this.mMagnification*width, this.mMagnification*height );

		return rect;

	}



	/**
	 * 
	 */
	protected Rectangle2D getStringRectDirectly()
	{

		// create font objects
		Font font = new Font(
			this.getFontFamilyName(), this.getFontStyle(),
			(int)(this.getFontSize()) );

		Rectangle2D strBounds = font.getStringBounds(
			this.getString(), new FontRenderContext( null, false, false ) );

		// create a rectangle
		Rectangle2D rect = new Rectangle2D.Float(
			0.0f, 0.0f,
			this.mMagnification*(float)strBounds.getWidth(),
			this.mMagnification*(float)strBounds.getHeight() );

		return rect;
	}



	/**
	 * 
	 */
	protected float getAscent()
	{
		Font font = new Font(
			mFontFamilyName, this.getFontStyle(),
			(int)(this.mFontSize) );
		LineMetrics lm = font.getLineMetrics(
			this.getString(),
			new FontRenderContext( null, false, false )
		);

		return mMagnification*lm.getAscent();
	}



	/**
	 * この文字列のアフィン変換を取得
	 */
	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 boolean drawString( final Graphics2D g2d )
	{
//System.out.println("<< drawString >>");

		if( g2d==null )
		{
			return false;
		}


		if( this.mString.indexOf("^")==-1 && this.mString.indexOf("_")==-1 )
		{
			return this.drawStringDirectly(g2d);
		}


		// get information of the input line
		String line = this.getString();
		ArrayList charList = new ArrayList();
		ArrayList superList = new ArrayList();
		ArrayList subList = new ArrayList();
		if( SGUtilityText.getSubscriptAndSuperscriptInfo(line,charList,superList,subList) == false )
		{
			return this.drawStringDirectly(g2d);
		}
		if( charList.size()!=superList.size() || superList.size()!=subList.size() )
		{
			return this.drawStringDirectly(g2d);
		}


		// angle
		final double angle = this.getAngle();
		final float cv = (float)Math.cos(-angle);
		final float sv = (float)Math.sin(-angle);

//final double deg = angle/(Math.PI/180.0);
//System.out.println("angle="+deg+"  "+cv+"  "+sv);

		// set the color
//System.out.println("cList:"+this.getString()+"  "+this.getColorList());
		g2d.setPaint(this.getColor(0));


		// create font objects
		Font font = new Font(
			this.getFontFamilyName(), this.getFontStyle(),
			(int)(this.getMagnification()*this.getFontSize()) );
		Font fontHalf = new Font(
			this.getFontFamilyName(), this.getFontStyle(),
			(int)(this.getMagnification()*this.getFontSize()/2.0f) );


		// draw strings
		float x = this.getX();
		float y = this.getY();
		for( int ii=0; ii<charList.size(); ii++ )
		{
			// characters
			{
				// create a string
				Character c = (Character)charList.get(ii);
				char[] array = new char[1];
				array[0] = c.charValue();
				String str = new String(array);


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


				// get a bounding box
				Rectangle2D stringRect = font.getStringBounds(
					str, new FontRenderContext( null, false, false ) );


				// create an affine transformation matrix
				AffineTransform af = new AffineTransform();
				af.translate( x, y );
				af.rotate( - angle );


				// transform
				AffineTransform saveAT = g2d.getTransform();
				g2d.transform(af);
				g2d.drawString(
					str, 0.0f, (float)(this.getAscent())
				);
				g2d.setTransform(saveAT);


				// update the location
				x += (cv*(float)stringRect.getWidth());
				y += (sv*(float)stringRect.getWidth());
			}


			// superscript characters
			Object sp = superList.get(ii);
			float xSuper = 0.0f;
			float ySuper = 0.0f;
			if( sp!=null )
			{
				String str = (String)sp;


				// set the font
				g2d.setFont( fontHalf );


				// get a bounding box
				Rectangle2D stringRect = fontHalf.getStringBounds(
					str, new FontRenderContext( null, false, false ) );


				// create an affine transformation matrix
				AffineTransform af = new AffineTransform();
				af.translate( x, y );
				af.rotate( - angle );


				// transform
				AffineTransform saveAT = g2d.getTransform();
				g2d.transform(af);
				g2d.drawString(
					str, 0.0f, (float)(this.getAscent()/2.0f)
				);
				g2d.setTransform(saveAT);


				// update the loction
				xSuper = (cv*(float)stringRect.getWidth());
				ySuper = (sv*(float)stringRect.getWidth());
			}


			// subscript characters
			Object sb = subList.get(ii);
			float xSub = 0.0f;
			float ySub = 0.0f;
			if( sb!=null )
			{
				String str = (String)sb;


				// set the font
				g2d.setFont( fontHalf );


				// get a bounding box
				Rectangle2D stringRect = fontHalf.getStringBounds(
					str, new FontRenderContext( null, false, false ) );


				// create an affine transformation matrix
				AffineTransform af = new AffineTransform();
				af.translate( x, y );
				af.rotate( - angle );


				// transform
				AffineTransform saveAT = g2d.getTransform();
				g2d.transform(af);
				g2d.drawString(
					str, 0.0f, (float)(this.getAscent())
				);
				g2d.setTransform(saveAT);


				// update the location
				xSub = (cv*(float)stringRect.getWidth());
				ySub = (sv*(float)stringRect.getWidth());
			}

			if( sp!=null || sb!=null )
			{

				float xx;
				float yy;
				if( Math.abs(xSuper) > Math.abs(xSub) )
				{
					xx = xSuper;
				}
				else
				{
					xx = xSub;
				}
				if( Math.abs(ySuper) > Math.abs(ySub) )
				{
					yy = ySuper;
				}
				else
				{
					yy = ySub;
				}

				x += xx;
				y += yy;

//System.out.println("x: "+xSuper+"  "+xSub);
//System.out.println("y: "+ySuper+"  "+ySub);

			}

		}

//System.out.println();

		return true;

	}


	/**
	 * 
	 */
	protected boolean drawStringDirectly( final Graphics2D g2d )
	{

		String str = this.getString();

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


		// create font objects
		Font font = new Font(
			this.getFontFamilyName(), this.getFontStyle(),
			(int)(this.getMagnification()*this.getFontSize()) );


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


		// get a bounding box
		Rectangle2D stringRect = font.getStringBounds(
			str, new FontRenderContext( null, false, false ) );


		// create an affine transformation matrix
		final float x = this.getX();
		final float y = this.getY();
		AffineTransform af = new AffineTransform();
		af.translate( x, y );
		af.rotate( - this.getAngle() );

		// transform
		AffineTransform saveAT = g2d.getTransform();
		g2d.transform(af);
		g2d.drawString(
			str, 0.0f, (float)(this.getAscent())
		);
		g2d.setTransform(saveAT);


		return true;
	}


}
