/*
 * Copyright (c) 2008-2009 OrangeSignal.com All rights reserved.
 */

package jp.sourceforge.orangesignal.ta.candle.generator;

import static java.lang.Math.abs;
import static java.lang.Math.max;
import static java.lang.Math.min;

import java.io.Serializable;
import java.util.Date;

import jp.sourceforge.orangesignal.ta.candle.Candlestick;
import jp.sourceforge.orangesignal.ta.candle.CandlestickColor;
import jp.sourceforge.orangesignal.ta.candle.RealBodyType;
import jp.sourceforge.orangesignal.ta.candle.ShadowType;
import jp.sourceforge.orangesignal.ta.candle.TrendType;

/**
 * 既定のローソク足情報を提供します。
 * 
 * @author 杉澤 浩二
 */
public class DefaultCandlestick implements Candlestick, Serializable {

	private static final long serialVersionUID = 1L;

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

	/**
	 * トレンドの種類を保持します。
	 */
	protected TrendType trend;
	@Override public TrendType getTrend() { return trend; }

	/**
	 * 陰陽線の種類を保持します。
	 */
	protected CandlestickColor color;
	@Override public CandlestickColor getColor() { return color; }

	/**
	 * 胴体の種類を保持します。
	 */
	protected RealBodyType bodyType;
	@Override public RealBodyType getBodyType() { return bodyType; }

	/**
	 * 上影(上ヒゲ)の種類を保持します。
	 */
	protected ShadowType upperShadowType;
	@Override public ShadowType getUpperShadowType() { return upperShadowType; }

	/**
	 * 下影(下ヒゲ)の種類を保持します。
	 */
	protected ShadowType lowerShadowType;
	@Override public ShadowType getLowerShadowType() { return lowerShadowType; }

	/**
	 * 日時を保持します。
	 */
	protected Date date;
	@Override public Date getDate() { return date; }

	/**
	 * 始値を保持します。
	 */
	protected double open;
	@Override public double getOpen() { return open; }

	/**
	 * 高値を保持します。
	 */
	protected double high;
	@Override public double getHigh() { return high; }

	/**
	 * 安値を保持します。
	 */
	protected double low;
	@Override public double getLow() { return low; }

	/**
	 * 終値を保持します。
	 */
	protected double close;
	@Override public double getClose() { return close; }

	/* ---------------------------------------------------------------------- */
	/* 長さ・位置 */

	@Override public double length() { return high - low; }
	@Override public double median() { return (high + low) * 0.5; }
	@Override public double body() { return abs(close - open); }
	@Override public double upperBody() { return max(open, close); }
	@Override public double lowerBody() { return min(open, close); }
	@Override public double midpoint() { return (close + open) * 0.5; }
	@Override public double shadow() { return length() - body(); }
	@Override public double upperShadow() { return high - upperBody(); }
	@Override public double lowerShadow() { return lowerBody() - low; }

	/* ---------------------------------------------------------------------- */
	/* ローソク単一形 */

