package com.desfate.chart;

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

import com.desfate.chart.entity.LineEntity;
import com.desfate.chart.event.ITouchEventNotify;
import com.desfate.chart.util.MathUtil;

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

/**
 * <p>
 * LineChart is a kind of graph that draw some lines on a GridChart
 * </p>
 * <p>
 * LineChartはGridChartの表面でラインを書いたラインチャードです。
 * </p>
 * <p>
 * LineChart是在GridChart上绘制一条或多条线条的图
 * </p>
 *
 * @see GridChart
 */
public class LineChart extends GridChart {

    private static final String TAG = "LineChart";

    /**
     * <p>
     * data to draw lines
     * </p>
     * <p>
     * ラインを書く用データ
     * </p>
     * <p>
     * 绘制线条用的数据
     * </p>
     */
    private List<LineEntity> lineData;
    private List<LineEntity> newLineData;

    /**
     * <p>
     * max points of a single line
     * </p>
     * <p>
     * ラインの最大ポイント数
     * </p>
     * <p>
     * 线条的最大表示点数
     * </p>
     */
    private int maxPointNum;
    private String lastPrice;
    /**
     * <p>
     * min value of Y axis
     * </p>
     * <p>
     * Y軸の最小値
     * </p>
     * <p>
     * Y的最小表示值
     * </p>
     */
    private double minValue = Double.MAX_VALUE;

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

    // FIXME:
    public void setDatasNum(int datasNum) {
        this.datasNum = datasNum;
    }

    public int getDatasNum() {
        return datasNum;
    }

    private int startIndex = 0;// !< @brief 起始位置
    private boolean isChangeLineData = false;

    /**
     * 当前行情的最新价
     */
    private String tasLastPrice = "";

    public String getTasLastPrice() {
        return tasLastPrice;
    }

    public void setTasLastPrice(String tasLastPrice) {
        this.tasLastPrice = tasLastPrice;
        super.setTasLastPrice(tasLastPrice);
    }

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

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

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

    /**
     * 设置默认初始值
     */
    private void initDatas() {
        setCan_move(true);//分时图必须设置的值
    }

