/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.hayabusa.io;

import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Stroke;
import java.awt.geom.Rectangle2D;
import java.awt.Color;

import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.CategoryItemRendererState;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.CategoryItemLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.data.category.CategoryDataset;
import org.jfree.ui.GradientPaintTransformer;
import org.jfree.ui.RectangleEdge;

/**
 * HybsBarRenderer は、org.jfree.chart.renderer.category.BarRenderer を
 * 拡張したカスタマイズクラスです。
 * これは、描画に対して、予め制限を設けて、処理速度の向上を図っています。
 *
 * @og.rev 4.1.1.0 (2008/02/04) 新規作成
 *
 * @version  0.9.0	2001/05/05
 * @author	 Kazuhiko Hasegawa
 * @since	 JDK1.1,
 */
public class HybsBarRenderer extends BarRenderer implements HybsDrawItem {
	private static final long serialVersionUID = 602120140926L ;

	private transient ValueMarkOverColors overColors ;	// 5.9.24.3 (2017/09/29) マーカーラインでShapeを切り替える時の色指定
	private int		dynamicOCNo  = -1;					// 4.1.1.0 (2008/02/04) 動的なマーカーラインの基準シリーズ番号

	private boolean isLastVisible			;			// 4.1.2.0 (2008/03/12) 6.0.2.5 (2014/10/31) refactoring
	private Color[]	categoryColor			;			// 6.0.2.1 (2014/09/26) categoryカラー配列
	private final int hsCode = Long.valueOf( System.nanoTime() ).hashCode() ;	// 5.1.9.0 (2010/08/01) equals,hashCode

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
	 */
	public HybsBarRenderer() { super(); }		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。

	/**
	 * itemLabelVisible 時に、最後の値のみ表示するかどうか[true:有効/false:無効]を指定します。
	 *
	 * これは、itemLabelVisible 属性に、"last" という設定値を指定した場合は、
	 * 最後のみラベル表示します。
	 * このメソッドでは、true が指定された場合は、"last" 属性が有効になったと
	 * 判断します。
	 * (独自メソッド。HybsDrawItem より継承)
	 *
	 * @og.rev 4.1.2.0 (2008/03/12) 新規追加
	 *
	 * @param	flag	最後の値のみ表示するかどうか[true:有効/false:無効]
	 */
	@Override
	public void setItemLabelLastVisible( final boolean flag ) {
		isLastVisible = flag;					// 6.0.2.5 (2014/10/31) refactoring
	}

	/**
	 * マーカーラインの超過時のShape色管理クラスを設定します。
	 *
	 * 動的なマーカーラインを使用する場合は、引数のシリーズデータが
	 * マーカーラインの最下位閾値に相当します。これは、グラフ化されますが、
	 * Shape は自動的に削除されます。
	 * 逆に、最上位のデータ(シリーズ＝０)のShape は必ず付けます。
	 *
	 * @og.rev 4.1.0.1(2008/01/19) 新規追加
	 *
	 * @param	vmoc	マーカーラインの超過時のShape色管理クラス
	 * @param	dynamicOverColorNo	動的なマーカーラインの基準シリーズ番号
	 */
	protected void setValueMarkOverColors( final ValueMarkOverColors vmoc,
										final int dynamicOverColorNo ) {
		overColors	= vmoc;
		dynamicOCNo	= dynamicOverColorNo;
	}

	/**
	 * categoryカラー配列を設定します。
	 *
	 * これは、HybsJDBCCategoryDataset クラスで、カテゴリカラーを指定した場合に、
	 * そこから取り出した値をセットすることで、Hybs***Renderer に設定して使います。
	 * Hybs***Renderer 側では、このカラー配列を使用して、getItemPaint(int,int) を
	 * オーバーライドして使います。
	 * (独自メソッド。HybsDrawItem より継承)
	 *
	 * @og.rev 6.0.2.1 (2014/09/26) 新規追加
	 *
	 * @param	cateColor	categoryカラー配列(可変長引数)
	 */
	@Override
	public void setCategoryColor( final Color... cateColor ) {
		// 6.0.2.5 (2014/10/31) refactoring
		if( cateColor != null ) { categoryColor = cateColor.clone(); }		// 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
	}

	/**
	 * カテゴリ違いのColorオブジェクトを返します。
	 *
	 * Returns the paint used to color data items as they are drawn.
	 * <p>
	 * The default implementation passes control to the
	 * <code>lookupSeriesPaint()</code> method. You can override this method
	 * if you require different behaviour.
	 *
	 * @param row  the row (or series) index (zero-based).
	 * @param column  the column (or category) index (zero-based).
	 *
	 * @return カテゴリ違いのColorオブジェクト
	 */
	@Override
	public Paint getItemPaint( final int row, final int column ) {
		// 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
		return categoryColor == null ? super.getItemPaint( row,column ) : categoryColor[column];

	}