	/**
	 * <p>このローソク足の形が大陽線かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * <ol>
	 * <li>胴体が陽線(白)で長い</li>
	 * <li>上影(上ヒゲ)が長くない</li>
	 * <li>下影(下ヒゲ)が長くない</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足が大陽線の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isLongWhite() {
		return color == CandlestickColor.WHITE && bodyType == RealBodyType.LONG && upperShadowType != ShadowType.LONG && lowerShadowType != ShadowType.LONG;
	}

	/**
	 * <p>このローソク足の形が大陰線かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * <ol>
	 * <li>胴体が陰線(黒)で長い</li>
	 * <li>上影(上ヒゲ)が長くない</li>
	 * <li>下影(下ヒゲ)が長くない</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足が大陰線の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isLongBlack() {
		return color == CandlestickColor.BLACK && bodyType == RealBodyType.LONG && upperShadowType != ShadowType.LONG && lowerShadowType != ShadowType.LONG;
	}

	/**
	 * <p>このローソク足の形が小陽線かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * <ol>
	 * <li>胴体が陽線(白)で短い</li>
	 * <li>上影(上ヒゲ)が長くない</li>
	 * <li>下影(下ヒゲ)が長くない</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足が小陽線の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isShortWhite() {
		return color == CandlestickColor.BLACK && bodyType == RealBodyType.SMALL && upperShadowType != ShadowType.LONG && lowerShadowType != ShadowType.LONG;
	}

	/**
	 * <p>このローソク足の形が小陰線かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * <ol>
	 * <li>胴体が陰線(黒)で短い</li>
	 * <li>上影(上ヒゲ)が長くない</li>
	 * <li>下影(下ヒゲ)が長くない</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足が小陰線の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isShortBlack() {
		return color == CandlestickColor.WHITE && bodyType == RealBodyType.SMALL && upperShadowType != ShadowType.LONG && lowerShadowType != ShadowType.LONG;
	}

	/**
	 * <p>このローソク足の形が丸坊主かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * <ol>
	 * <li>胴体が長い</li>
	 * <li>上影(上ヒゲ)が無い(極短含む)</li>
	 * <li>下影(下ヒゲ)が無い(極短含む)</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足が丸坊主の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isMarubozu() {
		return bodyType == RealBodyType.LONG && upperShadowType == ShadowType.NO && lowerShadowType == ShadowType.NO;
	}

	/**
	 * <p>このローソク足の形が寄付坊主かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * 陽の寄付坊主
	 * <ol>
	 * <li>胴体が長い</li>
	 * <li>上影(上ヒゲ)が無い(極短含む)以外</li>
	 * <li>下影(下ヒゲ)が無い(極短含む)</li>
	 * </ol>
	 * </p>
	 * <p>
	 * 陰の寄付坊主
	 * <ol>
	 * <li>胴体が長い</li>
	 * <li>上影(上ヒゲ)が無い(極短含む)</li>
	 * <li>下影(下ヒゲ)が無い(極短含む)以外</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足の形が寄付坊主の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isOpeningMarubozu() {
		switch (color) {
			case WHITE:	// 陽の寄付坊主
				return bodyType == RealBodyType.LONG && upperShadowType != ShadowType.NO && lowerShadowType == ShadowType.NO;
			case BLACK:	// 陰の寄付坊主
				return bodyType == RealBodyType.LONG && upperShadowType == ShadowType.NO && lowerShadowType != ShadowType.NO;
			default:
				throw new RuntimeException();
		}
	}

	/**
	 * <p>このローソク足の形が大引坊主かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * 陽の大引坊主
	 * <ol>
	 * <li>胴体が長い</li>
	 * <li>上影(上ヒゲ)が無い(極短含む)</li>
	 * <li>下影(下ヒゲ)が無い(極短含む)以外</li>
	 * </ol>
	 * </p>
	 * <p>
	 * 陰の大引坊主
	 * <ol>
	 * <li>胴体が長い</li>
	 * <li>上影(上ヒゲ)が無い(極短含む)以外</li>
	 * <li>下影(下ヒゲ)が無い(極短含む)</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足の形が大引坊主の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isClosingMarubozu() {
		switch (this.color) {
			case WHITE:	// 陽の大引坊主
				return bodyType == RealBodyType.LONG && upperShadowType == ShadowType.NO && lowerShadowType != ShadowType.NO;
			case BLACK:	// 	陰の大引坊主
				return bodyType == RealBodyType.LONG && upperShadowType != ShadowType.NO && lowerShadowType == ShadowType.NO;
			default:
				throw new RuntimeException();
		}
	}

	/**
	 * <p>このローソク足の形が小さい丸坊主かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * <ol>
	 * <li>胴体が短い</li>
	 * <li>上影(上ヒゲ)が無い(極短含む)</li>
	 * <li>下影(下ヒゲ)が無い(極短含む)</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足が小さい丸坊主の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isSmallMarubozu() {
		return bodyType == RealBodyType.SMALL && upperShadowType == ShadowType.NO && lowerShadowType == ShadowType.NO;
	}

	/**
	 * <p>このローソク足の形が小さい寄付坊主かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * 陽の寄付坊主
	 * <ol>
	 * <li>胴体が短い</li>
	 * <li>上影(上ヒゲ)が短い</li>
	 * <li>下影(下ヒゲ)が無い(極短含む)</li>
	 * </ol>
	 * </p>
	 * <p>
	 * 陰の寄付坊主
	 * <ol>
	 * <li>胴体が短い</li>
	 * <li>上影(上ヒゲ)が無い(極短含む)</li>
	 * <li>下影(下ヒゲ)が短い</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足の形が小さい寄付坊主の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isSmallOpeningMarubozu() {
		switch (color) {
			case WHITE:	// 陽の寄付坊主
				return bodyType == RealBodyType.SMALL && upperShadowType == ShadowType.SHORT && lowerShadowType == ShadowType.NO;
			case BLACK:	// 陰の寄付坊主
				return bodyType == RealBodyType.SMALL && upperShadowType == ShadowType.NO && lowerShadowType == ShadowType.SHORT;
			default:
				throw new RuntimeException();
		}
	}

	/**
	 * <p>このローソク足の形が小さい大引坊主かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * 陽の大引坊主
	 * <ol>
	 * <li>胴体が短い</li>
	 * <li>上影(上ヒゲ)が無い(極短含む)</li>
	 * <li>下影(下ヒゲ)が短い</li>
	 * </ol>
	 * </p>
	 * <p>
	 * 陰の大引坊主
	 * <ol>
	 * <li>胴体が短い</li>
	 * <li>上影(上ヒゲ)が短い</li>
	 * <li>下影(下ヒゲ)が無い(極短含む)</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足の形が小さい大引坊主の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isSmallClosingMarubozu() {
		switch (this.color) {
			case WHITE:	// 陽の大引坊主
				return bodyType == RealBodyType.SMALL && upperShadowType == ShadowType.NO && lowerShadowType == ShadowType.SHORT;
			case BLACK:	// 	陰の大引坊主
				return bodyType == RealBodyType.SMALL && upperShadowType == ShadowType.SHORT && lowerShadowType == ShadowType.NO;
			default:
				throw new RuntimeException();
		}
	}

	/**
	 * <p>このローソク足の形が独楽(コマ)かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * <ol>
	 * <li>胴体が短い</li>
	 * <li>上影(下ヒゲ)が短い</li>
	 * <li>下影(下ヒゲ)が短い</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足の形が独楽(コマ)の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isSpinningTop() {
		return bodyType == RealBodyType.SMALL && upperShadowType == ShadowType.SHORT && lowerShadowType == ShadowType.SHORT;
	}

	/**
	 * <p>このローソク足の形が足長独楽(コマ)かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * <ol>
	 * <li>胴体が短い</li>
	 * <li>上影(下ヒゲ)が長い</li>
	 * <li>下影(下ヒゲ)が長い</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足の形が足長独楽(コマ)の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isHighWave() {
		return bodyType == RealBodyType.SMALL && upperShadowType == ShadowType.LONG && lowerShadowType == ShadowType.LONG;
	}

	/**
	 * <p>このローソク足の形が唐傘(カラカサ)又は首吊りかどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * <ol>
	 * <li>胴体が短い</li>
	 * <li>上影(上ヒゲ)が無い(極短含む)</li>
	 * <li>下影(下ヒゲ)が長い</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足の形が唐傘(カラカサ)又は首吊りの場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isHangingMan() {
		return bodyType == RealBodyType.SMALL && upperShadowType == ShadowType.NO && lowerShadowType == ShadowType.LONG;
	}

	/**
	 * <p>このローソク足の形が金槌(カナヅチ/トンカチ)かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * <ol>
	 * <li>胴体が短い</li>
	 * <li>上影(上ヒゲ)が長い</li>
	 * <li>下影(下ヒゲ)が無い(極短含む)</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足の形が金槌(カナヅチ/トンカチ)の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isShootingStar() {
		return bodyType == RealBodyType.SMALL && upperShadowType == ShadowType.LONG && lowerShadowType == ShadowType.NO;
	}

	/**
	 * <p>このローソク足の形が足長同時かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * <ol>
	 * <li>胴体が同時</li>
	 * <li>上影(上ヒゲ)が長い</li>
	 * <li>下影(下ヒゲ)が長い</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足の形が足長同時の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isLongLeggedDoji() {
		return bodyType == RealBodyType.DOJI && upperShadowType == ShadowType.LONG && lowerShadowType == ShadowType.LONG;
	}

	/**
	 * <p>このローソク足の形が塔婆(トウバ)かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * <ol>
	 * <li>胴体が同時</li>
	 * <li>上影(上ヒゲ)が長い</li>
	 * <li>下影(下ヒゲ)が無い(極短含む)</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足の形が塔婆(トウバ)の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isGravestoneDoji() {
		return bodyType == RealBodyType.DOJI && upperShadowType == ShadowType.LONG && lowerShadowType == ShadowType.NO;
	}

	/**
	 * <p>このローソク足の形が蜻蛉(トンボ)かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * <ol>
	 * <li>胴体が同時</li>
	 * <li>上影(上ヒゲ)が無い(極短含む)</li>
	 * <li>下影(下ヒゲ)が長い</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足の形が蜻蛉(トンボ)の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isDragonflyDoji() {
		return bodyType == RealBodyType.DOJI && upperShadowType == ShadowType.NO && lowerShadowType == ShadowType.LONG;
	}

	/**
	 * <p>このローソク足の形が四値同時かどうかを返します。</p>
	 * この実装は以下の基準で判断します。
	 * <p>
	 * <ol>
	 * <li>胴体が同時</li>
	 * <li>上影(上ヒゲ)が無い(極短含む)</li>
	 * <li>下影(下ヒゲ)が無い(極短含む)</li>
	 * </ol>
	 * </p>
	 * 
	 * @return このローソク足の形が四値同時の場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	@Override
	public boolean isFourPriceDoji() {
		return bodyType == RealBodyType.DOJI && upperShadowType == ShadowType.NO && lowerShadowType == ShadowType.NO;
	}

	/* ---------------------------------------------------------------------- */
	/* 範囲判定 */

