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

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;

import jp.riken.brain.ni.samuraigraph.base.SGDrawingElement;
import jp.riken.brain.ni.samuraigraph.base.SGIConstants;
import jp.riken.brain.ni.samuraigraph.base.SGProperties;
import jp.riken.brain.ni.samuraigraph.base.SGTuple2f;
import jp.riken.brain.ni.samuraigraph.base.SGUtilityNumber;
import jp.riken.brain.ni.samuraigraph.base.SGUtilityText;
import jp.riken.brain.ni.samuraigraph.figure.SGDrawingElementBar;
import jp.riken.brain.ni.samuraigraph.figure.SGIBarConstants;

import org.w3c.dom.Element;


/**
 *
 */
public abstract class SGElementGroupBar extends SGElementGroupRectangle
	implements SGIBarConstants, SGIElementGroupConstants
{


	/**
	 * 
	 */
	protected double mBaselineValue;



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



	/**
	 * 
	 */
	public double getBaselineValue()
	{
		return this.mBaselineValue;
	}


	/**
	 * 
	 */
	public boolean setBaselineValue( final double value )
	{
		this.mBaselineValue = value;
		return true;
	}




	/**
	 * 
	 */
	public boolean setProperty( final SGDrawingElement element )
	{
		if( !(element instanceof SGDrawingElementBar) )
		{
			return false;
		}

		if( super.setProperty( element ) == false )
		{
			return false;
		}

		SGDrawingElementBar bar = (SGDrawingElementBar)element;
		this.setBaselineValue( bar.getBaselineValue() );

		return true;
	}



	/**
	 * 
	 */
	public boolean paintElement(
		final Graphics2D g2d, final Rectangle2D clipRect )
	{
		SGDrawingElement[] array = this.mDrawingElementArray;
		if( array!=null )
		{
			final float width = mMagnification*this.mEdgeLineWidth;
			ArrayList cList = this.mColorList;

			for( int ii=0; ii<array.length; ii++ )
			{

				SGDrawingElementBar2D el
					= (SGDrawingElementBar2D)array[ii];

				if( el.isVisible() == false )
				{
					continue;
				}


				final float x = el.getX();
				final float y = el.getY();
				final float w = el.getRectangleWidth();
				final float h = el.getRectangleHeight();

				Rectangle2D dRect = new Rectangle2D.Float( x, y, w, h );

				Stroke stroke = new BasicStroke(
					width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER );


				// fill the rectangles
				final int cNum = cList.size();
				if( cNum > 1 )
				{
					//
					// 複数の色を持つ場合には、GradientPaintを用いる
					//

					// X座標値
					final float gX = x + 0.50f*w;

					// 高さを色数で割った値
					final float diff = h/cNum;

					// グラデーションを付ける幅
//					final float delta = diff/10.0f;
final float delta = 5.0f;

					// rectangleを分割する（色数+1）
					Rectangle2D[] rectArray = new Rectangle2D[cNum+1];
					rectArray[0] = new Rectangle2D.Float( x, y, w, 0.50f*diff );	// 上端
					rectArray[rectArray.length-1]
						= new Rectangle2D.Float(
							x, y + h - 0.50f*diff, w, 0.50f*diff );		// 下端
					for( int jj=1; jj<rectArray.length-1; jj++ )
					{
						final float yy = y+ 0.50f*diff + (jj-1)*diff;
						rectArray[jj] = new Rectangle2D.Float(
							x, yy, w, diff );
					}


					// 区切り点のY座標値の配列（色数-1）
					float[] gYArray = new float[cNum-1];
					for( int jj=0; jj<gYArray.length; jj++ )
					{
						gYArray[jj] = y + (jj+1)*diff;
					}


					// 分割した矩形を塗りつぶす
					g2d.setPaint( (Color)cList.get(0) );
					g2d.fill(rectArray[0]);

					for( int jj=0; jj<gYArray.length; jj++ )
					{
						Color c1 = (Color)this.mColorList.get(jj);
						Color c2 = (Color)this.mColorList.get(jj+1);
						final float y1 = gYArray[jj] - delta;
						final float y2 = gYArray[jj] + delta;
						GradientPaint gPaint = new GradientPaint(
							gX, y1, c1, gX, y2, c2 );
						g2d.setPaint( gPaint );
						g2d.fill(rectArray[jj+1]);
					}

					g2d.setPaint( (Color)this.getColorList().get(cNum-1) );
					g2d.fill(rectArray[rectArray.length-1]);

				}
				else
				{

					if( clipRect==null )
					{
						// paint
						g2d.setPaint( this.getColor(0) );
						g2d.fill(dRect);

						// draw edge lines
						g2d.setStroke(stroke);
						g2d.setPaint(this.mEdgeLineColor);
						g2d.draw(dRect);

					}
					else
					{
						Area clipArea = new Area( clipRect );
						Shape edge = stroke.createStrokedShape( dRect );
						Area edgeArea = new Area( edge );
						Area inner = new Area( dRect );
						
						edgeArea.intersect( clipArea );
						inner.intersect( clipArea );

						// paint
						g2d.setPaint( this.getColor(0) );
						g2d.fill( inner );

						// draw edge lines
						g2d.setStroke(stroke);
						g2d.setPaint(this.mEdgeLineColor);
						g2d.fill( edgeArea );
					}

				}

			}

//System.out.println();

		}

		return true;
	}



	/**
	 * 
	 */
	public SGDrawingElement getDrawingElement()
	{
		SGDrawingElementBar rect = new SGDrawingElementBar2D();
		rect.setVisible( this.mVisibleFlag );
		rect.setWidth( this.mRectangleWidth );
		rect.setHeight( this.mRectangleHeight );
		rect.setEdgeLineWidth( this.mEdgeLineWidth );
		rect.setEdgeLineColor( this.mEdgeLineColor );
		rect.setColorList( this.mColorList );
		rect.setBaselineValue( this.mBaselineValue );

		return rect;
	}



	
	/**
	 * 
	 * @return
	 */
	public String getTagName()
	{
		return TAG_NAME_BAR;
	}

	
	/**
	 * 
	 */
	public boolean writeProperty( final Element el )
	{
		final String cm = SGUtilityNumber.cm;
		final String pt = SGUtilityNumber.pt;
		el.setAttribute( KEY_VISIBLE, Boolean.toString( this.mVisibleFlag ) );
		el.setAttribute( KEY_BAR_WIDTH, Float.toString( this.mRectangleWidth*SGIConstants.CM_POINT_RATIO ) + cm );
		el.setAttribute( KEY_EDGE_LINE_WIDTH, Float.toString( this.mEdgeLineWidth ) + pt );
		el.setAttribute( KEY_BASELINE_VALUE, Double.toString( this.mBaselineValue ) );
		el.setAttribute( KEY_COLOR_LIST, SGUtilityText.getColorListString( this.mColorList ) );
		el.setAttribute( KEY_EDGE_LINE_COLOR, SGUtilityText.getColorString( this.mEdgeLineColor ) );
		return true;
	}

	

	
	/**
	 * 
	 * @param el
	 * @return
	 */
	public SGProperties readProperties( final Element el )
	{
		final String cm = SGUtilityNumber.cm;
		final String pt = SGUtilityNumber.pt;
		final float ratio = SGIConstants.CM_POINT_RATIO;

		String str = null;
		Number num = null;
		Color cl = null;
		Boolean b = null;
		ArrayList list = null;

		BarProperties p = new BarProperties();

		
		// visible
		str = el.getAttribute( KEY_VISIBLE );
		if( str.length()==0 )
		{
			return null;
		}
		b = SGUtilityText.getBoolean(str);
		if( b==null )
		{
			return null;
		}
		p.setVisible( b.booleanValue() );


		// width of the bar
		str = el.getAttribute( KEY_BAR_WIDTH );
		if( str.length()==0 )
		{
			return null;
		}
		num = SGUtilityText.getFloat(str,cm);
		if( num==null )
		{
			return null;
		}
		p.setBarWidth( num.floatValue()/ratio );


		// inner color list
		str = el.getAttribute( KEY_COLOR_LIST );
		if( str.length()==0 )
		{
			return null;
		}
		list = SGUtilityText.getColorList(str);
		if( list==null )
		{
			return null;
		}
		p.setColorList( list );


		// edge line width
		str = el.getAttribute( KEY_EDGE_LINE_WIDTH );
		if( str.length()==0 )
		{
			return null;
		}
		num = SGUtilityText.getFloat(str,pt);
		if( num==null )
		{
			return null;
		}
		p.setEdgeLineWidth( num.floatValue() );


		// baseline value
		str = el.getAttribute( KEY_BASELINE_VALUE );
		if( str.length()==0 )
		{
			return null;
		}
		num = SGUtilityText.getDouble(str);
		if( num==null )
		{
			return null;
		}
		p.setBaselineValue( num.doubleValue() );


		// edge line color
		str = el.getAttribute( KEY_EDGE_LINE_COLOR );
		if( str.length()==0 )
		{
			return null;
		}
		cl = SGUtilityText.getColorFromString(str);
		if( cl==null )
		{
			return null;
		}
		p.setEdgeLineColor( cl );
		
		
		return p;
	}

	

	/**
	 * 
	 * @return
	 */
	protected boolean initDrawingElement( final int num )
	{
		SGDrawingElementBar2D[] array = new SGDrawingElementBar2D[num];
		for( int ii=0; ii<num; ii++ )
		{
			array[ii] = new SGDrawingElementBar2D();
		}
		this.mDrawingElementArray = array;
		return true;
	}


	/**
	 * 
	 * @return
	 */
	protected boolean initDrawingElement( final SGTuple2f[] array )
	{
		final int num = array.length;
		SGDrawingElementBar2D[] bArray = new SGDrawingElementBar2D[num];
		for( int ii=0; ii<num; ii++ )
		{
			bArray[ii] = new SGDrawingElementBar2D();
			bArray[ii].setLocation( array[ii].x, array[ii].y );
		}
		this.mDrawingElementArray = bArray;
		return true;
	}


	/**
	 * 
	 */
	public boolean setLocation(
		final SGTuple2f[] pointArray )
	{

		if( this.mDrawingElementArray==null )
		{
			return true;
		}

		if( pointArray.length != this.mDrawingElementArray.length )
		{
			throw new IllegalArgumentException();
		}

		// 棒グラフの描画要素を作成
		for( int ii=0; ii<this.mDrawingElementArray.length; ii++ )
		{
			SGDrawingElementBar2D bar
				= (SGDrawingElementBar2D)this.mDrawingElementArray[ii];
			final boolean eff = !( pointArray[ii].isInfinite() || pointArray[ii].isNaN() );
			bar.setVisible( eff );
			if( eff )
			{
				bar.setBounds(
					pointArray[ii].x, pointArray[ii].y,
					this.mRectangleWidth, this.mRectangleHeight );
				bar.setLocation( pointArray[ii] );
			}
		}

		return true;
	}



	/**
	 * 
	 * @return
	 */
	public boolean setPropertiesOfDrawingElements()
	{
		for( int ii=0; ii<this.mDrawingElementArray.length; ii++ )
		{
			SGDrawingElementBar2D bar
				= (SGDrawingElementBar2D)this.mDrawingElementArray[ii];
			bar.setMagnification( this.mMagnification );
			bar.setColorList( this.mColorList );
			bar.setEdgeLineWidth( this.mEdgeLineWidth );
			bar.setEdgeLineColor( this.mEdgeLineColor );
		}
		return true;
	}



	/**
	 * 
	 */
	public SGProperties getProperties()
	{
		BarProperties p = new BarProperties();
		this.getProperties(p);
		return p;
	}


	/**
	 * 
	 */
	public boolean getProperties( SGProperties p )
	{
		if( p==null ) return false;
		if( ( p instanceof BarProperties ) == false ) return false;

		super.getProperties(p);

		BarProperties bp = (BarProperties)p;
		bp.setBarWidth( this.getRectangleWidth() );
		bp.setEdgeLineWidth( this.getEdgeLineWidth() );
		bp.setEdgeLineColor( this.getEdgeLineColor() );
		bp.setBaselineValue( this.getBaselineValue() );

		return true;
	}


	/**
	 * 
	 */
	public boolean setProperties( SGProperties p )
	{
		if( ( p instanceof BarProperties ) == false ) return false;

		if( super.setProperties(p) == false ) return false;

		BarProperties bp = (BarProperties)p;

		Float width = bp.getBarWidth();
		if( width==null )
		{
			return false;
		}
		this.setRectangleWidth( width.floatValue() );

		Float edgeLineWidth = bp.getEdgeLineWidth();
		if( edgeLineWidth==null )
		{
			return false;
		}
		this.setEdgeLineWidth( edgeLineWidth.floatValue() );

		Color edgeLineColor = bp.getEdgeLineColor();
		if( edgeLineColor==null )
		{
			return false;
		}
		this.setEdgeLineColor( edgeLineColor );

		Double baselineValue = bp.getBaselineValue();
		if( baselineValue==null )
		{
			return false;
		}
		this.setBaselineValue( baselineValue.doubleValue() );

		return true;
	}




	/**
	 * 
	 */
	public static class BarProperties extends ElementGroupProperties
	{
//		float width;
//		float edgeLineWidth;
//		Color edgeLineColor;
//		double baselineValue;


		public static final String[] keys = {
			KEY_BAR_WIDTH, KEY_EDGE_LINE_WIDTH, KEY_EDGE_LINE_COLOR, KEY_BASELINE_VALUE
		};


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


		/**
		 * 
		 */
		public boolean equals( final Object obj )
		{
			if( ( obj instanceof BarProperties ) == false )
			{
				return false;
			}

			if( super.equals(obj) == false ) return false;

			BarProperties p = (BarProperties)obj;
			for( int ii=0; ii<keys.length; ii++ )
			{
				if( this.getProperty(keys[ii]).equals(p.getProperty(keys[ii])) == false )
				{
					return false;
				}
			}

			return true;
		}



		public Float getBarWidth()
		{
			final String value = this.getProperty(KEY_BAR_WIDTH);
			return SGUtilityText.getFloat(value);
		}

		public Float getEdgeLineWidth()
		{
			final String value = this.getProperty(KEY_EDGE_LINE_WIDTH);
			return SGUtilityText.getFloat(value);
		}

		public Color getEdgeLineColor()
		{
			final String value = this.getProperty(KEY_EDGE_LINE_COLOR);
			return SGUtilityText.getColorFromString(value);
		}

		public Double getBaselineValue()
		{
			final String value = this.getProperty(KEY_BASELINE_VALUE);
			return SGUtilityText.getDouble(value);
		}


		public boolean setBarWidth( final float width )
		{
			if( width<0.0f )
			{
				return false;
			}
			this.setProperty( KEY_BAR_WIDTH, new Float(width).toString() );
			return true;
		}


		public boolean setBaselineValue( final double value )
		{
			this.setProperty( KEY_BASELINE_VALUE, new Double(value).toString() );
			return true;
		}


		public boolean setEdgeLineWidth( final float width )
		{
			if( width<0.0f )
			{
				return false;
			}
			this.setProperty( KEY_EDGE_LINE_WIDTH, new Float(width).toString() );
			return true;
		}

		public boolean setEdgeLineColor( final Color cl )
		{
			if( cl==null )
			{
				return false;
			}
			String str = SGUtilityText.getColorString(cl);
			if( str==null )
			{
				return false;
			}
			this.setProperty( KEY_EDGE_LINE_COLOR, str );
			return true;
		}





		/**
		 * 
		 */
/*		public String toString()
		{
			String str = new String("[");
			str += new String("visible="+visible+", ");
			str += new String("colorList="+colorList+", ");
			str += new String("width="+width+", ");
			str += new String("edgeLineWidth="+edgeLineWidth+", ");
			str += new String("edgeLineColor="+edgeLineColor+", ");
			str += new String("baselineValue="+baselineValue+", ");
			str += new String("]");

			return str;
		}
*/
	}

}
