/*
 * 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.geom.Rectangle2D;

import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.renderer.category.StackedBarRenderer;
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.data.category.CategoryDataset;
import org.jfree.ui.GradientPaintTransformer;
import org.jfree.ui.RectangleEdge;
import org.jfree.data.DataUtilities;

/**
 * HybsStackedBarRenderer は、org.jfree.chart.renderer.category.StackedBarRenderer を
 * 拡張したカスタマイズクラスです。
 * これは、グラフの書き出し位置の調整比率(domainMargin)を設定できます。
 *
 * @og.rev 4.1.1.0 (2008/02/16) 新規作成
 *
 * @version  0.9.0	2001/05/05
 * @author	 Kazuhiko Hasegawa
 * @since	 JDK1.1,
 */
public class HybsStackedBarRenderer extends StackedBarRenderer  {
	private static final long serialVersionUID = 519020100801L ;

	private double	domainMargin	= 0.0;		// 4.1.1.0 (2008/02/14) グラフの書き出し位置の調整比率

	private final int hsCode = Long.valueOf( System.nanoTime() ).hashCode() ;	// 5.1.9.0 (2010/08/01) equals,hashCode

	// 4.3.4.4 (2009/01/01)
//	/**
//	 * Creates a new bar renderer with default settings.
//	 */
//	public HybsStackedBarRenderer() {
//		super();
//	}

	/**
	 * グラフの書き出し位置の調整比率を指定します。
	 *
	 * グラフを描画する場合の、書き出し位置を少しずらします。
	 * これは、グラフの幅に対して、比率で指定します。
	 * 0.0(初期値)の場合は、初期描画位置である、CategoryAnchor.Middle と
	 * 同じ箇所から、書き出されます。
	 * 1.0 の場合、中心から、グラフ幅の半分が加算され、END位置に寄ります。
	 * 同様に、-1.0 の場合は、グラフ幅の半分が減算され、START 位置になります。
	 * つまり、中心から、グラフ幅の半分単位で、前方/後方にずらす事が出来ます。
	 *   書き出し位置 ＝ 中心(Middle) + (domainMargin)＊幅/２
	 * 初期値は、0.0(真ん中:MIDDLE)です。
	 *
	 * @og.rev 4.1.1.0 (2008/02/14) 新規追加
	 *
	 * @param	margin	グラフの書き出し位置の調整比率
	 */
	public void setDomainMargin( final double margin ) {
		domainMargin = margin;
	}