	@Override public boolean contains(final double a) { return high >= a && low <= a; }
	@Override public boolean contains(final double a, final double b) { return high >= max(a, b) && low <= min(a, b); }
	@Override public boolean contains(final Candlestick c) { return high >= c.getHigh() && low <= c.getLow(); }
	@Override public boolean containsByBody(final Candlestick c) { return high >= c.upperBody() && low <= c.lowerBody(); }
	@Override public boolean containsBody(final double a) { return upperBody() >= a && lowerBody() <= a; }
	@Override public boolean containsBody(final double a, final double b) { return upperBody() >= max(a, b) && lowerBody() <= min(a, b); }
	@Override public boolean containsBody(final Candlestick c) { return upperBody() >= c.getHigh() && lowerBody() <= c.getLow(); }
	@Override public boolean containsBodyByBody(final Candlestick c) { return upperBody() >= c.upperBody() && lowerBody() <= c.lowerBody(); }
	@Override public boolean overlaps(final double a, final double b) { return contains(a) || contains(b); }
	@Override public boolean overlaps(final Candlestick c) { return contains(c.getHigh()) || contains(c.getLow()); }
	@Override public boolean overlapsByBody(final Candlestick c) { return contains(c.upperBody()) || contains(c.lowerBody()); }
	@Override public boolean overlapsBody(final double a, final double b) { return containsBody(a) || containsBody(b); }
	@Override public boolean overlapsBody(final Candlestick c) { return containsBody(c.getHigh()) || containsBody(c.getLow()); }
	@Override public boolean overlapsBodyByBody(final Candlestick c) { return containsBody(c.upperBody()) || containsBody(c.lowerBody()); }
	@Override public boolean gap(final double a, final double b) { return !overlaps(a, b); }
	@Override public boolean gap(final Candlestick c) { return !overlaps(c); }
	@Override public boolean gapByBody(final Candlestick c) { return !overlapsByBody(c); }
	@Override public boolean gapBody(final double a, final double b) { return !overlapsBody(a, b); }
	@Override public boolean gapBody(final Candlestick c) { return !overlapsBody(c); }
	@Override public boolean gapBodyByBody(final Candlestick c) { return !overlapsBodyByBody(c); }
	@Override public boolean gapUp(final double a, final double b) { return high < min(a, b); }
	@Override public boolean gapUp(final Candlestick c) { return high < c.getLow(); }
	@Override public boolean gapUpByBody(final Candlestick c) { return high < c.lowerBody(); }
	@Override public boolean gapUpBody(final double a, final double b) { return upperBody() < min(a, b); }
	@Override public boolean gapUpBody(final Candlestick c) { return upperBody() < c.getLow(); }
	@Override public boolean gapUpBodyByBody(final Candlestick c) { return upperBody() < c.lowerBody(); }
	@Override public boolean gapDown(final double a, final double b) { return low > max(a, b); }
	@Override public boolean gapDown(final Candlestick c) { return low > c.getHigh(); }
	@Override public boolean gapDownByBody(final Candlestick c) { return low > c.upperBody(); }
	@Override public boolean gapDownBody(final double a, final double b) { return lowerBody() > max(a, b); }
	@Override public boolean gapDownBody(final Candlestick c) { return lowerBody() > c.getHigh(); }
	@Override public boolean gapDownBodyByBody(final Candlestick c) { return lowerBody() > c.upperBody(); }

//	public Candlestick intersect(final Candlestick c) { return null; }
//	public Candlestick union(final Candlestick c) { return null; }

