/*
 * Copyright (c) 2006-2009 OrangeSignal.com All rights reserved.
 * 
 * これは Apache ライセンス Version 2.0 (以下、このライセンスと記述) に
 * 従っています。このライセンスに準拠する場合以外、このファイルを使用
 * してはなりません。このライセンスのコピーは以下から入手できます。
 * 
 * http://www.apache.org/licenses/LICENSE-2.0.txt
 * 
 * 適用可能な法律がある、あるいは文書によって明記されている場合を除き、
 * このライセンスの下で配布されているソフトウェアは、明示的であるか暗黙の
 * うちであるかを問わず、「保証やあらゆる種類の条件を含んでおらず」、
 * 「あるがまま」の状態で提供されるものとします。
 * このライセンスが適用される特定の許諾と制限については、このライセンス
 * を参照してください。
 */

package jp.sf.orangesignal.chart.axis;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;

import jp.sf.orangesignal.chart.ChartColor;
import jp.sf.orangesignal.chart.FontConstants;
import jp.sf.orangesignal.chart.util.StringManager;

/**
 * 軸情報の抽象クラスです。
 * 
 * @author 杉澤 浩二
 */
public abstract class Axis {

	/**
	 * デフォルトの目盛りの最大項目数です。
	 */
	public static final int DEFAULT_TICK_COUNT = 2;

	/**
	 * デフォルトの目盛りラベルの間隔です。
	 */
	public static final double DEFAULT_GAP = 2.0D;

	/**
	 * 目盛りラベルのフォントです。
	 */
	public static final Font FONT = FontConstants.FONT_INDEX_SMALL;

	/**
	 * デフォルトの目盛り線から外側へ伸びる項目線の長さです。
	 */
	public static final int DEFAULT_TICK_MARK_OUTSIDE_LENGTH = 4;

	/**
	 * デフォルトの目盛り線から内側へ伸びる項目線の長さです。
	 */
	public static final int DEFAULT_TICK_MARK_INSIDE_LENGTH = 3;

	/**
	 * デフォルトの余白の長さです。
	 */
	public static final int DEFAULT_TICK_WIDTH_MARGIN = 2;

	// ---------------------------------------- 方向

	/**
	 * 軸の方向を保持します。
	 */
	private Orientation orientation = Orientation.VERTICAL;

	// ---------------------------------------- 目盛り

	/**
	 * 目盛りの最大項目数を保持します。
	 */
	private int tickCount = DEFAULT_TICK_COUNT;

	/**
	 * 目盛りラベルの間隔を保持します。
	 */
	private double gap = DEFAULT_GAP;

	// ---------------------------------------- 領域計算

	/**
	 * 目盛り線から外側へ伸びる項目線の長さです。
	 */
	private int tickMarkOutsideLength = DEFAULT_TICK_MARK_OUTSIDE_LENGTH;

	/**
	 * 目盛り線から内側へ伸びる項目線の長さです。
	 */
	private int tickMarkInsideLength = DEFAULT_TICK_MARK_INSIDE_LENGTH;

	/**
	 * 余白の長さです。
	 */
	private int tickWidthMargin = DEFAULT_TICK_WIDTH_MARGIN;

	// ----------------------------------------

	/**
	 * グリッド線の色を表します。
	 */
	private Color glidlineColor = ChartColor.LIGHT_GRAY;