	/**
	 * drawItem と同等の機能を持った、高速版メソッドです。
	 *
	 * @og.rev 4.1.1.0 (2008/02/04) 新規追加
	 * @og.rev 4.1.2.0 (2008/03/12) ラベルのアンダーライン時にItemLavelを表示しない
	 * @og.rev 5.9.24.3 (2017/09/29) overColor対応
	 *
	 * @param g2			Graphics2Dオブジェクト
	 * @param state			CategoryItemRendererStateオブジェクト
	 * @param dataArea		Rectangle2Dオブジェクト
	 * @param plot			CategoryPlotオブジェクト
	 * @param domainAxis	CategoryAxisオブジェクト
	 * @param rangeAxis		ValueAxisオブジェクト
	 * @param dataset		CategoryDatasetオブジェクト
	 * @param serNo			シリアル番号
	 */
	@Override
	public void drawItem2( final Graphics2D g2, final CategoryItemRendererState state,
			final Rectangle2D dataArea, final CategoryPlot plot, final CategoryAxis domainAxis,
			final ValueAxis rangeAxis, final CategoryDataset dataset, final int serNo ) {

		final int clmCount = dataset.getColumnCount();
		final int rowCount = dataset.getRowCount();
		final RectangleEdge edge = plot.getRangeAxisEdge();

		final PlotOrientation orientation = plot.getOrientation();
		final double minBarLen = getMinimumBarLength();
		final double barWidth  = state.getBarWidth();
		final boolean isDrawOutline = isDrawBarOutline() &&
									state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD ;

		// 4.1.2.0 (2008/03/12)
		HybsCategoryAxis hybsAxis = null;
		if( domainAxis instanceof HybsCategoryAxis ) {
			hybsAxis = (HybsCategoryAxis)domainAxis;
			hybsAxis.setItemLabelLastVisible( isLastVisible );				// 6.0.2.5 (2014/10/31) refactoring
		}

		for( int row=0; row<rowCount; row++ ) {
			final boolean isLabelsVisible = isSeriesItemLabelsVisible( row );		// 6.0.2.5 (2014/10/31) refactoring

			for( int column=0; column<clmCount; column++ ) {
				final Number v1Num = dataset.getValue( row,column );
				if( v1Num == null ) { continue; }
				final double value = v1Num.doubleValue();

				// 書き出し開始位置をずらす。
				final double barW0 = calculateBarW0( plot,orientation,dataArea,domainAxis,state,row,column );
				final double[] barL0L1 = calculateBarL0L1( value );
				if( barL0L1 == null ) { continue; }

				final double transL0 = rangeAxis.valueToJava2D( barL0L1[0],dataArea,edge );
				final double transL1 = rangeAxis.valueToJava2D( barL0L1[1],dataArea,edge );
				final double barL0 = Math.min( transL0,transL1 );
				final double barLength = Math.max( Math.abs( transL1 - transL0 ),minBarLen );

				// Bar の描画
				Rectangle2D bar = null;
				if( orientation == PlotOrientation.HORIZONTAL ) {
					bar = new Rectangle2D.Double( barL0,barW0,barLength,barWidth );
				}
				else {
					bar = new Rectangle2D.Double( barW0,barL0,barWidth,barLength );
				}

				Paint itemPaint = getItemPaint( row,column );
				// 4.3.1.1 (2008/08/23) 変数名を t ⇒ gpt に変更
				final GradientPaintTransformer gpt = getGradientPaintTransformer();
				if( gpt != null && itemPaint instanceof GradientPaint ) {
					itemPaint = gpt.transform( (GradientPaint) itemPaint,bar );
				}

				// 5.9.24.3 (2017/09/29) overColor対応
				if( overColors != null ) {
					if( dynamicOCNo >= 0 ) {
						itemPaint = overColors.getColor( value,dataset.getValue( dynamicOCNo,column ) );
					}
					else {
						itemPaint = overColors.getColor( value );
					}
				}
				
				g2.setPaint( itemPaint );
				g2.fill( bar );

				// outline の描画
				if( isDrawOutline ) {
					final Stroke stroke = getItemOutlineStroke( row,column );
					final Paint paint = getItemOutlinePaint( row,column );
					if( stroke != null && paint != null ) {
						g2.setStroke( stroke );
						g2.setPaint( paint );
						g2.draw( bar );
					}
				}

				// ItemLabel の描画
				final CategoryItemLabelGenerator generator = getItemLabelGenerator( row,column );
				// 6.9.7.0 (2018/05/14) PMD These nested if statements could be combined
//				if( generator != null && isLabelsVisible ) {		// 6.0.2.5 (2014/10/31) refactoring
//					// 4.1.2.0 (2008/03/12) アンダースコアの場合は、表示しない。
//					if( hybsAxis != null && hybsAxis.isViewItemLabel( column ) ) {
				if( generator != null && isLabelsVisible			// 6.0.2.5 (2014/10/31) refactoring
					// 4.1.2.0 (2008/03/12) アンダースコアの場合は、表示しない。
					&& hybsAxis != null && hybsAxis.isViewItemLabel( column ) ) {
						drawItemLabel( g2,dataset,row,column,plot,generator,bar, value<0.0 );
//					}
				}
				// 4.3.1.0 (2008/08/09) item entity の追加
				final EntityCollection entities = state.getEntityCollection();
				if( entities != null ) {
					addItemEntity( entities, dataset, row, column, bar );
				}
			}
		}
	}

	/**
	 * この文字列と指定されたオブジェクトを比較します。
	 *
	 * 親クラスで、equals メソッドが実装されているため、警告がでます。
	 *
	 * @og.rev 5.1.8.0 (2010/07/01) findbug対応
	 * @og.rev 5.1.9.0 (2010/08/01) findbug対応
	 *
	 * @param	object	比較するオブジェクト
	 *
	 * @return	Objectが等しい場合は true、そうでない場合は false
	 */
	@Override
	public boolean equals( final Object object ) {
		// 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
		return super.equals( object ) && hsCode == ((HybsBarRenderer)object).hsCode;

	}

	/**
	 * このオブジェクトのハッシュコードを取得します。
	 *
	 * @og.rev 5.1.8.0 (2010/07/01) findbug対応
	 * @og.rev 5.1.9.0 (2010/08/01) findbug対応
	 *
	 * @return	ハッシュコード
	 */
	@Override
	public int hashCode() { return hsCode ; }
}
