/*
 * 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.Graphics2D;
import java.awt.Stroke;
import java.awt.Paint;
import java.awt.Color;
import java.awt.geom.Rectangle2D;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;

import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.CategoryCrosshairState;
import org.jfree.chart.urls.CategoryURLGenerator;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.axis.ValueTick;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.chart.renderer.category.CategoryItemRendererState;
import org.jfree.chart.renderer.category.StackedBarRenderer;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.general.DatasetUtilities;

/**
 * HybsCategoryPlot は、CategoryPlot を拡張したカスタマイズクラスです。
 * これは、シリーズの表示色を変更する箇所で、描画順により、きれいに表示されない
 * 現象への対応案です。
 * 描画順を、最も最後に行うように、修正しています。
 *
 * renders each data item using a {@link CategoryItemRenderer}.
 *
 * @og.rev 3.8.9.2 (2007/07/28) 新規作成
 *
 * @version  0.9.0  2001/05/05
 * @author   Kazuhiko Hasegawa
 * @since    JDK1.1,
 */
public class HybsCategoryPlot extends CategoryPlot {
	private static final long serialVersionUID = 602020140919L ;

	private final Map<Integer,Double> barWidths = new HashMap<>();	// 4.3.1.1 (2008/08/23) final化
	private int		serNo	  = -1;
	private int		rangeSkip = -1;			// 4.1.1.0 (2008/02/04) 縦軸のグリッドをスキップする間隔(初期値:-1)
	private final int hsCode = Long.valueOf( System.nanoTime() ).hashCode() ;	// 5.1.9.0 (2010/08/01) equals,hashCode

	/**
	 * デフォルトコンストラクター
	 * シリーズ番号を、初期化しておきます。
	 *
	 **/
	public HybsCategoryPlot() {
		// 4.3.4.4 (2009/01/01)
		this( -1 );
	}

	/**
	 * シリーズ番号 を、指定して、オブジェクトを作成するコンストラクター
	 *
	 * @param	serNo	ピックアップするシリーズ番号
	 **/
	protected HybsCategoryPlot( final int serNo ) {
		super();		// 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
		// 4.3.4.4 (2009/01/01)
		this.serNo = serNo;
	}

	/**
	 * ピックアップするシリーズ番号を設定します(独自メソッド)。
	 *
	 * @og.rev 4.1.1.0 (2008/02/04) データの値(itemText)表示の継承
	 *
	 * @param	newSerNo	ピックアップするシリーズ番号
	 **/
	protected void setSeriesPikup( final int newSerNo ) {
		final int oldSerNo = serNo ;
		serNo = newSerNo;
		final CategoryItemRenderer rend = getRenderer();
		final CategoryURLGenerator urlGen = rend.getSeriesItemURLGenerator( oldSerNo );
		if( urlGen != null ) {
			rend.setSeriesItemURLGenerator( oldSerNo,null );
			rend.setSeriesItemURLGenerator( serNo   ,urlGen );
		}

		// 4.1.1.0 (2008/02/04) データの値(itemText)表示の継承
		if( rend.isSeriesItemLabelsVisible( oldSerNo ) ) {
			rend.setSeriesItemLabelsVisible( oldSerNo,false );
			rend.setSeriesItemLabelsVisible( serNo   ,true  );
		}
	}

	/**
	 * 縦軸のグリッド線(水平線)をスキップする間隔を指定します(初期値:-1)。
	 *
	 * 縦軸のグリッド線を表示する際に、スキップする間隔を指定します。
	 * 通常は、ラベルと同じだけのグリッド線が掛かれますが、ラベルよりも
	 * 少ない数のグリッド線(例えば、２つおき)を出す場合に、値を設定します。
	 * "1" (初期値)では、１つづつ表示(つまり、すべて表示する)します。
	 * "2" とすると、１つおきに、"3" とすると、２つおきに表示します。
	 * なお、先頭から表示を開始します。
	 * (独自メソッド)
	 *
	 * 6.0.2.0 (2014/09/19) 前回の JFreeChart のVerUpで、グリッド線の表示が
	 * ５個おきに変わったようです。設定する値を マイナスにすると、初期設定の値を
	 * 使用するように変更します。微調整は、個々にお願いします。
	 *
	 * 初期値は、"-1" (設定しない)です。
	 *
	 * @og.rev 4.1.1.0 (2008/02/04) 新規追加
	 *
	 * @param	rngSkip	縦軸のグリッド線(水平線)をスキップする間隔
	 */
	protected void setRangeSkip( final int rngSkip ) {
		rangeSkip = rngSkip;
	}

	/**
	 * BarChart のバーの幅を直接指定します。
	 * 通常は、maxBarWidth や itemMargin で比率指定しますが、
	 * ここでは、CategoryItemRendererState オブジェクトに直接設定する為の
	 * データセット単位のマップを作成します。
	 * (独自メソッド)
	 *
	 * @param	index	データセット番号
	 * @param	width	バーの幅
	 **/
	protected void setBarWidth( final int index,final Double width ) {
		barWidths.put( index,width );
	}

