/*
 * 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.Color;
import java.awt.Stroke;
import java.awt.Paint;
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.event.PlotChangeEvent;
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 = 4000L ;

	private final Map<Integer,Double> barWidths = new HashMap<Integer,Double>();	// 4.3.1.1 (2008/08/23) final化
	private int		serNo	  = -1;
	private boolean isNotify  = false;
	private int		rangeSkip = 1;			// 4.1.1.0 (2008/02/04) 縦軸のグリッドをスキップする間隔

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

	/**
	 * シリーズ番号 を、指定して、オブジェクトを作成するコンストラクター
	 *
	 * @param   serNo int ピックアップするシリーズ番号
	 **/
	protected HybsCategoryPlot( final int serNo ) {
		// 4.3.4.4 (2009/01/01)
//		super();
		this.serNo = serNo;
	}

	/**
	 * ピックアップするシリーズ番号を設定します。
	 *
	 * @og.rev 4.1.1.0 (2008/02/04) データの値(itemText)表示の継承
	 *
	 * @param   newSerNo int ピックアップするシリーズ番号
	 **/
	protected void setSeriesPikup( final int newSerNo ) {
		int oldSerNo = serNo ;
		serNo = newSerNo;
		CategoryItemRenderer rend = getRenderer();
		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  );
		}
	}

	/**
	 * Notifyを設定します。
	 *
	 * @param   isNotify boolean
	 **/
	protected void setNotify( final boolean isNotify ) {
		this.isNotify = isNotify;
	}

	/**
	 * 縦軸のグリッド線(水平線)をスキップする間隔を指定します。
	 *
	 * 縦軸のグリッド線を表示する際に、スキップする間隔を指定します。
	 * 通常は、ラベルと同じだけのグリッド線が掛かれますが、ラベルよりも
	 * 少ない数のグリッド線（例えば、２つおき）を出す場合に、値を設定します。
	 * "1" (初期値)では、１つづつ表示（つまり、すべて表示する）します。
	 * "2" とすると、１つおきに、"3" とすると、２つおきに表示します。
	 * 初期値は、"1" (すべて表示)です。
	 * なお、先頭から表示を開始します。
	 *
	 * @og.rev 4.1.1.0 (2008/02/04) 新規追加
	 *
	 * @param   rngSkip int 縦軸のグリッド線(水平線)をスキップする間隔
	 */
	protected void setRangeSkip( final int rngSkip ) {
		rangeSkip = rngSkip;
	}

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

	/**
	 * 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.
	 *
	 * @return A boolean that indicates whether or not real data was found.
	 */
	public boolean render( final Graphics2D g2, final Rectangle2D dataArea, final int index,
						  final PlotRenderingInfo info ) {
		boolean foundData = false;
		CategoryDataset currentDataset = getDataset(index);
		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);
		}

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

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

			if( renderer instanceof HybsDrawItem ) {
				((HybsDrawItem)renderer).drawItem2(g2, state, dataArea, this,
						domainAxis, rangeAxis, currentDataset , serNo );
			}
			else {
				int columnCount = currentDataset.getColumnCount();
				int rowCount = currentDataset.getRowCount();
				int passCount = renderer.getPassCount();
				for( int pass=0; pass<passCount; pass++ ) {
					for( int column=0; column<columnCount; 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;
	}
 
	/**
	 * Draws the gridlines for the plot.
	 *
	 * @param g2  the graphics device.
	 * @param dataArea	the area inside the axes.
	 * @param ticks  the ticks.
	 * 
	 * @see #drawDomainGridlines(Graphics2D, Rectangle2D)
	 */
	protected void drawRangeGridlines( final Graphics2D g2, final Rectangle2D dataArea, 
									   final List ticks ) {
		// draw the range grid lines, if any...

		if (isRangeGridlinesVisible()) {
			Stroke gridStroke = getRangeGridlineStroke();
			Paint gridPaint = getRangeGridlinePaint();
			if ((gridStroke != null) && (gridPaint != null)) {
				ValueAxis axis = getRangeAxis();
				CategoryItemRenderer renderer1 = getRenderer();
				if (axis != null && renderer1 != null) {
					Iterator iterator = ticks.iterator();
					int cnt = 0;
					while (iterator.hasNext()) {
						ValueTick tick = (ValueTick) iterator.next();
						if( cnt % rangeSkip == 0 ) {
							renderer1.drawRangeGridline(g2, this, 
										axis, dataArea, tick.getValue());
						}
						cnt++ ;
					}
				}
			}
		}
	}

	/**
	 * このオブジェクトと指定されたオブジェクトを比較します。
	 *
	 * @og.rev 4.0.0.0 (2007/11/28) 新規追加
	 *
	 * @param anObject Object 比較されるオブジェクト
	 * @return boolean 指定されたオブジェクトが等しい場合は true、そうでない場合は false
	 */
	public boolean equals( final Object anObject ) {
		if( super.equals( anObject ) ) {
			HybsCategoryPlot other = ((HybsCategoryPlot)anObject);
			if( other.serNo == serNo && other.isNotify == isNotify ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * このオブジェクトのハッシュコードを返します。
	 *
	 * @og.rev 4.0.0.0 (2007/11/28) 新規追加
	 *
	 * @return int このオブジェクトのハッシュコード値
	 */
	public int hashCode() {
		return super.hashCode() + serNo + Boolean.valueOf( isNotify ).hashCode() ;
	}
}
