/*
 * 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 org.opengion.fukurou.util.StringUtil;

import java.util.List;
import java.util.ArrayList;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import org.jfree.ui.RectangleEdge;
import org.jfree.text.TextBlock;
import org.jfree.chart.axis.AxisState;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryAnchor;
// import org.jfree.chart.plot.CategoryPlot;

/**
 * <p>HybsCategoryAxis は、CategoryAxis クラスを継承した、横軸管理クラスです。
 * 横軸ラベルの表示制御を、主に行っています。
 * 横軸表示には、３つの制御機能がカスタマイズされています。
 *
 *  １．"_" ラベルのスキップ(非表示)
 *  ２．cutNo 属性による、ラベル文字位置指定のキーブレイク
 *  ３．skip 属性による、ラベルをスキップする間隔の指定
 *
 * 上記、１，２，３ の順番で優先的に処理されます。
 *
 * @version  0.9.0	2007/06/21
 * @author	 Kazuhiko Hasegawa
 * @since	 JDK1.1,
 */
public class HybsCategoryAxis extends CategoryAxis {
	private static final long serialVersionUID = 4110L;

	private static final TextBlock NULL_LABEL = new TextBlock() ;

	// 4.1.2.0 (2008/03/12) 新規追加
	private enum LabelVisible { TRUE(true) , FALSE(false) , UNDER(true) ;
		private final boolean flag ;
		LabelVisible( boolean flag ) { this.flag = flag; }
		public boolean booleanValue() { return flag; }
	};

	/** For serialization. */
	private int skip  = 1;				// skip数
	private int count = 0;				// skip 時の現在位置のカウント

	private transient List<LabelVisible> labelBreak = null;
	private int		cutNo			= -1;		// 4.1.1.0 (2008/02/04) ラベルブレイクのsubstring 位置
	private String	breakKey		= null;		// 4.1.1.0 (2008/02/04) ラベルブレイクの前回キー
	private boolean isItemLabelLastVisible = false;	// 4.1.2.0 (2008/03/12)

	/**
	 * Creates a new category axis with no label.
	 */
	public HybsCategoryAxis( final String label ) {
		this( label,1,-1 );
	}

	/**
	 * Constructs a category axis, using default values where necessary.
	 *
	 * @og.rev 4.1.1.0 (2008/02/04) cutNo 新規追加
	 *
	 * @param label  String the axis label (<code>null</code> permitted).
	 * @param skip   int    ラベルの表示間隔
	 * @param cutNo  int    ラベルブレイクのsubstring 位置
	 */
	protected HybsCategoryAxis( final String label,final int skip,final int cutNo ) {
		super( label );
		this.skip	= skip ;
		this.cutNo	= cutNo ;
	}

	/**
	 * itemLabelVisible 時に、最後の値のみ表示するかどうかを指定します。
	 *
	 * これは、itemLabelVisible 属性に、"last" という設定値を指定した場合は、
	 * 最後のみラベル表示します。
	 * このメソッドでは、true が指定された場合は、"last" 属性が有効になったと
	 * 判断します。
	 *
	 * @og.rev 4.1.2.0 (2008/03/12) 新規追加
	 *
	 * @param flag		boolean
	 */
	protected void setItemLabelLastVisible( final boolean flag ) {
		isItemLabelLastVisible = flag;
	}

	/**
	 * Creates a temporary list of ticks that can be used when drawing the axis.
	 *
	 * @og.rev 4.1.1.0 (2008/02/04) labelBreak 新規追加
	 *
	 * @param g2  the graphics device (used to get font measurements).
	 * @param state  the axis state.
	 * @param dataArea	the area inside the axes.
	 * @param edge	the location of the axis.
	 * 
	 * @return A list of ticks.
	 */
	public List refreshTicks( final Graphics2D g2,
							  final AxisState state,
							  final Rectangle2D dataArea,
							  final RectangleEdge edge) {
		count = 0;
		labelBreak = new ArrayList<LabelVisible>();

		return super.refreshTicks( g2, state, dataArea, edge);
	}

