package com.desfate.chart;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;

import com.desfate.chart.entity.StickEntity;
import com.desfate.chart.event.ITouchEventNotify;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * StickChart is a kind of graph that draw the sticks on a GridChart if you want
 * display some moving average lines on this graph, please use see MAStickChart
 * for more information.
 * </p>
 * <p>
 * StickChartはGridChartの表面でスティックを書いたラインチャードです。移動平均線など
 * 分析線がお使いしたい場合、MAStickChartにご参照ください。
 * </p>
 * <p>
 * StickChart是在GridChart上绘制柱状数据的图表、不支持显示均线。
 * </p>
 */
public class StickChart extends GridChart {

    private static final String TAG = "StickChart";

    /**
     * <p>
     * default color for display stick border
     * </p>
     * <p>
     * 表示スティックのボーダーの色のデフォルト値
     * </p>
     * <p>
     * 默认表示柱条的边框颜色
     * </p>
     */
    public static final int DEFAULT_STICK_BORDER_COLOR = Color.RED;

    /**
     * <p>
     * default color for display stick
     * </p>
     * <p>
     * 表示スティックの色のデフォルト値
     * </p>
     * <p>
     * 默认表示柱条的填充颜色
     * </p>
     */
    public static final int DEFAULT_STICK_FILL_COLOR = Color.RED;

    /**
     * <p>
     * Color for display stick border
     * </p>
     * <p>
     * 表示スティックのボーダーの色
     * </p>
     * <p>
     * 表示柱条的边框颜色
     * </p>
     */
    private int stickBorderColor = DEFAULT_STICK_BORDER_COLOR;

    /**
     * <p>
     * Color for display stick
     * </p>
     * <p>
     * 表示スティックの色
     * </p>
     * <p>
     * 表示柱条的填充颜色
     * </p>
     */
    private int stickFillColor = DEFAULT_STICK_FILL_COLOR;

    /**
     * <p>
     * data to draw sticks
     * </p>
     * <p>
     * スティックを書く用データ
     * </p>
     * <p>
     * 绘制柱条用的数据
     * </p>
     */
    private List<StickEntity> StickData;
    private List<StickEntity> newStickData;

    /**
     * <p>
     * max number of sticks
     * </p>
     * <p>
     * スティックの最大表示数
     * </p>
     * <p>
     * 柱条的最大表示数
     * </p>
     */
    private int maxSticksNum;

    /**
     * <p>
     * max value of Y axis
     * </p>
     * <p>
     * Y軸の最大値
     * </p>
     * <p>
     * Y的最大表示值
     * </p>
     */
    protected double maxValue;

    /**
     * <p>
     * min value of Y axis
     * </p>
     * <p>
     * Y軸の最小値
     * </p>
     * <p>
     * Y的最小表示值
     * </p>
     */
    protected double minValue;

    private boolean isChangeStickData = false;

    /*
     * (non-Javadoc)
     *
     * @param context
     *
     */
    public StickChart(Context context) {
        super(context);
    }