	/**
	 * CategoryPlot の render メソッドをオーバーライドしています。
	 *
	 * Draws a representation of a dataset within the dataArea region using the
	 * appropriate renderer.
	 *
	 * @param g2  the graphics device.
	 * @param dataArea	the region in which the data is to be drawn.
	 * @param index  the dataset and renderer index.
	 * @param info	an optional object for collection dimension information.
     * @param crosshairState  a state object for tracking crosshair info (null permitted).
	 *
	 * @return	描画するデータが見つかった場合は、true
	 */
	@Override
	public boolean render( final Graphics2D g2, final Rectangle2D dataArea, final int index,
						  final PlotRenderingInfo info , final CategoryCrosshairState crosshairState) {

		boolean foundData = false;
		final CategoryDataset currentDataset = getDataset(index);
		final CategoryItemRenderer renderer = getRenderer(index);
		// 4.3.1.0 (2008/08/19) 軸とデータセットのマッピング
		this.mapDatasetToRangeAxis( index, index );

		CategoryAxis domainAxis = null ;
		if( renderer instanceof StackedBarRenderer ) {
			domainAxis = getDomainAxis(index);
		}
		else {
			domainAxis = getDomainAxisForDataset(index);
		}

		final ValueAxis rangeAxis = getRangeAxis(index);
		final boolean hasData = !DatasetUtilities.isEmptyOrNull(currentDataset);
		if( hasData && renderer != null ) {
			foundData = true;
			final CategoryItemRendererState state = renderer.initialise(g2, dataArea, this, index, info);

			// 4.0.3.0 (2008/01/07) 棒グラフのバー幅指定
			final Double bwidth = barWidths.get( index );
			if( bwidth != null ) { state.setBarWidth( bwidth.doubleValue() ); }

			if( renderer instanceof HybsDrawItem ) {
				// 6.0.2.1 (2014/09/26) categoryカラー配列
				if( currentDataset instanceof HybsDataset ) {
					final Color[] categoryColor = ((HybsDataset)currentDataset).getCategoryColor();
					((HybsDrawItem)renderer).setCategoryColor( categoryColor );
				}

				((HybsDrawItem)renderer).drawItem2(g2, state, dataArea, this,
						domainAxis, rangeAxis, currentDataset , serNo );
			}
			else {
				final int clmCount = currentDataset.getColumnCount();
				final int rowCount = currentDataset.getRowCount();
				final int passCount = renderer.getPassCount();
				for( int pass=0; pass<passCount; pass++ ) {
					for( int column=0; column<clmCount; column++ ) {
						for( int row=0; row<rowCount; row++ ) {
							if( row == serNo ) { continue; }	// Mis Add 2007/07/23
							renderer.drawItem(g2, state, dataArea, this,
									domainAxis, rangeAxis, currentDataset,
									row, column, pass);
						}
						// 指定のシリーズと一致する場合は、最後に描画する。 Mis Add 2007/07/23
						if( serNo >= 0 ) {
							renderer.drawItem(g2, state, dataArea, this,
									domainAxis, rangeAxis, currentDataset,
									serNo, column, pass);
						}
					}
				}
			}
		}
		return foundData;
	}

	/**
	 * CategoryPlot の drawRangeGridlines メソッドをオーバーライドします。
	 *
	 * Draws the gridlines for the plot.
	 *
	 * @og.rev 6.0.2.0 (2014/09/19) rangeSkip 属性 の初期値を、-1(設定なし)に変更
	 *
	 * @param g2  the graphics device.
	 * @param dataArea	the area inside the axes.
	 * @param ticks  the ticks.
	 * @see #drawDomainGridlines(Graphics2D, Rectangle2D)
	 */
	@SuppressWarnings("rawtypes")
	@Override
	protected void drawRangeGridlines( final Graphics2D g2, final Rectangle2D dataArea,
									   final List ticks ) {
		// draw the range grid lines, if any...
	 	// 6.0.2.0 (2014/09/19) rangeSkip 属性 の初期値を、-1(設定なし)に変更
		if( rangeSkip <= 0 ) { super.drawRangeGridlines( g2,dataArea,ticks ); }
		else {
			if( isRangeGridlinesVisible() ) {
				final Stroke gridStroke = getRangeGridlineStroke();
				final Paint gridPaint = getRangeGridlinePaint();
				if( gridStroke != null && gridPaint != null ) {
					final ValueAxis axis = getRangeAxis();
					final CategoryItemRenderer renderer1 = getRenderer();
					if( axis != null && renderer1 != null ) {
						final Iterator<?> iterator = ticks.iterator();
						int cnt = 0;
						while( iterator.hasNext() ) {
							final ValueTick tick = (ValueTick) iterator.next();
							if( cnt % rangeSkip == 0 ) {
								renderer1.drawRangeGridline(g2, this,
											axis, dataArea, tick.getValue());
							}
							cnt++ ;
						}
					}
				}
			}
		}
	}

	/**
	 * この文字列と指定されたオブジェクトを比較します。
	 *
	 * 親クラスで、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 == ((HybsCategoryPlot)object).hsCode;

//		if( super.equals( object ) ) {
//			return hsCode == ((HybsCategoryPlot)object).hsCode;
//		}
//		return false;
	}

	/**
	 * このオブジェクトのハッシュコードを取得します。
	 *
	 * @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 ; }
}
