/*
 * 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.ui.canvas;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.util.Date;

import jp.sf.orangesignal.chart.ChartColor;
import jp.sf.orangesignal.chart.FontConstants;
import jp.sf.orangesignal.chart.ChartSettings;
import jp.sf.orangesignal.chart.ChartSettings.TimeSeriesSettings;
import jp.sf.orangesignal.chart.data.AntiWatchChartDataset;
import jp.sf.orangesignal.chart.data.ChartDataset;
import jp.sf.orangesignal.chart.data.StepChartDataset;
import jp.sf.orangesignal.chart.data.TimeSeriesChartDataset;
import jp.sf.orangesignal.chart.data.VolumePriceHistogram;
import jp.sf.orangesignal.chart.event.ChartEvent;
import jp.sf.orangesignal.chart.event.ChartScreenEvent;
import jp.sf.orangesignal.chart.ui.BandType;
import jp.sf.orangesignal.chart.ui.DatasetType;
import jp.sf.orangesignal.chart.ui.Icons;
import jp.sf.orangesignal.chart.ui.UpDownColorType;
import jp.sf.orangesignal.chart.ui.VolumeType;
import jp.sf.orangesignal.chart.ui.screen.SideScreen;
import jp.sf.orangesignal.chart.util.DrawUtils;
import jp.sf.orangesignal.chart.util.StringManager;

/**
 * 指標キャンバスを提供します。
 * 
 * @author 杉澤 浩二
 */
public class IndexCanvas extends AbstractChartCanvas {

	private static final long serialVersionUID = -6936061295835378348L;

	private static final int DEFAULT_PRICE_PRECISION = 0;
	private static final int DEFAULT_RATIO_PRECISION = 4;
	private static final int DEFAULT_INDEX_PRECISION = 2;
	private static final int RATE_INDEX_PRECISION = 6;

	// ---------------------------------------- 書式

	/**
	 * 整数表記の書式を保持します。
	 */
	private static final String FORMAT_DECIMAL = StringManager.getString("format.number", 0);
	private static final String FORMAT_ENVELOPE = StringManager.getString("format.envelope");

	/**
	 * 4本値の書式を保持します。
	 */
	private String formatPrice = StringManager.getString("format.number", DEFAULT_PRICE_PRECISION);

	/**
	 * 前日比(値幅)の書式を保持します。
	 */
	private String formatChange = StringManager.getString("format.change", DEFAULT_PRICE_PRECISION);

	/**
	 * 前日比(率)の書式を保持します。
	 */
	private String formatPercentChange = StringManager.getString("format.pchange", DEFAULT_INDEX_PRECISION);

	/**
	 * 信用倍率の書式を保持します。
	 */
	private String formatRatio = StringManager.getString("format.number", DEFAULT_RATIO_PRECISION);

	/**
	 * 指標の書式を保持します。
	 */
	private String formatIndex = StringManager.getString("format.number", DEFAULT_INDEX_PRECISION);

	/**
	 * 指標の書式を保持します。
	 */
	private String formatRateIndex = StringManager.getString("format.number", RATE_INDEX_PRECISION);

	// ---------------------------------------- データ

	/**
	 * データセットを保持します。
	 */
	private ChartDataset dataset = null;

	/**
	 * 足単位を保持します。
	 */
	private DatasetType type;

	/**
	 * 開始位置を保持します。
	 */
	private int start;

	/**
	 * 範囲を保持します。
	 */
	private int period;

	/**
	 * 座標を保持します。
	 */
	private int position;

	/**
	 * 設定情報を保持します。
	 */
	private ChartSettings settings;

	/**
	 * 付属情報を保持します。
	 */
	private Object info;

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

	/**
	 * コンストラクタです。
	 * 
	 * @param icons アイコン情報
	 */
	public IndexCanvas(final Icons icons) {
		setBackground(Color.WHITE);
		setPreferredSize(new Dimension(SideScreen.SIDE_SCREEN_WIDTH, 0));
	}

	@Override
	public void switchDataset(final ChartEvent e) {
		// メンバーへ保存します。
		dataset = e.getDataset();
		type = e.getType();
		if (!e.isIgnoreStart())
			start = e.getStart();
		period = e.getPeriod();
		settings = e.getSettings();

		// 少数点以下表示桁数から数値書式を構築します。
		final int precision = settings.precision;
		formatPrice = StringManager.getString("format.number", Math.max(precision, DEFAULT_PRICE_PRECISION));
		formatChange = StringManager.getString("format.change", Math.max(precision, DEFAULT_PRICE_PRECISION));
		formatPercentChange = StringManager.getString("format.pchange", Math.max(precision, DEFAULT_INDEX_PRECISION));
		formatRatio = StringManager.getString("format.number", Math.max(precision, DEFAULT_RATIO_PRECISION));
		formatIndex = StringManager.getString("format.number", Math.max(precision, DEFAULT_INDEX_PRECISION));
		formatRateIndex = StringManager.getString("format.number", Math.max(precision, RATE_INDEX_PRECISION));
	}