    /*
     * (non-Javadoc)
     *
     * @param context
     *
     * @param attrs
     *
     * @param defStyle
     *
     * AttributeSet, int)
     */
    public StickChart(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /*
     * (non-Javadoc)
     *
     * @param context
     *
     * @param attrs
     *
     * AttributeSet)
     */
    public StickChart(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    //逻辑更新
    protected void onUpdate() {
        updateStickData();
        checkMember();
        initMaxAndMin();
        initAxisY();
        initAxisX();
    }

    /*
     * (non-Javadoc)
     *
     * <p>Called when is going to draw this chart<p> <p>チャートを書く前、メソッドを呼ぶ<p>
     * <p>绘制图表时调用<p>
     *
     * @param canvas
     *
     * @see android.view.View#onDraw(android.graphics.Canvas)
     */
    @Override
    protected void onDraw(Canvas canvas) {
        onUpdate();
        super.onDraw(canvas);
    }

    @Override
    protected void drawChart(Canvas canvas) {
        super.drawChart(canvas);
        drawSticks(canvas);
    }

    protected void initMaxAndMin() {
        if (null != getStickData()) {

            int minIndex = getStickData().size() - maxSticksNum;
            if (this.getStartIndex() > minIndex && minIndex > 0) {
                this.setStartIndex(minIndex);
            }

            for (int i = this.getStartIndex(); i >= 0 && i < getStickData().size() && i < this.getStartIndex() + maxSticksNum; i++) {
                StickEntity ohlc = getStickData().get(i);

                if (i == this.getStartIndex()) {
                    maxValue = ohlc.getHigh();
                    minValue = ohlc.getLow();
                } else {
                    if (ohlc.getHigh() > maxValue) {
                        maxValue = ohlc.getHigh();
                    }
                    if (ohlc.getLow() < minValue) {
                        minValue = ohlc.getLow();
                    }
                }
            }
        }
    }

    @Override
    public int getAxisXPos(Object value) {
        float graduate = Float.valueOf(super.getAxisXGraduate(value));
        int pos = (int) Math.floor(graduate * maxSticksNum);

        if (pos >= maxSticksNum) {
            pos = maxSticksNum - 1;
        } else if (pos < 0) {
            pos = 0;
        }
        pos += this.getStartIndex();

        return pos;
    }

    /*
     * (non-Javadoc)
     *
     * @param value
     *
     */
    @Override
    public String getAxisXValue(Object value) {
        int index = getAxisXPos(value);

        return String.valueOf(getStickData().get(index).getDate());
    }

    /*
     * (non-Javadoc)
     *
     * @param value
     *
     */
    @Override
    public String getAxisYGraduate(Object value) {
        float graduate = Float.valueOf(super.getAxisYGraduate(value));
        return String.valueOf((int) Math.floor(graduate * (maxValue - minValue)
                + minValue));
    }

	/*@Override
    public void notifyEvent(GridChart chart) {
		CandleStickChart candlechart = (CandleStickChart) chart;

		this.maxSticksNum = candlechart.getMaxSticksNum();

		super.setDisplayCrossYOnTouch(false);
		// notifyEvent
		super.notifyEvent(chart);
		// notifyEventAll
		super.notifyEventAll(this);
	}*/

    /**
     * <p>
     * initialize degrees on Y axis
     * </p>
     * <p>
     * X軸の目盛を初期化
     * </p>
     * <p>
     * 初始化X轴的坐标值
     * </p>
     */
    protected void initAxisX() {
        List<String> TitleX = new ArrayList<String>();
        if (null != getStickData() && getStickData().size() > 0) {
            float average = maxSticksNum / this.getLongitudeNum();
            for (int i = 0; i < this.getLongitudeNum(); i++) {
                int index = (int) Math.floor(i * average);
                if (index > maxSticksNum - 1) {
                    index = maxSticksNum - 1;
                }
                if (this.getStartIndex() + index < getStickData().size() && this.getStartIndex() >= 0) {
                    TitleX.add(String.valueOf(getStickData().get(this.getStartIndex() + index).getDate()));
                }
            }
            if (this.getStartIndex() + maxSticksNum - 1 < getStickData().size() && this.getStartIndex() >= 0) {
                TitleX.add(String.valueOf(getStickData().get(this.getStartIndex() + maxSticksNum - 1).getDate()));
            }
        }
        super.setAxisXTitles(TitleX);
    }

    /**
     * <p>
     * get current selected data index
     * </p>
     * <p>
     * 選択したスティックのインデックス
     * </p>
     * <p>
     * 获取当前选中的柱条的index
     * </p>
     *
     * @return int
     * <p>
     * index
     * </p>
     * <p>
     * インデックス
     * </p>
     * <p>
     * index
     * </p>
     */
    public int getSelectedIndex() {
        if (null == super.getTouchPoint()) {
            return 0;
        }
        float graduate = Float.valueOf(super.getAxisXGraduate(super
                .getTouchPoint().x));
        int index = (int) Math.floor(graduate * maxSticksNum);

        if (index >= maxSticksNum) {
            index = maxSticksNum - 1;
        } else if (index < 0) {
            index = 0;
        }

        return index;
    }

    /**
     * <p>
     * initialize degrees on Y axis
     * </p>
     * <p>
     * Y軸の目盛を初期化
     * </p>
     * <p>
     * 初始化Y轴的坐标值
     * </p>
     */
    protected void initAxisY() {
        List<String> TitleY = new ArrayList<String>();
        double average = (maxValue - minValue) * 10000 / this.getLatitudeNum() / 10000;
        // calculate degrees on Y axis
        for (int i = 0; i < this.getLatitudeNum(); i++) {
            String value = getNumberFormat().format(minValue + i * average);
            if (value.length() < super.getAxisYMaxTitleLength()) {
                while (value.length() < super.getAxisYMaxTitleLength()) {
                    value = new String(" ") + value;
                }
            }
            TitleY.add(value);
        }
        // calculate last degrees by use max value
        String value = getNumberFormat().format(maxValue);
        if (value.length() < super.getAxisYMaxTitleLength()) {
            while (value.length() < super.getAxisYMaxTitleLength()) {
                value = new String(" ") + value;
            }
        }
        TitleY.add(value);

        super.setAxisYTitles(TitleY);
        if (super.isDisplayAxisYTitle()) {
            int counts = super.getAxisYTitles().size();
            Paint mPaintFont = new Paint();
            mPaintFont.setTextSize(super.getLatitudeFontSize());
            float textWidth = 0;
            float maxAxisMarginLeft = 0;
            for (int i = 0; i < counts; i++) {
                textWidth = mPaintFont.measureText(super.getAxisYTitles().get(i)) + 10;
                if (textWidth > maxAxisMarginLeft) {
                    maxAxisMarginLeft = textWidth;
                }
            }

            if (maxAxisMarginLeft > super.getNotifyAxisMarginLeft()) {
                super.setAxisMarginLeft(maxAxisMarginLeft);
                notifyEventAll(ITouchEventNotify.EVENT_MOVE, this);
            }
        }
    }

    /**
     * <p>
     * draw sticks
     * </p>
     * <p>
     * スティックを書く
     * </p>
     * <p>
     * 绘制柱条
     * </p>
     *
     * @param canvas
     */
    protected void drawSticks(Canvas canvas) {
        float stickWidth = getItemWidth();
        float stickShowWidth = getItemShowWidth();
        float stickShowOffset = (stickWidth - stickShowWidth) / 2;
        float stickX = super.getAxisMarginLeft() + super.getAxisMarginRight();//绘图区域左右对称缩进

        Paint mPaintStick = new Paint();
        mPaintStick.setColor(stickFillColor);

        if (null != getStickData()) {

            int minIndex = getStickData().size() - maxSticksNum;
            if (this.getStartIndex() > minIndex && minIndex > 0) {
                setStartIndex(minIndex);
            }

            for (int i = this.getStartIndex(); i >= 0 && i < getStickData().size() && i < this.getStartIndex() + maxSticksNum; i++) {
                StickEntity ohlc = getStickData().get(i);

                float highY = (float) ((1f - (ohlc.getHigh() - minValue)
                        / (maxValue - minValue))
                        * (super.getHeight() - super.getAxisMarginBottom()) - super
                        .getAxisMarginTop());
                float lowY = (float) ((1f - (ohlc.getLow() - minValue)
                        / (maxValue - minValue))
                        * (super.getHeight() - super.getAxisMarginBottom()) - super
                        .getAxisMarginTop());

                // stick or line?
                float left = stickX + stickShowOffset;
                if (stickWidth >= 2f) {
                    canvas.drawRect(left, highY, left + stickShowWidth, lowY,
                            mPaintStick);
                } else {
                    canvas.drawLine(left, highY, left, lowY, mPaintStick);
                }

                // next x
                stickX = stickX + stickWidth;
            }
        }
    }

    /**
     * <p>
     * Zoom in the graph
     * </p>
     * <p>
     * 拡大表示する。
     * </p>
     * <p>
     * 放大表示
     * </p>
     */
    protected boolean zoomIn() {
        if (maxSticksNum > 10) {
            maxSticksNum = maxSticksNum - 3;
            return true;
        }
        return false;
    }

    /**
     * <p>
     * Zoom out the grid
     * </p>
     * <p>
     * 縮小表示する。
     * </p>
     * <p>
     * 缩小
     * </p>
     */
    protected boolean zoomOut() {
        if (maxSticksNum < getStickData().size() - 3) {
            int temp = maxSticksNum;
            maxSticksNum = maxSticksNum + 3;
            if (this.getItemWidth() < 1) {
                maxSticksNum = temp;
                return false;
            }
            return true;
        }
        return false;
    }

    /**
     * 监听滑动
     */
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
                            float distanceY) {
        // TODO Auto-generated method stub
        if (moveChart(distanceX)) {
            this.setTouchDistanceX(distanceX);
            notifyEventAll(ITouchEventNotify.EVENT_MOVE, this);
            this.setTouchDistanceX(0);
        }
        return true;
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        // TODO Auto-generated method stub
        if (zoomChart(detector)) {
            this.invalidate();
            notifyEventAll(ITouchEventNotify.EVENT_SCALE, this);
        }
        return false;
    }

    public boolean zoomChart(ScaleGestureDetector detector) {
        boolean isZoom = false;
        float ps = detector.getPreviousSpan();
        float cs = detector.getCurrentSpan();
        if (ps > cs + 5) {
            isZoom = zoomOut();
        } else if (ps < cs - 5) {
            isZoom = zoomIn();
        }
        return isZoom;
    }

    @Override
    public void notifyEvent(int eventId, GridChart chart) {

        switch (eventId) {
            case ITouchEventNotify.EVENT_MOVE: {
                moveChart(chart.getTouchDistanceX());
                changeMarginLeft(chart.getAxisMarginLeft());
                setNotifyAxisMarginLeft(chart.getAxisMarginLeft());
            }
            break;
            case ITouchEventNotify.EVENT_SCALE:
                zoomChart(chart.getScaleGestureDetector());
                super.invalidate();
                break;
            case ITouchEventNotify.EVENT_CROSSONTOUCH:
                this.setClickPostX(chart.getClickPostX());
                this.setClickPostY(chart.getClickPostY());
                super.invalidate();
                break;
            case ITouchEventNotify.EVENT_UP:
                this.setClickPostX(0);
                this.setClickPostY(0);
                super.invalidate();
                break;
            default:
                break;
        }
    }

    private void changeMarginLeft(float marginLeft) {
        if (super.isDisplayAxisYTitle() && super.getAxisYTitles() != null) {
            int counts = super.getAxisYTitles().size();
            Paint mPaintFont = new Paint();
            mPaintFont.setTextSize(super.getLatitudeFontSize());
            float textWidth = 0;
            float maxAxisMarginLeft = 0;
            for (int i = 0; i < counts; i++) {
                textWidth = mPaintFont.measureText(super.getAxisYTitles().get(i)) + 10;
                if (textWidth > maxAxisMarginLeft) {
                    maxAxisMarginLeft = textWidth;
                }
            }

            if (maxAxisMarginLeft < marginLeft && marginLeft != super.getAxisMarginLeft()) {
                super.setAxisMarginLeft(marginLeft);
                super.invalidate();
            }
        }
    }

    private boolean moveChart(float distance) {
        float fMoveNum = distance / getItemWidth();
        int nMoveNum = Math.round(fMoveNum);
        int tempIdx = 0;
        if (nMoveNum != 0) {
            tempIdx = getStartIndex() + nMoveNum;
            if (tempIdx <= getStickData().size() - this.getMaxSticksNum()) {
                setStartIndex(tempIdx);
                if (this.getStartIndex() < 0)
                    setStartIndex(0);

                super.invalidate();
                return true;
            }
        }
        return false;
    }

    private void checkMember() {
        if (this.getStartIndex() < 0) {
            int minIndex = getStickData().size() - maxSticksNum;
            if (minIndex > 0) {
                setStartIndex(minIndex);
            } else if (getStickData().size() > 0) {
                setStartIndex(0);
                //setMaxSticksNum(getStickData().size());
            }
        }
    }

    @Override
    protected float getClickPosXOffset() {
        return getItemWidth() / 2;
    }

    @Override
    protected float getItemWidth() {
        return (super.getWidth() - super.getAxisMarginLeft() - 2 * super.getAxisMarginRight()) / maxSticksNum;
    }

    /**
     * <p>
     * 实际绘制的柱宽
     * </p>
     */
    @Override
    protected float getItemShowWidth() {
        return getItemWidth() - 4f;
    }

    /**
     * <p>
     * add a new stick data to sticks and refresh this chart
     * </p>
     * <p>
     * 新しいスティックデータを追加する，フラフをレフレシューする
     * </p>
     * <p>
     * 追加一条新数据并刷新当前图表
     * </p>
     *
     * @param entity <p>
     *               data
     *               </p>
     *               <p>
     *               データ
     *               </p>
     *               <p>
     *               新数据
     *               </p>
     */
    public void pushData(StickEntity entity) {
        if (null != entity) {
            addData(entity);
            super.postInvalidate();
        }
    }

    /**
     * <p>
     * add a new stick data to sticks
     * </p>
     * <p>
     * 新しいスティックデータを追加する
     * </p>
     * <p>
     * 追加一条新数据
     * </p>
     *
     * @param entity <p>
     *               data
     *               </p>
     *               <p>
     *               データ
     *               </p>
     *               <p>
     *               新数据
     *               </p>
     */
    public void addData(StickEntity entity) {
        if (null != entity) {
            // data is null or empty
            if (null == getStickData() || 0 == getStickData().size()) {
                setStickData(new ArrayList<StickEntity>());
                this.maxValue = ((int) entity.getHigh()) / 100 * 100;
            }

            // add
            this.getStickData().add(entity);

            if (this.maxValue < entity.getHigh()) {
                this.maxValue = 100 + ((int) entity.getHigh()) / 100 * 100;
            }

			/*if (StickData.size() > maxSticksNum) {
				maxSticksNum = maxSticksNum + 1;
			}*/
        }
    }

    /**
     * @return the stickBorderColor
     */
    public int getStickBorderColor() {
        return stickBorderColor;
    }

    /**
     * @param stickBorderColor the stickBorderColor to set
     */
    public void setStickBorderColor(int stickBorderColor) {
        this.stickBorderColor = stickBorderColor;
    }

    /**
     * @return the stickFillColor
     */
    public int getStickFillColor() {
        return stickFillColor;
    }

    /**
     * @param stickFillColor the stickFillColor to set
     */
    public void setStickFillColor(int stickFillColor) {
        this.stickFillColor = stickFillColor;
    }

    public void updateStickData() {
        if (isChangeStickData) {
            isChangeStickData = false;
            if (newStickData != null) {
                if (null == StickData) {
                    StickData = new ArrayList<StickEntity>();
                }
                StickData.clear();
                StickData.addAll(newStickData);
				/*for (StickEntity e : newStickData) {
					addData(e);
				}*/
            }
        }
    }

    /**
     * @return the stickData
     */
    public List<StickEntity> getStickData() {
        if (StickData == null) {
            StickData = new ArrayList<StickEntity>();
        }
        return StickData;
    }

    /**
     * @return the stickData
     */
    public List<StickEntity> getNewStickData() {
        if (newStickData == null) {
            newStickData = new ArrayList<StickEntity>();
        }
        return newStickData;
    }

    /**
     * @param stickData the stickData to set
     */
    public void setStickData(List<StickEntity> stickData) {
        if (newStickData == null) {
            newStickData = new ArrayList<StickEntity>();
        }
        newStickData.clear();
        newStickData.addAll(stickData);
        isChangeStickData = true;
    }

    /**
     * @return the maxSticksNum
     */
    public int getMaxSticksNum() {
        return maxSticksNum;
    }

    /**
     * @param maxSticksNum the maxSticksNum to set
     */
    public void setMaxSticksNum(int maxSticksNum) {
        this.maxSticksNum = maxSticksNum;
    }

    /**
     * @return the maxValue
     */
    public double getMaxValue() {
        return maxValue;
    }

    /**
     * @param maxValue the maxValue to set
     */
    public void setMaxValue(double maxValue) {
        this.maxValue = maxValue;
    }

    /**
     * @return the minValue
     */
    public double getMinValue() {
        return minValue;
    }

    /**
     * @param minValue the minValue to set
     */
    public void setMinValue(double minValue) {
        this.minValue = minValue;
    }
}