	/**
	 * Draws a stacked bar for a specific item.
	 *
	 * @param g2  the graphics device.
	 * @param state  the renderer state.
	 * @param dataArea	the plot area.
	 * @param plot	the plot.
	 * @param domainAxis  the domain (category) axis.
	 * @param rangeAxis  the range (value) axis.
	 * @param dataset  the data.
	 * @param row  the row index (zero-based).
	 * @param column  the column index (zero-based).
	 * @param pass	the pass index.
	 */
	@Override
	public void drawItem( final Graphics2D g2,
						  final CategoryItemRendererState state,
						  final Rectangle2D dataArea,
						  final CategoryPlot plot,
						  final CategoryAxis domainAxis,
						  final ValueAxis rangeAxis,
						  final CategoryDataset dataset,
						  final int row,
						  final int column,
						  final int pass) {

		// nothing is drawn for null values...
		Number dataValue = dataset.getValue(row, column);
		if (dataValue == null) {
			return;
		}

		double value = dataValue.doubleValue();
		double total = 0.0;  // only needed if calculating percentages
		if (getRenderAsPercentages()) {
			total = DataUtilities.calculateColumnTotal(dataset, column);
			value = value / total;
		}

		PlotOrientation orientation = plot.getOrientation();

		double barW0 = domainAxis.getCategoryStart(column, getColumnCount(),
				dataArea, plot.getDomainAxisEdge())
				+ domainMargin * state.getBarWidth() / 2.0;

		double positiveBase = getBase();
		double negativeBase = positiveBase;

		// 4.3.1.1 (2008/08/23) 変数名が短いので変更(v ⇒ nm , d ⇒ vd )
		for (int i = 0; i < row; i++) {
			Number nm = dataset.getValue(i, column);
			if (nm != null) {
				double vd = nm.doubleValue();
				if (getRenderAsPercentages()) {
					vd = vd / total;
				}
				if (vd > 0) {
					positiveBase = positiveBase + vd;
				}
				else {
					negativeBase = negativeBase + vd;
				}
			}
		}

		double translatedBase;
		double translatedValue;
		RectangleEdge location = plot.getRangeAxisEdge();
		if (value >= 0.0) {
			translatedBase = rangeAxis.valueToJava2D(positiveBase, dataArea,
					location);
			translatedValue = rangeAxis.valueToJava2D(positiveBase + value,
					dataArea, location);
		}
		else {
			translatedBase = rangeAxis.valueToJava2D(negativeBase, dataArea,
					location);
			translatedValue = rangeAxis.valueToJava2D(negativeBase + value,
					dataArea, location);
		}
		double barL0 = Math.min(translatedBase, translatedValue);
		double barLength = Math.max(Math.abs(translatedValue - translatedBase),
				getMinimumBarLength());

		Rectangle2D bar = null;
		if (orientation == PlotOrientation.HORIZONTAL) {
			bar = new Rectangle2D.Double(barL0, barW0, barLength,
					state.getBarWidth());
		}
		else {
			bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(),
					barLength);
		}
		if (pass == 0) {
			Paint itemPaint = getItemPaint(row, column);
			// 4.3.1.1 (2008/08/23) 変数名を t ⇒ gpt に変更
			GradientPaintTransformer gpt = getGradientPaintTransformer();
			if (gpt != null && itemPaint instanceof GradientPaint) {
				itemPaint = gpt.transform((GradientPaint) itemPaint, bar);
			}
			g2.setPaint(itemPaint);
			g2.fill(bar);
			if (isDrawBarOutline()
					&& state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
				g2.setStroke(getItemOutlineStroke(row, column));
				g2.setPaint(getItemOutlinePaint(row, column));
				g2.draw(bar);
			}

			// add an item entity, if this information is being collected
			EntityCollection entities = state.getEntityCollection();
			if (entities != null) {
				addItemEntity(entities, dataset, row, column, bar);
			}
		}
		else if (pass == 1) {
			CategoryItemLabelGenerator generator = getItemLabelGenerator(row,
					column);
			if (generator != null && isItemLabelVisible(row, column)) {
				drawItemLabel(g2, dataset, row, column, plot, generator, bar,
						(value < 0.0));
			}
		}
	}

	/**
	 * この文字列と指定されたオブジェクトを比較します。
	 *
	 * 親クラスで、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 ) {
//		return super.equals( object );
		if( super.equals( object ) ) {
			return hsCode == ((HybsStackedBarRenderer)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	ハッシュコード
	 */
//	public int hashCode() { return super.hashCode() ; }
	@Override
	public int hashCode() { return hsCode ; }

	/**
	 * このオブジェクトと指定されたオブジェクトを比較します。
	 *
	 * @og.rev 4.3.1.1 (2008/08/23) 新規追加
	 * @og.rev 5.1.8.0 (2010/07/01) 廃止
	 *
	 * @param anObject Object 比較されるオブジェクト
	 *
	 * @return	指定されたオブジェクトが等しい場合は true、そうでない場合は false
	 */
//	public boolean equals( final Object anObject ) {
//		if( super.equals( anObject ) ) {
//			HybsStackedBarRenderer other = ((HybsStackedBarRenderer)anObject);
////			return ( domainMargin == other.domainMargin );
//			return Double.compare( domainMargin,other.domainMargin ) == 0 ;
//		}
//		return false;
//	}

	/**
	 * このオブジェクトのハッシュコードを返します。
	 *
	 * @og.rev 4.3.1.1 (2008/08/23) 新規追加
	 * @og.rev 5.1.8.0 (2010/07/01) 廃止
	 *
	 * @return	このオブジェクトのハッシュコード値
	 */
//	public int hashCode() {
//		return super.hashCode() ^ Double.valueOf( domainMargin ).hashCode() ;
//	}
}