    protected void onUpdate() {
        updateLineData();
        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);
        // draw lines
        if (null != this.lineData) {
            drawLines(canvas);
        }
    }

    protected void initMaxAndMin() {
        // calculate max value and min value
        if (null != this.lineData) {

            for (int i = 0; i < lineData.size(); i++) {
                LineEntity line = lineData.get(i);
                if (line.isDisplay()) {
                    List<Double> lineData = line.getLineData();

                    if (lineData != null) {

                        int minIndex = lineData.size() - this.getMaxPointNum();
                        if (startIndex > minIndex && minIndex > 0) {
                            startIndex = minIndex;
                        }

                        for (int j = startIndex; j >= 0 && j < lineData.size()
                                && j < startIndex + this.getMaxPointNum(); j++) {
                            float value = lineData.get(j).floatValue();
                            // if (i == 0 && j == startIndex) {
                            //
                            // maxValue = value;
                            // minValue = value;
                            // } else {
                            if (value > maxValue) {
                                maxValue = value;
                            } else if (value < minValue && value > 0) {
                                minValue = value;
                            }
                            // }
                        }

                        if (maxValue == minValue) {
                            maxValue = maxValue * (1.05);
                            minValue = minValue * 0.95;
                        } else {

                        }
                    }
                }
            }

        }
    }

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

                int end = startIndex + this.getMaxPointNum() - 1;
                if (end < listTime.size() && end >= 0)
                    TitleX.add(listTime.get(end));
            }
        }
        super.setAxisXTitles(TitleX);
    }

    /**
     * <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;
        double ave = Math.pow(10, -this.getDecimalNum());
        if (average < ave) {
            average = ave;
        }
        // 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
        double dLast = minValue + this.getLatitudeNum() * average;
        if (dLast > maxValue) {
            maxValue = dLast;
        }
        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() && 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 > super.getNotifyAxisMarginLeft()) {
                super.setAxisMarginLeft(maxAxisMarginLeft);
                notifyEventAll(ITouchEventNotify.EVENT_MOVE, this);
            }
        }
    }

    /**
     * @param tasPreprice 昨收
     * @param lastPrice   最新价
     */
    public void initAxisY(String tasPreprice, String lastPrice, String hqExchFigures) {
        double average = Double.parseDouble(tasPreprice);
        super.setTasLastPrice(MathUtil.roundNum(lastPrice, Integer.parseInt(hqExchFigures)));
        List<String> TitleY = new ArrayList<String>();
        if (getMaxValue() > 0) {
            if (Math.abs(average - this.getMaxValue()) >= Math.abs(average - this.getMinValue())) {

                this.setMinValue(Math.abs(average - (this.getMaxValue() - average)));
                average = (this.getMaxValue() - average) / (this.getLatitudeNum() / 2);

            } else {
                if (average <= getMaxValue()) {
                    this.setMaxValue(Math.abs(average + (this.getMinValue()) - average));
                    average = Math.abs((this.getMinValue() - average)) / (this.getLatitudeNum() / 2);
                } else {
                    this.setMaxValue(Math.abs(average + (average - this.getMinValue())));
                    average = Math.abs((this.getMinValue() - average)) / (this.getLatitudeNum() / 2);
                }
            }
        } else {
            return;
        }
        for (int i = 0; i < this.getLatitudeNum(); i++) {
            String value = getNumberFormat().format(this.getMinValue() + i * average);
            if (value.length() < super.getAxisYMaxTitleLength()) {
                while (value.length() < super.getAxisYMaxTitleLength()) {
                    value = new String(" ") + value;
                }
            }
            TitleY.add(MathUtil.roundNum(value, Integer.parseInt(hqExchFigures)));
        }
        // calculate last degrees by use max value
        double dLast = minValue + this.getLatitudeNum() * average;
        if (dLast > maxValue) {
            maxValue = dLast;
        }
        String value = getNumberFormat().format(maxValue);
        if (value.length() < super.getAxisYMaxTitleLength()) {
            while (value.length() < super.getAxisYMaxTitleLength()) {
                value = new String(" ") + value;
            }
        }
        TitleY.add(MathUtil.roundNum(value, Integer.parseInt(hqExchFigures)));
        super.setAxisYTitles(TitleY);
        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 > super.getNotifyAxisMarginLeft()) {
                super.setAxisMarginLeft(maxAxisMarginLeft);
                notifyEventAll(ITouchEventNotify.EVENT_MOVE, this);
            }
        }
    }

    @Override
    public int getAxisXPos(Object value) {
        float graduate = Float.valueOf(super.getAxisXGraduate(value));
        int pos = (int) Math.floor(graduate * (maxPointNum - 1));
        if (pos >= maxPointNum) {
            pos = maxPointNum - 1;
        } else if (pos < 0) {
            pos = 0;
        }
        pos += this.getStartIndex();
        return pos;
    }

    /*
     * (non-Javadoc)
     *
     * @param value
     *
     * @see
     * (Object)
     */
    @Override
    public String getAxisXValue(Object value) {
        int index = getAxisXPos(value);
        String ret = "";
        if (lineData.size() <= 0) {
            return "0";
        } else {
            List<String> listTime = lineData.get(0).getLineTime();
            if (index < listTime.size()) {
                ret = listTime.get(index);
            }
        }

        return ret;
    }

    /*
     * (non-Javadoc)
     *
     * @param value
     *
     * @see
     * (Object)
     */
    @Override
    public String getAxisYGraduate(Object value) {
        float graduate = Float.valueOf(super.getAxisYGraduate(value));
        double dYVal = graduate * (maxValue - minValue) + minValue;
        String strYVal = getNumberFormat().format(dYVal);
        return strYVal;
    }

    /**
     * <p>
     * draw lines
     * </p>
     * <p>
     * ラインを書く
     * </p>
     * <p>
     * 绘制线条
     * </p>
     *
     * @param canvas
     */
    protected void drawLines(Canvas canvas) {
        // distance between two points
        float lineLength = getItemWidth();
        // start point‘s X
        float startX;

        // draw lines
        for (int i = 0; i < lineData.size(); i++) {
            LineEntity line = lineData.get(i);
            if (line.isDisplay()) {
                Paint mPaint = new Paint();
                mPaint.setColor(line.getLineColor());
                mPaint.setAntiAlias(true);
                mPaint.setStrokeWidth(2.0f);
                List<Double> lineData = line.getLineData();
                // set start point’s X
                startX = /*super.getAxisMarginLeft() + super.getAxisMarginRight()*/ 2f;// 绘图区域左右对称缩进
                // start point
                PointF ptFirst = null;
                if (lineData != null) {

                    int minIndex = lineData.size() - this.getMaxPointNum();
                    if (startIndex > minIndex && minIndex > 0) {
                        startIndex = minIndex;
                    }

                    for (int j = startIndex; j >= 0 && j < lineData.size() && j < startIndex + this.getMaxPointNum(); j++) {

                        float value = lineData.get(j).floatValue();
                        // calculate Y
                        float valueY = super.getAxisMarginTop()
                                + (float) ((1f - (value - this.getMinValue()) * 10000000
                                / ((this.getMaxValue() - this.getMinValue()) * 10000) / 1000) * (super
                                .getHeight() - super.getAxisMarginBottom() - 2 * super.getAxisMarginTop()));
                        // if is not last point connect to previous point
                        if (j > startIndex) {
                            canvas.drawLine(ptFirst.x, ptFirst.y, startX, valueY, mPaint);
                        }
                        // reset
                        ptFirst = new PointF(startX, valueY);
                        startX = startX + lineLength;
                        if (j >= datasNum - 2) {
                            super.setMaxDatasLength((int) (startX));
                            break;
                        }
                        //super.setMaxDatasLength(startX);
                    }
                }
            }
        }
    }

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

    /**
     * <p>
     * Zoom out the grid
     * </p>
     * <p>
     * 縮小表示する。
     * </p>
     * <p>
     * 缩小
     * </p>
     */
    protected boolean zoomOut() {
        LineEntity line = lineData.get(0);
        if (line != null && maxPointNum < line.getLineData().size() - 3) {
            int temp = maxPointNum;
            maxPointNum = maxPointNum + 3;
            if (this.getItemWidth() < 2) {
                maxPointNum = 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
        try {
            if (!"".equals(tasLastPrice)) {// 暂时这么处理，分时图不让放大
                return false;
            }
            if (zoomChart(detector)) {
                this.invalidate();
                notifyEventAll(ITouchEventNotify.EVENT_SCALE, this);
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            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) {

        try {
            switch (eventId) {
                case ITouchEventNotify.EVENT_MOVE: {
                    moveChart(chart.getTouchDistanceX());
                    changeMarginLeft(chart.getAxisMarginLeft());
                    setNotifyAxisMarginLeft(chart.getAxisMarginLeft());
                }
                break;
                case ITouchEventNotify.EVENT_SCALE:
                    zoomChart(chart.getScaleGestureDetector());
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private void changeMarginLeft(float marginLeft) {
        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 < 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;
        int size = this.getSize();
        if (nMoveNum != 0) {
            tempIdx = getStartIndex() + nMoveNum;
            if (tempIdx <= size - this.getMaxPointNum()) {
                this.setStartIndex(tempIdx);
            }
            if (this.getStartIndex() < 0)
                setStartIndex(0);

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

    @Override
    protected float getItemWidth() {
        return (super.getWidth() - super.getAxisMarginLeft() - super.getAxisMarginRight() - 4f)
                / (this.getMaxPointNum() - 1);
    }

    public void updateLineData() {
        if (this.lineData == null) {
            lineData = new ArrayList<LineEntity>();
        }
        if (isChangeLineData) {
            isChangeLineData = false;
            if (this.newLineData != null) {
                this.lineData.clear();
                this.lineData.addAll(newLineData);
            }
        }
    }

    /**
     * @return the lineData
     */
    public List<LineEntity> getLineData() {
        if (newLineData == null) {
            newLineData = new ArrayList<LineEntity>();
        }
        return newLineData;
    }

    /**
     * @param lineData the lineData to set
     */
    public void setLineData(List<LineEntity> lineData) {
        if (this.newLineData == null) {
            this.newLineData = new ArrayList<LineEntity>();
        }
        this.newLineData.clear();
        this.newLineData.addAll(lineData);
        isChangeLineData = true;
    }

    /**
     * @return the maxPointNum
     */
    public int getMaxPointNum() {
        return maxPointNum;
    }

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

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

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

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

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

    public int getStartIndex() {
        return startIndex;
    }

    public void setStartIndex(int startIndex) {
        this.startIndex = startIndex;
    }

    private int getSize() {
        int size = 0;
        if (lineData != null && lineData.size() > 0) {
            LineEntity line = lineData.get(0);
            if (line != null && line.isDisplay()) {
                List<Double> singleLineData = line.getLineData();
                if (singleLineData != null) {
                    size = singleLineData.size();
                }
            }
        }
        return size;
    }

    private void checkMember() {
        int size = getSize();
        if (this.startIndex < 0 && size > 0) {
            int minIndex = size - this.getMaxPointNum();
            if (minIndex > 0) {
                setStartIndex(minIndex);
            } else {
                setStartIndex(0);
                setMaxPointNum(size);
            }
        }
    }


    public String getLastPrice() {
        return lastPrice;
    }

    public void setLastPrice(String lastPrice) {
        this.lastPrice = lastPrice;
        super.setTasLastPrice(lastPrice);
    }
}