	public void setup(final ChartScreenEvent e) {
		start = e.getStart();
		position = e.getPosition();
		info = e.getInfo();
		repaint();
	}

	@Override
	public void setStart(final int start) {
		this.start = start;
	}

	/**
	 * 1行の基準の高さを保持します。
	 */
	private int ascent;

	@Override
	public void draw(final Graphics2D g2) {
		if (this.dataset == null || this.dataset.getCount() <= 0)
			return;

		// 1行の基準の高さを取得します。
		this.ascent = g2.getFontMetrics(FontConstants.FONT_MESSAGE).getAscent();

		int y = 0;

		// 時系列
		if (this.dataset instanceof TimeSeriesChartDataset) {
			final TimeSeriesChartDataset dataset = (TimeSeriesChartDataset) this.dataset;
			final Date startDate = dataset.date[this.start];
			final Date endDate = dataset.date[this.start + this.period - 1];
			if (startDate == null && endDate == null)
				return;

			// 日付範囲
			y = y + drawDateRange(g2, y, startDate, endDate);
			y = y + drawHorizontalLine(g2, y);
			// 日付
			y = y + drawDate(g2, y, StringManager.getString("_date"), dataset.date[position]);
			// 4本値
			y = y + drawPrice(g2, y, StringManager.getString("_open"), dataset.open[position]);
			y = y + drawPrice(g2, y, StringManager.getString("_high"), dataset.high[position]);
			y = y + drawPrice(g2, y, StringManager.getString("_low"), dataset.low[position]);
			y = y + drawPrice(g2, y, StringManager.getString("_close"), dataset.close[position]);

			// 前日比
			Color changeColor = Color.BLACK;
			if (dataset.change[position] != null) {
				final UpDownColorType colors = this.settings.updownLineColors;
				if (dataset.change[position].doubleValue() > 0)
					changeColor = colors.getUpLineColor();
				else if (dataset.change[position].doubleValue() < 0)
					changeColor = colors.getDownLineColor();
			}

			y = y + drawChange(g2, y, this.type.getChangeLabel(), changeColor, dataset.change[this.position], dataset.percentChange[this.position]);

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

			final TimeSeriesSettings settings = this.settings.timeSeries;

			// 出来高
			if (settings.volumeType != VolumeType.NONE) {
				// 出来高
				if (dataset.volume != null)
					y = y + drawDecimal(g2, y, settings.volumeColor2, StringManager.getString("_volume"), dataset.volume[this.position]);
				// 信用残
				if (settings.volumeType == VolumeType.MARGIN) {
					// 信用買残
					if (dataset.bought != null)
						y = y + drawDecimal(g2, y, settings.boughtColor, StringManager.getString("_bought"), dataset.bought[this.position]);
					// 信用売残
					if (dataset.sold != null)
						y = y + drawDecimal(g2, y, settings.soldColor, StringManager.getString("_sold"), dataset.sold[this.position]);
				}
			}

			y = y + drawHorizontalLine(g2, y);

			if (settings.avgprice) {
				if (dataset.avgprice != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_avgprice"), dataset.avgprice[this.position]);
			}
			if (settings.mp) {
				if (dataset.mp != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_mp"), dataset.mp[this.position]);
			}
			// ティピカル・プライス
			if (settings.tp) {
				if (dataset.tp != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_tp"), dataset.tp[this.position]);
			}
			// 加重終値
			if (settings.wc) {
				if (dataset.wc != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_wc"), dataset.wc[this.position]);
			}

			// トレンド系

			// Zig Zag
			if (settings.zigzag && dataset.zigzag != null) {
				y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_zigzag"), dataset.zigzag[position], settings.zigzag_rate);
			}

			// 移動平均線
			if (settings.ma) {
				if (dataset.ma1 != null)
					y = y + drawIndex(g2, y, settings.ma1Color, StringManager.getString("_ma"), dataset.ma1[this.position], settings.ma1Period);
				if (dataset.ma2 != null)
					y = y + drawIndex(g2, y, settings.ma2Color, StringManager.getString("_ma"), dataset.ma2[this.position], settings.ma2Period);
				if (dataset.ma_bias != null)
					y = y + drawIndex(g2, y, Color.BLACK, StringManager.getString("_ma.bias"), dataset.ma_bias[this.position]);
			}

			if (settings.bandType == BandType.ENVELOPE) {
				// エンベロープ
				if (dataset.envelope_upper2 != null)
					y = y + drawIndex(g2, y, settings.envelopeUpper2Color, String.format(FORMAT_ENVELOPE, settings.envelopeRate * 2), dataset.envelope_upper2[this.position]);
				if (dataset.envelope_upper1 != null)
					y = y + drawIndex(g2, y, settings.envelopeUpper1Color, String.format(FORMAT_ENVELOPE, settings.envelopeRate), dataset.envelope_upper1[this.position]);
				if (dataset.envelope != null)
					y = y + drawIndex(g2, y, settings.envelopeColor, StringManager.getString("_ma"), dataset.envelope[this.position], settings.envelopePeriod);
				if (dataset.envelope_lower1 != null)
					y = y + drawIndex(g2, y, settings.envelopeLower1Color, String.format(FORMAT_ENVELOPE, -settings.envelopeRate), dataset.envelope_lower1[this.position]);
				if (dataset.envelope_lower2 != null)
					y = y + drawIndex(g2, y, settings.envelopeLower2Color, String.format(FORMAT_ENVELOPE, -settings.envelopeRate * 2), dataset.envelope_lower2[this.position]);
			} else if (settings.bandType == BandType.HIGH_LOW_MOVING_AVERAGE) {
				// チャネルシステム
				if (dataset.hma != null)
					y = y + drawIndex(g2, y, settings.ma1Color, StringManager.getString("_support"), dataset.hma[this.position], settings.hmaPeriod);
				if (dataset.lma != null)
					y = y + drawIndex(g2, y, settings.ma2Color, StringManager.getString("_resistance"), dataset.lma[this.position], settings.lmaPeriod);
			} else if (settings.bandType == BandType.DONCHIAN) {
				// ドンチャンズ
				if (dataset.donchian_upper != null)
					y = y + drawIndex(g2, y, settings.ma1Color, StringManager.getString("_support"), dataset.donchian_upper[this.position], settings.donchianPeriod);
				if (dataset.donchian_lower != null)
					y = y + drawIndex(g2, y, settings.ma2Color, StringManager.getString("_resistance"), dataset.donchian_lower[this.position], settings.donchianPeriod);
			} else if (settings.bandType == BandType.BOLLINGER_BANDS) {
				// ボリンジャーバンド
				if (dataset.bb_upper2 != null)
					y = y + drawIndex(g2, y, settings.bbUpper2Color, StringManager.getString("_bb.upper2"), dataset.bb_upper2[this.position]);
				if (dataset.bb_upper1 != null)
					y = y + drawIndex(g2, y, settings.bbUpper1Color, StringManager.getString("_bb.upper1"), dataset.bb_upper1[this.position]);
				if (dataset.bb_tpma != null)
					y = y + drawIndex(g2, y, settings.bbColor, StringManager.getString("_bb.tpma"), dataset.bb_tpma[this.position], settings.bbPeriod);
				if (dataset.bb_lower1 != null)
					y = y + drawIndex(g2, y, settings.bbLower1Color, StringManager.getString("_bb.lower1"), dataset.bb_lower1[this.position]);
				if (dataset.bb_lower2 != null)
					y = y + drawIndex(g2, y, settings.bbLower2Color, StringManager.getString("_bb.lower2"), dataset.bb_lower2[this.position]);
			} else if (settings.bandType == BandType.PARABOLIC_TIME_PRICE) {
				// パラボリック
				if (dataset.sar != null)
					y = y + drawIndex(g2, y, Color.BLACK, StringManager.getString("_parabolic"), dataset.sar[this.position]);
			} else if (settings.bandType == BandType.VIX) {
				// ボラティリティ・システム
				if (dataset.vi_upper != null)
					y = y + drawIndex(g2, y, settings.viUpperColor, StringManager.getString("_vix.high"), dataset.vi_upper[this.position], settings.atrPeriod);
				if (dataset.vi_lower != null)
					y = y + drawIndex(g2, y, settings.viLowerColor, StringManager.getString("_vix.low"), dataset.vi_lower[this.position], settings.atrPeriod);
			} else if (settings.bandType == BandType.PIVOT) {
				// ピボット
				if (dataset.pivot_hbop != null)
					y = y + drawIndex(g2, y, settings.pivotHBOPColor, StringManager.getString("_pivot.hbop"), dataset.pivot_hbop[this.position]);
				if (dataset.pivot_s2 != null)
					y = y + drawIndex(g2, y, settings.pivotS2Color, StringManager.getString("_pivot.s2"), dataset.pivot_s2[this.position]);
				if (dataset.pivot_s1 != null)
					y = y + drawIndex(g2, y, settings.pivotS1Color, StringManager.getString("_pivot.s1"), dataset.pivot_s1[this.position]);
				if (dataset.pivot != null)
					y = y + drawIndex(g2, y, settings.pivotColor, StringManager.getString("_pivot"), dataset.pivot[this.position]);
				if (dataset.pivot_b1 != null)
					y = y + drawIndex(g2, y, settings.pivotB1Color, StringManager.getString("_pivot.b1"), dataset.pivot_b1[this.position]);
				if (dataset.pivot_b2 != null)
					y = y + drawIndex(g2, y, settings.pivotB2Color, StringManager.getString("_pivot.b2"), dataset.pivot_b2[this.position]);
				if (dataset.pivot_lbop != null)
					y = y + drawIndex(g2, y, settings.pivotLBOPColor, StringManager.getString("_pivot.lbop"), dataset.pivot_lbop[this.position]);
			} else if (settings.bandType == BandType.ICHIMOKU) {
				// 一目均衡表
				if (dataset.kijun != null)
					y = y + drawIndex(g2, y, settings.ichimokuKijunColor, StringManager.getString("_ichimoku.kijun"), dataset.kijun[this.position]);
				if (dataset.tenkan != null)
					y = y + drawIndex(g2, y, settings.ichimokuTenkanColor, StringManager.getString("_ichimoku.tenkan"), dataset.tenkan[this.position]);
				if (dataset.senkou1 != null)
					y = y + drawIndex(g2, y, settings.ichimokuSenkou1Color, StringManager.getString("_ichimoku.senkou1"), dataset.senkou1[this.position]);
				if (dataset.senkou2 != null)
					y = y + drawIndex(g2, y, settings.ichimokuSenkou2Color, StringManager.getString("_ichimoku.senkou2"), dataset.senkou2[this.position]);
				if (dataset.chikou != null)
					y = y + drawIndex(g2, y, settings.ichimokuChikouColor, StringManager.getString("_ichimoku.chikou"), dataset.chikou[this.position]);
			} else if (settings.bandType == BandType.MESA) {
				// MESA 適応移動平均
				if (dataset.mama != null)
					y = y + drawIndex(g2, y, settings.ma1Color, StringManager.getString("_mesa.mama"), dataset.mama[this.position]);
				if (dataset.fama != null)
					y = y + drawIndex(g2, y, settings.ma2Color, StringManager.getString("_mesa.fama"), dataset.fama[this.position]);
			}
			// 出来高移動平均
			if (settings.volumeType == VolumeType.MOVING_AVERAGE) {
				if (dataset.vma1 != null)
					y = y + drawIndex(g2, y, settings.ma1Color, StringManager.getString("_vma"), dataset.vma1[this.position], settings.vma1Period);
				if (dataset.vma2 != null)
					y = y + drawIndex(g2, y, settings.ma2Color, StringManager.getString("_vma"), dataset.vma2[this.position], settings.vma2Period);
			}

			// オシレーター系

			// アルーン
			if (settings.aroon) {
				if (dataset.aroonUp != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_aroon.up"), dataset.aroonUp[this.position], settings.aroonPeriod);
				if (dataset.aroonDown != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_aroon.down"), dataset.aroonDown[this.position], settings.aroonPeriod);
				if (dataset.aroon != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_aroon"), dataset.aroon[this.position], settings.aroonPeriod);
			}
			// ATR
			if (settings.atr && dataset.atr != null) y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_atr"), dataset.atr[position], settings.atr_period);
			// NATR
			if (settings.natr && dataset.natr != null) y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_natr"), dataset.natr[position], settings.natr_period);
			// BMP
			if (settings.bop && dataset.bop != null) y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_bmp"), dataset.bop[position]);
			// チャイキンズ・ボラティリティ
			if (settings.chv) {
				if (dataset.chv != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_cv"), dataset.chv[this.position], settings.chv_period1, settings.chv_period2);
			}
			// CCI
			if (settings.cci) {
				if (dataset.cci != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_cci"), dataset.cci[this.position], settings.cciPeriod);
			}
			// CMF
			if (settings.cmf) {
				if (dataset.cmf != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_cmf"), dataset.cmf[this.position], settings.cmf_period);
			}
			// CMO
			if (settings.cmo) {
				if (dataset.cmo != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_cmo"), dataset.cmo[this.position], settings.cmo_period);
			}
			// コポック
			if (settings.coppock) {
				if (dataset.coppock != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_coppock"), dataset.coppock[this.position], settings.coppockPeriod);
			}
			// DPO
			if (settings.dpo) {
				if (dataset.dpo != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_dpo"), dataset.dpo[this.position], settings.dpoPeriod);
			}
			// DMI
			if (settings.dmi) {
				if (dataset.pdi != null)
					y = y + drawIndex(g2, y, settings.pdiColor, StringManager.getString("_pdi"), dataset.pdi[this.position], settings.diPeriod);
				if (dataset.mdi != null)
					y = y + drawIndex(g2, y, settings.mdiColor, StringManager.getString("_mdi"), dataset.mdi[this.position], settings.diPeriod);
				if (dataset.adx != null)
					y = y + drawIndex(g2, y, settings.adxColor, StringManager.getString("_adx"), dataset.adx[this.position], settings.adxPeriod);
			}
			// EMV
			if (settings.emv && dataset.emv != null) y = y + drawRateIndex(g2, y, ChartColor.BLACK, StringManager.getString("_emv"), dataset.emv[position], settings.emv_period);
			// ヒストリカル・ボラティリティ
			if (settings.hv) {
				if (dataset.hv != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_hv"), dataset.hv[this.position], settings.hvPeriod);
			}
			// 移動平均乖離率
			if (settings.kairi) {
				if (dataset.kairi != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_bias"), dataset.kairi[this.position], settings.biasPeriod);
			}
			// マス・インデクッス(MI)
			if (settings.mi) {
				if (dataset.mi != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_mi"), dataset.mi[this.position], settings.miMaPeriod, settings.miSumPeriod);
			}
			// モメンタム
			if (settings.mom) {
				if (dataset.mom != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_momentum"), dataset.mom[this.position], settings.mom_period);
			}
			// MFI
			if (settings.mfi) {
				if (dataset.mfi != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_mfi"), dataset.mfi[this.position], settings.mfiPeriod);
			}
			// MACD
			if (settings.macd) {
				if (dataset.macd != null)
					y = y + drawIndex(g2, y, settings.macdColor, StringManager.getString("_macd"), dataset.macd[this.position], settings.macdMa1Period, settings.macdMa2Period);
				if (dataset.macdSignal != null)
					y = y + drawIndex(g2, y, settings.macdSignalColor, StringManager.getString("_signal"), dataset.macdSignal[this.position], settings.macdSignalPeriod);
			}
			// PAIN
			if (settings.pain && dataset.pain != null) y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_pain"), dataset.pain[position]);
			// PCR
			if (settings.pcr && dataset.pcr != null) y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_pcr"), dataset.pcr[position], settings.pcr_period);
			// プライス・オシレータ(APO)
			if (settings.apo) {
				if (dataset.apo != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_apo"), dataset.apo[this.position], settings.apoMa1Period, settings.apoMa2Period);
			}
			// プライス・オシレータ(PPO)
			if (settings.ppo) {
				if (dataset.ppo != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_ppo"), dataset.ppo[this.position], settings.ppoMa1Period, settings.ppoMa2Period);
			}
			// サイコロジカルライン
			if (settings.psy) {
				if (dataset.psy != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_psychological"), dataset.psy[this.position], settings.psy_period);
			}
			// QSTICK
			if (settings.qstick) {
				if (dataset.qstick != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_qstick"), dataset.qstick[this.position], settings.qstick_period);
			}
			// RCI
			if (settings.rci) {
				if (dataset.rci != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_rci"), dataset.rci[this.position], settings.rciPeriod);
			}
			// ROC
			if (settings.roc) {
				if (dataset.roc != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_roc"), dataset.roc[this.position], settings.rocPeriod);
			}
			// RSI
			if (settings.rsi && dataset.rsi != null) y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_rsi"), dataset.rsi[position], settings.rsiPeriod);
			// RVI
			if (settings.rvi && dataset.rvi != null) y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_rvi"), dataset.rvi[position], settings.rvi_period);
			// 強弱レシオ(篠原レシオ)
			if (settings.shinohara) {
				if (dataset.shinohara_a != null)
					y = y + drawIndex(g2, y, settings.oscillatorColor1, StringManager.getString("_shinohara.a"), dataset.shinohara_a[position], settings.shinoharaPeriod);
				if (dataset.shinohara_b != null)
					y = y + drawIndex(g2, y, settings.oscillatorColor2, StringManager.getString("_shinohara.b"), dataset.shinohara_b[position], settings.shinoharaPeriod);
			}
			// ストキャスティクス
			if (settings.srv) {
				if (dataset.srvK != null)
					y = y + drawIndex(g2, y, settings.oscillatorColor1, StringManager.getString("_srv.k"), dataset.srvK[this.position], settings.srvKPeriod);
				if (dataset.srvD != null)
					y = y + drawIndex(g2, y, settings.oscillatorColor2, StringManager.getString("_srv.d"), dataset.srvD[this.position], settings.srvDPeriod);
				if (dataset.srvSD != null)
					y = y + drawIndex(g2, y, settings.oscillatorColor3, StringManager.getString("_srv.sd"), dataset.srvSD[this.position]);
			}
			// TII
			if (settings.tii) {
				if (dataset.tii != null)
					y = y + drawIndex(g2, y, settings.oscillatorColor1, StringManager.getString("_tii"), dataset.tii[this.position], settings.tii_period);
			}
			// トリックス(Trix)
			if (settings.trix) {
				if (dataset.trix != null)
					y = y + drawIndex(g2, y, settings.macdColor, StringManager.getString("_trix"), dataset.trix[this.position], settings.trixPeriod);
				if (dataset.trixSignal != null)
					y = y + drawIndex(g2, y, settings.macdSignalColor, StringManager.getString("_signal"), dataset.trixSignal[this.position], settings.trixSignalPeriod);
			}
			// TSI
			if (settings.tsi) {
				if (dataset.tsi != null)
					y = y + drawIndex(g2, y, settings.macdColor, StringManager.getString("_tsi"), dataset.tsi[this.position], settings.tsi_period1, settings.tsi_period2);
				if (dataset.tsi_signal != null)
					y = y + drawIndex(g2, y, settings.macdSignalColor, StringManager.getString("_signal"), dataset.tsi_signal[this.position], settings.tsi_signal_period);
			}
			// 究極のオシレーター
			if (settings.ultimate) {
				if (dataset.ultimate != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_ultimate"), dataset.ultimate[this.position], settings.ultimatePeriod);
			}
			// ウィリアムズAD
			if (settings.wad) {
				if (dataset.wad != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_wad"), dataset.wad[this.position]);
			}
			// ウィリアムズ%R
			if (settings.williamsR) {
				if (dataset.williamsR != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_williamsr"), dataset.williamsR[this.position], settings.williamsRPeriod);
			}

			// ADライン
			if (settings.ad) {
				if (dataset.ad != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_ad"), dataset.ad[this.position]);
			}
			// チャイキンズADオシレーター
			if (settings.cho) {
				if (dataset.cho != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_cho"), dataset.cho[this.position]);
			}
			// ボリューム・オシレータ(AVO)
			if (settings.avo) {
				if (dataset.avo != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_avo"), dataset.avo[this.position], settings.avoMa1Period, settings.avoMa2Period);
			}
			// ボリューム・オシレータ(PVO)
			if (settings.pvo) {
				if (dataset.pvo != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_pvo"), dataset.pvo[this.position], settings.pvoMa1Period, settings.pvoMa2Period);
			}
			// 出来高乖離率
			if (settings.vkairi) {
				if (dataset.vkairi != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_vbias"), dataset.vkairi[this.position], settings.vbiasPeriod);
			}
			// ボリュームレシオ1
			if (settings.vr1) {
				if (dataset.vr1 != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_vr1"), dataset.vr1[this.position], settings.vr1Period);
			}
			// ボリュームレシオ2
			if (settings.vr2) {
				if (dataset.vr2 != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_vr2"), dataset.vr2[this.position], settings.vr2Period);
			}
			// 出来高ROC
			if (settings.vroc) {
				if (dataset.vroc != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_vroc"), dataset.vroc[this.position], settings.vrocPeriod);
			}
			// VRSI
			if (settings.vrsi) {
				if (dataset.vrsi != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_vrsi"), dataset.vrsi[this.position], settings.vrsiPeriod);
			}
			// 和光ボリュームレシオ
			if (settings.wvr) {
				if (dataset.wvr != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_wvr"), dataset.wvr[this.position], settings.wvrPeriod);
			}
			// PVI
			if (settings.pvi) {
				if (dataset.pvi != null)
					y = y + drawIndex(g2, y, settings.oscillatorColor3, StringManager.getString("_pvi"), dataset.pvi[this.position]);
				if (dataset.pvi_ma1 != null)
					y = y + drawIndex(g2, y, settings.oscillatorColor1, StringManager.getString("_ma"), dataset.pvi_ma1[this.position], settings.pvi_fast);
				if (dataset.pvi_ma2 != null)
					y = y + drawIndex(g2, y, settings.oscillatorColor2, StringManager.getString("_ma"), dataset.pvi_ma2[this.position], settings.pvi_slow);
			}
			// NVI
			if (settings.nvi) {
				if (dataset.nvi != null)
					y = y + drawIndex(g2, y, settings.oscillatorColor3, StringManager.getString("_nvi"), dataset.nvi[this.position]);
				if (dataset.nvi_ma1 != null)
					y = y + drawIndex(g2, y, settings.oscillatorColor1, StringManager.getString("_ma"), dataset.nvi_ma1[this.position], settings.nvi_fast);
				if (dataset.nvi_ma2 != null)
					y = y + drawIndex(g2, y, settings.oscillatorColor2, StringManager.getString("_ma"), dataset.nvi_ma2[this.position], settings.nvi_slow);
			}
			// OBV
			if (settings.obv) {
				if (dataset.obv != null)
					y = y + drawDecimal(g2, y, ChartColor.BLACK, StringManager.getString("_obv"), dataset.obv[this.position], settings.obvPeriod);
			}

			// 価格帯別出来高
			if (this.info instanceof VolumePriceHistogram) {
				final VolumePriceHistogram vph = (VolumePriceHistogram) this.info;
				y = y + drawPrice(g2, y, StringManager.getString("_vp"), vph.upper);
				y = y + drawPrice(g2, y, StringManager.getString("_vp"), vph.lower);
				y = y + drawDecimal(g2, y, settings.volumeColor2, StringManager.getString("_volume"), vph.volume);
			}

			// 信用倍率
			if (settings.ratio) {
				if (dataset.ratio != null)
					y = y + drawRatio(g2, y, StringManager.getString("_ratio"), dataset.ratio[this.position]);
			}
			// 騰落価格
			if (settings.price_performance) {
				if (dataset.price_performance != null)
					y = y + drawPrice(g2, y, StringManager.getString("_fluctuations.price"), dataset.price_performance[this.position]);
			}
			// 騰落率
			if (settings.percent_performance) {
				if (dataset.percent_performance != null)
					y = y + drawIndex(g2, y, ChartColor.BLACK, StringManager.getString("_fluctuations"), dataset.percent_performance[this.position]);
			}

		// ポイント＆フィギュア、カギ足、練行足、新値足
		} else if (this.dataset instanceof StepChartDataset) {
			final StepChartDataset dataset = (StepChartDataset) this.dataset;
			final Date startDate = dataset.startDate[this.start];
			final Date endDate = dataset.endDate[this.start + this.period - 1];
			if (startDate == null && endDate == null)
				return;

			// 日付範囲
			y = y + drawDateRange(g2, y, startDate, endDate);
			y = y + drawHorizontalLine(g2, y);
			y = y + drawDate(g2, y, StringManager.getString("_high.date"), dataset.highDate[this.position]);
			y = y + drawPrice(g2, y, StringManager.getString("_high"), dataset.high[this.position]);
			y = y + drawDate(g2, y, StringManager.getString("_low.date"), dataset.lowDate[this.position]);
			y = y + drawPrice(g2, y, StringManager.getString("_low"), dataset.low[this.position]);

			if (dataset.point != 0) {
				y = y + drawHorizontalLine(g2, y);
				y = y + drawPrice(g2, y, StringManager.getString("_difference"), dataset.point);
			}

		// 逆ウォッチ曲線
		} else if (this.dataset instanceof AntiWatchChartDataset) {
			final AntiWatchChartDataset dataset = (AntiWatchChartDataset) this.dataset;
			final Date startDate = dataset.date[this.start];
			final Date endDate = dataset.date[this.start + this.period - 1];
			if (startDate == null && endDate == null)
				return;

			// 日付範囲
			y = y + drawDateRange(g2, y, startDate, endDate);
			y = y + drawHorizontalLine(g2, y);

		} else
			throw new RuntimeException();
	}

	// ------------------------------------------------------------ 描画用メソッド

	/**
	 * 左右のマージンです。
	 */
	private static final int MARGIN = 4;

	private static final String FORMAT_DATE = StringManager.getString("format.date");

	/**
	 * 日付範囲を描画します。
	 * 
	 * @param g2
	 * @param y Y座標
	 * @param date1 開始日付
	 * @param date2 終了日付
	 */
	private int drawDateRange(final Graphics2D g2, final int y, final Date date1, final Date date2) {
		final StringBuilder sb = new StringBuilder(30);
		if (date1 != null)
			sb.append(String.format(FORMAT_DATE, date1));
		sb.append(" - ");
		sb.append(String.format(FORMAT_DATE, date2));

		return drawValue(g2, y, Color.BLACK, sb.toString());
	}

	/**
	 * 水平線を描画します。
	 * 
	 * @param g2
	 * @param y Y座標
	 */
	private int drawHorizontalLine(final Graphics2D g2, final float y) {
		final float _y = y + MARGIN;
		g2.setColor(ChartColor.SKY_BLUE);
		g2.draw(new Line2D.Double(MARGIN, _y, getWidth() - MARGIN, _y));
		return MARGIN + 1;
	}

	/**
	 * 日付を描画します。
	 * 
	 * @param g2
	 * @param y Y座標
	 * @param label ラベル
	 * @param date 日付
	 */
	private int drawDate(final Graphics2D g2, final int y, final String label, final Date date) {
		int h1 = this.ascent;
		if (label != null)
			h1 = drawLabel(g2, y, ChartColor.BLACK, label);
		int h2 = this.ascent;
		if (date != null)
			h2 = drawValue(g2, y, ChartColor.BLACK, String.format(FORMAT_DATE, date));
		return Math.max(h1, h2);
	}

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

	/**
	 * 小数部を持たない整数表記の値を描画します。
	 * 
	 * @param g2 Graphics2D オブジェクト
	 * @param y Y座標
	 * @param labelColor ラベルの色
	 * @param label ラベル
	 * @param value 値
	 */
	private int drawDecimal(final Graphics2D g2, final int y, final Color labelColor, final String label, final Number value, final int... period) {
		return drawNumber(g2, y, labelColor, label, Color.BLACK, value, FORMAT_DECIMAL, period);
	}

	/**
	 * 価格を描画します。
	 * 
	 * @param g2 Graphics2D オブジェクト
	 * @param y Y座標
	 * @param label ラベル
	 * @param value 値
	 */
	private int drawPrice(final Graphics2D g2, final int y, final String label, final Number value) {
		return drawNumber(g2, y, Color.BLACK, label, Color.BLACK, value, formatPrice);
	}

	/**
	 * 前日比を描画します。
	 * 
	 * @param g2 Graphics2D オブジェクト
	 * @param y Y座標
	 * @param label ラベル
	 * @param color 前日比の色
	 * @param change 前日比(値幅)
	 * @param percentChange 前日比(率)
	 */
	private int drawChange(
		final Graphics2D g2, final int y, final String label,
		final Color color, final Number change, final Number percentChange) {

		return Math.max(
			drawNumber(g2, y, Color.BLACK, label, color, change, formatChange, percentChange, formatPercentChange),
			ascent + g2.getFontMetrics(FontConstants.FONT_INDEX_SMALL).getAscent()
		);
	}

	/**
	 * 信用倍率を描画します。
	 * 
	 * @param g2 Graphics2D オブジェクト
	 * @param y Y座標
	 * @param label ラベル
	 * @param ratio 信用倍率
	 */
	private int drawRatio(final Graphics2D g2, final int y, final String label, final Number ratio) {
		return drawNumber(g2, y, Color.BLACK, label, Color.BLACK, ratio, formatRatio);
	}

	/**
	 * 指標を描画します。
	 * 
	 * @param g2 Graphics2D オブジェクト
	 * @param y Y座標
	 * @param labelColor ラベルの色
	 * @param label ラベル
	 * @param value 値
	 * @param period 期間(可変長引数)
	 */
	private int drawIndex(
		final Graphics2D g2, final int y,
		final Color labelColor, final String label,
		final Number value,
		final int... period) {

		return drawNumber(g2, y, labelColor, label, Color.BLACK, value, formatIndex, period);
	}

	private int drawRateIndex(
		final Graphics2D g2, final int y,
		final Color labelColor, final String label,
		final Number value,
		final int... period) {

		return drawNumber(g2, y, labelColor, label, Color.BLACK, value, formatRateIndex, period);
	}

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

	/**
	 * ラベル(期間を含む)と値を描画します。
	 * 
	 * @param g2 Graphics2D オブジェクト
	 * @param y Y座標
	 * @param labelColor ラベルの色
	 * @param label ラベル
	 * @param valueColor 値の色
	 * @param value 値
	 * @param format 値の書式
	 * @param period 期間(可変長引数)
	 */
	private int drawNumber(
		final Graphics2D g2, final int y,
		final Color labelColor, final String label,
		final Color valueColor,
		final Number value, final String format,
		final int... period) {

		int h1 = this.ascent;
		if (label != null)
			h1 = drawLabel(g2, y, labelColor, label, period);
		int h2 = this.ascent;
		if (value != null)
			h2 = drawValue(g2, y, valueColor, String.format(format, value.doubleValue()));
		return Math.max(h1, h2);
	}

	/**
	 * ラベルと値および副次的な値を描画します。
	 * 
	 * @param g2 Graphics2D オブジェクト
	 * @param y Y座標
	 * @param labelColor ラベルの色
	 * @param label ラベル
	 * @param valueColor 値の色
	 * @param value 値1
	 * @param format1 値1の書式
	 * @param value2 値2
	 * @param format2 値2の書式
	 */
	private int drawNumber(
		final Graphics2D g2, final int y,
		final Color labelColor, final String label,
		final Color valueColor,
		final Number value, final String format1,
		final Number value2, final String format2) {

		int h1 = this.ascent;
		if (label != null)
			h1 = drawLabel(g2, y, labelColor, label);
		int h2 = this.ascent;
		if (value != null)
			h2 = drawValue(g2, y, valueColor, String.format(format1, value.doubleValue()), String.format(format2, value2));

		return Math.max(h1, h2);
	}

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

	/**
	 * ラベルを描画します。
	 * 
	 * @param g2 Graphics2D オブジェクト
	 * @param y Y座標
	 * @param color ラベルの色
	 * @param label ラベル
	 * @param period 期間(可変長引数)
	 */
	private int drawLabel(final Graphics2D g2, final int y, final Color color, final String label, final int... period) {
		final int _y = y + this.ascent;

		g2.setFont(FontConstants.FONT_MESSAGE);
		DrawUtils.drawText(g2, label, MARGIN, _y, color);

		final double w = g2.getFontMetrics().stringWidth(label) + 2;
		if (period.length > 0) {
			final StringBuilder sb = new StringBuilder();
			for (final int p : period) {
				if (sb.length() > 0)
					sb.append('-');
				sb.append(p);
			}
			sb.insert(0, '[');
			sb.append(']');

			g2.setFont(FontConstants.FONT_INDEX);
			DrawUtils.drawText(g2, sb.toString(), (float) (MARGIN + w), _y);
		}

		return this.ascent;
	}

	/**
	 * 値を描画します。
	 * 
	 * @param g2
	 * @param y Y座標
	 * @param color 値の色
	 * @param text 値(可変長引数)
	 */
	private int drawValue(final Graphics2D g2, final int y, final Color color, final String... text) {
		int _y = y + this.ascent;

		for (int i = 0; i < text.length; i++) {
			final String s = text[i];
			if (i == 0)
				g2.setFont(FontConstants.FONT_INDEX);
			else {
				g2.setFont(FontConstants.FONT_INDEX_SMALL);
				_y = _y + g2.getFontMetrics().getAscent();
			}
			DrawUtils.drawText(g2, s, getWidth() - g2.getFontMetrics().stringWidth(s) - MARGIN, _y, color);
		}

		return _y - y;
	}

}