	/**
	 * Creates a label.
	 *
	 * このメソッドでは、３つの拡張機能を実現しています。
	 *  １．"_" ラベルのスキップ(非表示)
	 *  ２．cutNo 属性による、ラベル文字位置指定のキーブレイク
	 *  ３．skip 属性による、ラベルをスキップする間隔の指定
	 * cutNo が指定された場合は、skip 処理は行われません。また、
	 * その場合のラベルは、cutNoで指定された先頭文字列のみ表示されます。
	 * 文字列が、cutNoで指定された数より小さい場合は、そのまま使用されます。
	 *
	 * @og.rev 4.1.1.0 (2008/02/04) cutNo,labelBreak 追加
	 * @og.rev 4.1.2.0 (2008/03/12) LabelVisible.UNDER 処理を追加
	 * @og.rev 4.3.1.1 (2008/08/23) lbl の null参照はずしの対応
	 *
	 * @param category	the category.
	 * @param width  the available width. 
	 * @param edge	the edge on which the axis appears.
	 * @param g2  the graphics device.
	 *
	 * @return A label.
	 */
	protected TextBlock createLabel( final Comparable category, final float width,
									final RectangleEdge edge, final Graphics2D g2) {
		TextBlock label = null ;
		String    lbl   = null;
//		if( category != null && category instanceof String ) {
		if( category instanceof String ) {	// 4.3.1.1 (2008/08/23) instanceof チェックは、nullチェック不要
			lbl = (String)category;
			if( lbl.startsWith( "_" ) ) {
				label = NULL_LABEL;
			}
		}

//		if( lbl.startsWith( "_" ) ) {
//			label = NULL_LABEL;
//		}
//		else if( cutNo > 0 && lbl != null ) {
		if( cutNo > 0 && lbl != null ) {
			if( lbl.length() >= cutNo ) {
				lbl = lbl.substring( 0,cutNo );
			}

			if( ! lbl.equals( breakKey ) ) {
				label = super.createLabel( lbl, width, edge,  g2);
				breakKey = lbl ;
			}
		}
		else {
			if( count % skip == 0 ) {
				label = super.createLabel( category, width, edge,  g2);
			}
			count++;
		}

		if( label == null ) {
			label = NULL_LABEL;
			labelBreak.add( LabelVisible.FALSE );
		}
		else if( label.equals( NULL_LABEL ) ) {
			labelBreak.add( LabelVisible.UNDER );
		}
		else {
			labelBreak.add( LabelVisible.TRUE );
		}

		return label;
	}

	/**
	 * ラベルブレイクするかどうかを返します。
	 *
	 * skip または、cutNo によるラベルの間引き処理で、指定のCategoryAxis
	 * に対するカラム番号を指定する事で、判定値を返します。
	 * 処理が、Label の作成済みかどうかに依存する為、その判定を先に行います。
	 *
	 * @og.rev 4.1.1.0 (2008/02/04) 新規追加
	 *
	 * @param column int 
	 * @return boolean ラベルブレイクするかどうか(true:する)
	 */
	protected boolean isLabelBreak( final int column ) {
		return labelBreak == null ||
				labelBreak.size() <= column ||
				labelBreak.get( column ).booleanValue() ;
	}

	/**
	 * ITEM ラベル（各データの設定値の説明用の値）を表示するかどうかを返します。
	 *
	 * ラベルの先頭に、アンダースコアがついたラベルは、ラベルの表示と
	 * ItemLabel の表示を抑止します。(false)
	 * それ以外のラベルは、表示する(true) を返します。
	 * 処理が、Label の作成済みかどうかに依存する為、その判定を先に行います。
	 *
	 * @og.rev 4.1.2.0 (2008/03/12) 新規追加
	 *
	 * @param column int 
	 * @return boolean ITEM ラベルを表示するかどうか(true:する)
	 */
	protected boolean isViewItemLabel( final int column ) {
		boolean flag = labelBreak == null ||
						labelBreak.size() <= column ||
						labelBreak.get( column ) != LabelVisible.UNDER ;

		if( flag && isItemLabelLastVisible && labelBreak.size() -1 != column ) {
			flag = false;
		}

		return flag;
	}

	/**
	 * ドメイン(横軸)のカテゴリ単位のライン(縦線)の描画位置を返します。
	 *
	 * この位置は、labelBreak が存在しないか、または、ブレークするときのみ
	 * 値を返します。これにより、ライン(縦線)の位置を、グラフの中心から
	 * ずらす事が可能になります。
	 * また、labelBreak により、ラベルを描画しない場合は、線の位置を、０ に
	 * 設定する事で、画面から見えなくします。
	 *
	 * @param anchor  the anchor point.
	 * @param category	the category index.
	 * @param categoryCount  the category count.
	 * @param area	the data area.
	 * @param edge	the location of the axis.
	 * 
	 * @return The coordinate.
	 */
	public double getCategoryJava2DCoordinate( final CategoryAnchor anchor, 
											   final int category, 
											   final int categoryCount, 
											   final Rectangle2D area,
											   final RectangleEdge edge) {

		final double result ;

		// labelBreak が存在しないか、または、ブレークするときのみ値を返す。
		if( isLabelBreak( category ) ) {
			result = super.getCategoryJava2DCoordinate(
							anchor,category,categoryCount,area,edge
					) ;
		}
		else {
			result = 0;
		}
		return result ;
	}

	/**
	 * Tests this axis for equality with an arbitrary object.
	 *
	 * @og.rev 4.1.0.1(2008/01/19) 新規追加
	 *
	 * @param obj  the object (<code>null</code> permitted).
	 *
	 * @return A boolean.
	 */
	public boolean equals( final Object obj ) {
		if( obj == null ) {
			return false;
		}
		if( obj == this ) {
			return true;
		}
		if( !(obj instanceof HybsCategoryAxis) ) {
			return false;
		}
		if( !super.equals(obj) ) {
			return false;
		}
		HybsCategoryAxis that = (HybsCategoryAxis) obj;
		if( that.count != count || that.skip != skip || that.cutNo != cutNo ) {
			return false;
		}

		return true;
	}

	/**
	 * Returns a hash code for this object.
	 * 
	 * @og.rev 4.1.0.1(2008/01/19) 新規追加
	 *
	 * @return A hash code.
	 */
	public int hashCode() {
		return ( getLabel() + ":" + count + ":" + skip + ":" + cutNo ).hashCode();
	}
}