	/* ---------------------------------------------------------------------- */
	/* 距離 */

	/**
	 * 同距離の基準値を保持します。
	 */
	protected double same;

	@Override public boolean isSameOpen(final double a) { return isSame(open, a); }
	@Override public boolean isSameHigh(final double a) { return isSame(high, a); }
	@Override public boolean isSameLow(final double a) { return isSame(low, a); }
	@Override public boolean isSameClose(final double a) { return isSame(close, a); }
	@Override public boolean isSame(final double a, final double b) { return abs(a - b) < same; }

	/**
	 * 近距離の基準値を保持します。
	 */
	protected double near;

	@Override public boolean isNearOpen(final double a) { return isNear(open, a); }
	@Override public boolean isNearHigh(final double a) { return isNear(high, a); }
	@Override public boolean isNearLow(final double a) { return isNear(low, a); }
	@Override public boolean isNearClose(final double a) { return isNear(close, a); }
	@Override public boolean isNear(final double a, final double b) { return abs(a - b) < near; }

	/**
	 * 遠距離の基準値を保持します。
	 */
	protected double far;

	@Override public boolean isFarOpen(final double a) { return isFar(open, a); }
	@Override public boolean isFarHigh(final double a) { return isFar(high, a); }
	@Override public boolean isFarLow(final double a) { return isFar(low, a); }
	@Override public boolean isFarClose(final double a) { return isFar(close, a); }
	@Override public boolean isFar(final double a, final double b) { return abs(a - b) > far; }

	@Override public boolean isCloseInBottomQuarter() { return close >= (low + length() * 0.75); }
	@Override public boolean isCloseInTopQuarter() { return close <= (low + length() * 0.25); }

}