	/**
	 * グリッド線の線情報を表します。
	 */
	private Stroke glidlineStroke = new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0F, new float[]{ 2F, 2F }, 0.0F);

	/**
	 * 目盛り線の色を表します。
	 */
	private Color tickMarkLineColor = Color.BLACK;

	/**
	 * 目盛り線の線情報を表します。
	 */
	private Stroke tickMarkLineStroke = new BasicStroke(1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);

	// ------------------------------------------------------------------------

	/**
	 * デフォルトコンストラクタです。
	 */
	protected Axis() {}

	// ------------------------------------------------------------------------
	// setter/getter

	/**
	 * 軸の方向を返します。
	 * 
	 * @return 軸の方向
	 */
	public Orientation getOrientation() { return orientation; }

	/**
	 * 軸の方向を設定します。
	 * 
	 * @param orientation 軸の方向
	 */
	public void setOrientation(final Orientation orientation) { this.orientation = orientation; }

	/**
	 * 目盛りの最大項目数を返します。
	 * 
	 * @return 目盛りの最大項目数
	 */
	public int getTickCount() { return tickCount; }

	/**
	 * 目盛りの最大項目数を設定します。
	 * 
	 * @param tickCount 目盛りの最大項目数
	 */
	public void setTickCount(final int tickCount) { this.tickCount = tickCount; }

	/**
	 * 目盛りラベルの間隔を返します。
	 * 
	 * @return 目盛りラベルの間隔
	 */
	public double getGap() { return gap; }

	/**
	 * 目盛りラベルの間隔を設定します。
	 * 
	 * @param gap 目盛りラベルの間隔
	 */
	public void setGap(final double gap) { this.gap = gap; }

	/**
	 * 目盛り線から外側へ伸びる項目線の長さを返します。
	 * 
	 * @return 目盛り線から外側へ伸びる項目線の長さ
	 */
	public int getTickMarkOutsideLength() { return tickMarkOutsideLength; }

	/**
	 * 目盛り線から外側へ伸びる項目線の長さを設定します。
	 * 
	 * @param length 目盛り線から外側へ伸びる項目線の長さ
	 */
	public void setTickMarkOutsideLength(final int length) { this.tickMarkOutsideLength = length; }

	/**
	 * 目盛り線から内側へ伸びる項目線の長さを返します。
	 * 
	 * @return 目盛り線から内側へ伸びる項目線の長さ
	 */
	public int getTickMarkInsideLength() { return tickMarkInsideLength; }

	/**
	 * 目盛り線から内側へ伸びる項目線の長さを設定します。
	 * 
	 * @param length 目盛り線から内側へ伸びる項目線の長さ
	 */
	public void setTickMarkInsideLength(final int length) { this.tickMarkInsideLength = length; }

	/**
	 * 余白の長さを返します。
	 * 
	 * @return 余白の長さ
	 */
	public int getTickWidthMargin() { return tickWidthMargin; }

	/**
	 * 余白の長さを設定します。
	 * 
	 * @param tickWidthMargin 余白の長さ
	 */
	public void setTickWidthMargin(final int tickWidthMargin) { this.tickWidthMargin = tickWidthMargin; }

	/**
	 * グリッド線の色を返します。
	 * 
	 * @return グリッド線の色
	 */
	public Color getGlidlineColor() { return glidlineColor; }

	/**
	 * グリッド線の色を設定します。
	 * 
	 * @param color グリッド線の色
	 */
	public void setGlidlineColor(final Color color) { this.glidlineColor = color; }

	/**
	 * グリッド線の線情報を返します。
	 * 
	 * @return グリッド線の線情報
	 */
	public Stroke getGlidlineStroke() { return glidlineStroke; }

	/**
	 * グリッド線の線情報を設定します。
	 * 
	 * @param stroke グリッド線の線情報
	 */
	public void setGlidlineStroke(final Stroke stroke) { this.glidlineStroke = stroke; }

	/**
	 * 目盛り線の色を返します。
	 * 
	 * @return 目盛り線の色
	 */
	public Color getTickMarkLineColor() { return tickMarkLineColor; }

	/**
	 * 目盛り線の色を設定します。
	 * 
	 * @param color 目盛り線の色
	 */
	public void setTickMarkLineColor(final Color color) { this.tickMarkLineColor = color; }

	/**
	 * 目盛り線の線情報を返します。
	 * 
	 * @return 目盛り線の線情報
	 */
	public Stroke getTickMarkLineStroke() { return tickMarkLineStroke; }

	/**
	 * 目盛り線の線情報を設定します。
	 * 
	 * @param stroke 目盛り線の線情報
	 */
	public void setTickMarkLineStroke(Stroke stroke) { this.tickMarkLineStroke = stroke; }

	// ------------------------------------------------------------------------

	/**
	 * 目盛りの基本的な幅です。
	 */
	protected int getBaseTickWidth() { return tickMarkOutsideLength + tickMarkInsideLength + tickWidthMargin; }

	/**
	 * この軸が必要とする最大の縦幅または横幅を計算して返します。
	 * 
	 * @param g Graphics オブジェクト
	 * @return 最大の幅
	 */
	public int getSpace(final Graphics g) {
		final FontMetrics fm = g.getFontMetrics(FONT);

		if (this.orientation == Orientation.VERTICAL)
			return fm.stringWidth(StringManager.getString("axis.sample")) + getBaseTickWidth();
		else if (this.orientation == Orientation.HORIZONTAL)
			return fm.getHeight() + getBaseTickWidth();

		throw new RuntimeException();
	}

	// ------------------------------------------------------------------------

	/**
	 * グリッド線を描画します。
	 * 
	 * @param g2 Graphics2D オブジェクト
	 * @param x1 X座標1
	 * @param y1 Y座標1
	 * @param x2 X座標2
	 * @param y2 Y座標2
	 */
	protected void drawGlidline(final Graphics2D g2, final double x1, final double y1, final double x2, final double y2) {
		g2.setColor(glidlineColor);
		g2.setStroke(glidlineStroke);
		g2.draw(new Line2D.Double(x1, y1, x2, y2));
	}

	/**
	 * 軸線を描画します。
	 * 
	 * @param g2 Graphics2D オブジェクト
	 * @param x1 X座標1
	 * @param y1 Y座標1
	 * @param x2 X座標2
	 * @param y2 Y座標2
	 */
	protected void drawAxisLine(final Graphics2D g2, final double x1, final double y1, final double x2, final double y2) {
		g2.setColor(tickMarkLineColor);
		g2.setStroke(tickMarkLineStroke);
		g2.draw(new Line2D.Double(x1, y1, x2, y2));
	}

	/**
	 * 目盛りを描画します。
	 * 
	 * @param g2 Graphics2D オブジェクト
	 * @param x1 X座標1
	 * @param y1 Y座標1
	 * @param x2 X座標2
	 * @param y2 Y座標2
	 */
	protected void drawTickMark(final Graphics2D g2, final double x1, final double y1, final double x2, final double y2) {
		g2.setColor(tickMarkLineColor);
		g2.setStroke(tickMarkLineStroke);
		g2.draw(new Line2D.Double(x1, y1, x2, y2));
	}

	/**
	 * トレース線を描画します。
	 * 
	 * @param g2 Graphics2D オブジェクト
	 * @param x1 X座標1
	 * @param y1 Y座標1
	 * @param x2 X座標2
	 * @param y2 Y座標2
	 */
	protected void drawTraceLine(final Graphics2D g2, final double x1, final double y1, final double x2, final double y2) {
		g2.setXORMode(Color.ORANGE);
		g2.setStroke(tickMarkLineStroke);
		g2.draw(new Line2D.Double(x1, y1, x2, y2));
		g2.setPaintMode();
	}

	// ------------------------------------------------------------------------

	/**
	 * 目盛りリストを更新します。
	 */
	public abstract void refreshTicks();

	/**
	 * 目盛りリストを更新します。
	 * 
	 * @param g2 Graphics2D オブジェクト
	 * @param chartArea 描画領域
	 */
	public abstract void refreshTicks(Graphics2D g2, Rectangle2D chartArea);

}
