package com.toly1994.ds4android.view;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Picture;
import android.graphics.Point;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;

import com.toly1994.ds4android.analyze.ColUtils;
import com.toly1994.ds4android.analyze.L;
import com.toly1994.ds4android.analyze.gold12.HelpDraw;
import com.toly1994.ds4android.analyze.gold12.JudgeMan;
import com.toly1994.ds4android.ds.impl.chart.LinkedChart;
import com.toly1994.ds4android.ds.itf.IChart;
import com.toly1994.ds4android.model.LinkedNode;
import com.toly1994.ds4android.view.other.Cons;
import com.toly1994.ds4android.view.other.OnCtrlClickListener;

/**
 * 作者:张风捷特烈<br/>
 * 时间:2018/11/21 0021:8:01<br/>
 * 邮箱:[email protected]<br/>
 * 说明:双链表实现表结构---测试视图
 */
public class LinkedView<E> extends View {
    private Point mCoo = new Point(200, 200);//坐标系
    private Picture mCooPicture;//坐标系canvas元件
    private Picture mGridPicture;//网格canvas元件


    private Path mPath;//主路径
    private Paint mPaint;//主画笔
    private Paint mTxtPaint;//数字画笔
    private Paint mPathPaint;//路径画笔
    private Paint mCtrlPaint;//几个圆的画笔

    private IChart<LinkedNode<E>> mArrayBoxes = new LinkedChart<>();
    private OnCtrlClickListener<LinkedView<E>> mOnCtrlClickListener;
    private int selectIndex = -1;//当前选中的索引
    private ValueAnimator mAnimator;


    private static final int OFFSET_X = 100;//X空隙
    private static final int OFFSET_Y = 250;//Y空隙
    private static final int OFFSET_OF_TXT_Y = 10;//文字的偏移
    private static final int LINE_ITEM_NUM = 5;//每行的单体个数

    private static final int BOX_RADIUS = 10;//数组盒子的圆角

    private static final Point[] CTRL_POS = new Point[]{//控制按钮的点位
            new Point(-100, 100),//添加
            new Point(-100, 300),//更新
            new Point(-100, 500),//查看
            new Point(-100, 700),//删除

            new Point(700, -70),//定点添加
            new Point(700 + 300, -70),//定值查询
            new Point(700 + 300 * 2, -70),//定点删除
            new Point(700 + 300 * 3, -70),//清除
    };

    private static int[] CTRL_COLOR = new int[]{//控制按钮的颜色
            0xff1EF519,//添加
            0xff2992F2,//更新
            0xffB946F4,//添加
            0xffF50C0C,//删除

            0xff1EF519,//定点添加
            0xffB946F4,//定值查询
            0xffF50C0C,//定点删除
            0xffF46410,//清除
    };

    private static final String[] CTRL_TXT = new String[]{//控制按钮的文字
            "添加",//添加
            "更新",//更新
            "查寻",//添加
            "删除",//删除

            "定点+",//定点添加
            "值查",//定值查询
            "定点-",//定点删除
            "清空",//清除按键
    };

    private static final int CTRL_RADIUS = 50;//控制按钮的半径


    public void setOnCtrlClickListener(OnCtrlClickListener<LinkedView<E>> onCtrlClickListener) {
        mOnCtrlClickListener = onCtrlClickListener;
    }

    public LinkedView(Context context) {
        this(context, null);
    }

    public LinkedView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();//初始化
    }

    private void init() {
        //初始化主画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.BLUE);
        mPaint.setStrokeWidth(5);
        mPaint.setTextAlign(Paint.Align.CENTER);
        mPaint.setTextSize(50);
        //初始化主路径
        mPath = new Path();
        //初始化文字画笔
        mTxtPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTxtPaint.setColor(Color.WHITE);
        mTxtPaint.setTextAlign(Paint.Align.CENTER);
        mTxtPaint.setTextSize(40);
        //初始化路径画笔
        mPathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPathPaint.setColor(0xff810DF3);
        mPathPaint.setStrokeWidth(4);
        mPathPaint.setStyle(Paint.Style.STROKE);
        mCooPicture = HelpDraw.getCoo(getContext(), mCoo, false);
        mGridPicture = HelpDraw.getGrid(getContext());
        //初始化圆球按钮画笔
        mCtrlPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCtrlPaint.setColor(Color.RED);
        mCtrlPaint.setTextAlign(Paint.Align.CENTER);
        mCtrlPaint.setTextSize(30);
        //初始化时间流ValueAnimator
        mAnimator = ValueAnimator.ofFloat(0, 1);
        mAnimator.setRepeatCount(-1);
        mAnimator.setDuration(2000);
        mAnimator.setRepeatMode(ValueAnimator.REVERSE);
        mAnimator.setInterpolator(new LinearInterpolator());
        mAnimator.addUpdateListener(animation -> {
            updateBall();//更新小球位置
            invalidate();
        });
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        HelpDraw.draw(canvas, mGridPicture);
        canvas.save();
        canvas.translate(mCoo.x, mCoo.y);//画布移到坐标原点
        mTxtPaint.setColor(Color.WHITE);

        dataView(canvas);//核心操作展示
        ctrlView(canvas);//操作按钮
        helpView(canvas);//辅助视图
        canvas.restore();
        HelpDraw.draw(canvas, mCooPicture);
    }

    /**
     * 绘制表结构
     *
     * @param canvas
     */
    private void dataView(Canvas canvas) {
        mPaint.setColor(Color.BLUE);
        mPaint.setStyle(Paint.Style.FILL);
        mPath.reset();
        for (int i = 0; i < mArrayBoxes.size(); i++) {
            LinkedNode box = mArrayBoxes.get(i);

            mPaint.setColor(box.color);
            canvas.drawRoundRect(
                    box.x, box.y, box.x + Cons.BOX_WIDTH, box.y + Cons.BOX_HEIGHT,
                    BOX_RADIUS, BOX_RADIUS, mPaint);

            mPath.moveTo(box.x, box.y);
            mPath.rCubicTo(Cons.BOX_WIDTH / 2, Cons.BOX_HEIGHT / 2,
                    Cons.BOX_WIDTH / 2, Cons.BOX_HEIGHT / 2, Cons.BOX_WIDTH, 0);

            if (i < mArrayBoxes.size() - 1 ) {
                LinkedNode box_next = mArrayBoxes.get(i + 1);
                LinkedNode box_now = mArrayBoxes.get(i);

                if (i % LINE_ITEM_NUM == LINE_ITEM_NUM - 1) {//边界情况
                    mPath.rLineTo(0, Cons.BOX_HEIGHT);
                    mPath.lineTo(box_next.x + Cons.BOX_WIDTH, box_next.y);
                    mPath.rLineTo(Cons.ARROW_DX, -Cons.ARROW_DX);

                    mPath.moveTo(box_next.x, box_next.y);
                    mPath.lineTo(box_now.x, box_now.y+Cons.BOX_HEIGHT);
                    mPath.rLineTo(-Cons.ARROW_DX, Cons.ARROW_DX);

                } else {
                    mPath.rLineTo(0, Cons.BOX_HEIGHT / 2.2f);
                    mPath.lineTo(box_next.x+Cons.BOX_WIDTH * 0.2f, box_next.y + Cons.BOX_HEIGHT / 2f);
                    mPath.rLineTo(-Cons.ARROW_DX, -Cons.ARROW_DX);

                    mPath.moveTo(box_next.x, box_next.y);
                    mPath.rLineTo(0, Cons.BOX_HEIGHT / 1.2f);
                    mPath.lineTo(box_now.x + Cons.BOX_WIDTH * 0.8f, box_now.y + Cons.BOX_HEIGHT * 0.8f);
                    mPath.rLineTo(Cons.ARROW_DX, Cons.ARROW_DX);
                }
            }
            canvas.drawPath(mPath, mPathPaint);

            canvas.drawText(box.index + "",
                    box.x + Cons.BOX_WIDTH / 2,
                    box.y + 3 * OFFSET_OF_TXT_Y, mTxtPaint);

            canvas.drawText(box.data + "",
                    box.x + Cons.BOX_WIDTH / 2,
                    box.y + Cons.BOX_HEIGHT / 2 + 3 * OFFSET_OF_TXT_Y, mTxtPaint);
        }
    }

    /**
     * 绘制数组的长度个空白矩形
     *
     * @param canvas
     */
    private void helpView(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawText("线性表", -100, -50, mPaint);
        canvas.drawText("当前选中点:" + selectIndex, 250, -50, mPaint);
    }

    /**
     * 控制面板--圆球
     *
     * @param canvas 画布
     */
    private void ctrlView(Canvas canvas) {
        for (int i = 0; i < CTRL_POS.length; i++) {
            mCtrlPaint.setColor(CTRL_COLOR[i]);
            canvas.drawCircle(CTRL_POS[i].x, CTRL_POS[i].y, CTRL_RADIUS, mCtrlPaint);
            canvas.drawText(CTRL_TXT[i], CTRL_POS[i].x, CTRL_POS[i].y + OFFSET_OF_TXT_Y, mTxtPaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:

                float downX = event.getX() - mCoo.x;
                float downY = event.getY() - mCoo.y;
                updateSelectIndex(downX, downY);//更新selectIndex

                for (int i = 0; i < CTRL_POS.length; i++) {
                    //区域判定
                    if (JudgeMan.judgeCircleArea(
                            CTRL_POS[i].x, CTRL_POS[i].y,
                            downX, downY, CTRL_RADIUS * 1.2f)) {
                        if (mOnCtrlClickListener != null) {
                            switch (i) {
                                case 0://插入尾部
                                    mOnCtrlClickListener.onAdd(this);
                                    if (selectIndex > 0) {
                                        mArrayBoxes.get(selectIndex).color = 0xff43A3FA;
                                        selectIndex = -1;
                                    }
                                    break;
                                case 1://更新
                                    mOnCtrlClickListener.onSet(this);
                                    break;
                                case 2://查找
                                    mOnCtrlClickListener.onFind(this);
                                    break;
                                case 3://删除尾部
                                    if (selectIndex > 0) {//如果有选中的颜色,先复原
                                        mArrayBoxes.get(selectIndex).color = 0xff43A3FA;
                                    }
                                    selectIndex = mArrayBoxes.size() - 1;
                                    mAnimator.start();
                                    break;
                                case 4://定点添加
                                    if (selectIndex >= 0) {
                                        mArrayBoxes.get(selectIndex).color = 0xff43A3FA;
                                        mOnCtrlClickListener.onAddByIndex(this);
                                        selectIndex = -1;
                                    }
                                    break;
                                case 5://定值查询
                                    mOnCtrlClickListener.onFindByData(this);
                                    break;
                                case 6://定点移除
                                    mAnimator.start();
                                    break;
                                case 7://清空
                                    mOnCtrlClickListener.onClear(this);
                                    selectIndex = -1;
                                    break;
                            }
                            CTRL_COLOR[i] = 0xff54E1F8;//点击更换颜色
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP://还原颜色
                CTRL_COLOR[0] = 0xff1EF519;
                CTRL_COLOR[1] = 0xff2992F2;
                CTRL_COLOR[2] = 0xffB946F4;
                CTRL_COLOR[3] = 0xffF50C0C;
                CTRL_COLOR[4] = 0xff1EF519;
                CTRL_COLOR[5] = 0xffB946F4;
                CTRL_COLOR[6] = 0xffF50C0C;
                CTRL_COLOR[7] = 0xffF46410;
                break;
        }
        invalidate();
        return true;
    }

    /**
     * 点击时动态更新选中值
     *
     * @param downX 按下点X
     * @param downY 按下点Y
     */
    private void updateSelectIndex(float downX, float downY) {
        float x = downX / (Cons.BOX_WIDTH + OFFSET_X) - 0.5f;
        float y = downY / (Cons.BOX_HEIGHT + OFFSET_Y) - 0.5f;
        if (x > -0.5 && y > -0.5) {
            if (selectIndex != -1) {
                mArrayBoxes.get(selectIndex).color = 0xff43A3FA;//还原之前选中的颜色
            }
            int indexOfData = Math.round(y) * LINE_ITEM_NUM + Math.round(x);//逆向求取点中的数据索引
            if (indexOfData < mArrayBoxes.size()) {
                mArrayBoxes.get(indexOfData).color = ColUtils.randomRGB();
                selectIndex = indexOfData;
            }
        }
    }

    /**
     * 更新小球
     */
    private void updateBall() {
        if (mArrayBoxes.size() > 0 && selectIndex != -1) {
            L.d(selectIndex + L.l());
            LinkedNode ball = mArrayBoxes.get(selectIndex);
            ball.x += ball.vX;
            ball.y += ball.vY;

            if (ball.y > 600) {
                if (mOnCtrlClickListener != null) {
                    mOnCtrlClickListener.onRemoveByIndex(this);//移除监听放在这里了!!
                    mAnimator.pause();
                }
            }
        }

    }


    /**
     * 视图的数据操作接口方法--添加
     *
     * @param data 数据
     */
    public void addData(E data) {
        LinkedNode<E> LinkedNode = new LinkedNode<>(0, 0);
        LinkedNode.data = data;
        mArrayBoxes.add(LinkedNode);
        updatePosOfData();
    }

    /**
     * 视图的数据操作接口方法--根据索引添加
     *
     * @param index 索引
     * @param data  数据
     */
    public void addDataById(int index, E data) {
        L.d("addDataById:" + index + L.l());
        if (mArrayBoxes.size() > 0 && index < mArrayBoxes.size() && index >= 0) {
            LinkedNode<E> LinkedNode = new LinkedNode<>(0, 0);
            LinkedNode.data = data;
            mArrayBoxes.add(index, LinkedNode);
            updatePosOfData();
        }
    }

    /**
     * 视图的数据操作接口方法--根据id查询操作
     *
     * @param index 索引
     * @return
     */
    public E findData(int index) {
        if (mArrayBoxes.size() > 0 && index < mArrayBoxes.size() && index >= 0) {
            return mArrayBoxes.get(index).data;
        }
        return null;
    }


    /**
     * 视图的数据操作接口方法--根据数据查询操作
     *
     * @param data 数据
     * @return
     */
    public int[] findData(E data) {
        if (selectIndex != -1) {
            LinkedNode<E> LinkedNode = new LinkedNode<>(0, 0);
            LinkedNode.data = data;
            return mArrayBoxes.getIndex(LinkedNode);
        }
        return null;
    }

    /**
     * 视图的数据操作接口方法--更新数据
     *
     * @param index 索引
     * @param data  数据
     */
    public void setData(int index, E data) {
        if (mArrayBoxes.size() > 0 && index < mArrayBoxes.size() && index >= 0) {
            mArrayBoxes.get(index).data = data;
        }
    }

    /**
     * 视图的数据操作接口方法--移除末尾
     */
    public void removeData() {
        if (mArrayBoxes.size() > 0) {
            mArrayBoxes.remove();
            updatePosOfData();
        }
    }

    /**
     * 视图的数据操作接口方法--定索引删除操作
     *
     * @param index 索引
     */
    public void removeData(int index) {
        if (mArrayBoxes.size() > 0 && index < mArrayBoxes.size() && index >= 0) {
            //更新后面的索引
            for (int i = index; i < mArrayBoxes.size(); i++) {
                mArrayBoxes.get(i).index -= 1;
            }
            mArrayBoxes.remove(index);
            selectIndex = -1;
            updatePosOfData();
        }
    }

    /**
     * 视图的数据操作接口方法--清空操作
     */
    public void clearData() {
        mArrayBoxes.clear();
    }

    /**
     * 更新绘制单体的点位
     */
    private void updatePosOfData() {
        for (int i = 0; i < mArrayBoxes.size(); i++) {
            int y = i / LINE_ITEM_NUM;//行坐标
            int x = i % LINE_ITEM_NUM;//列坐标

            LinkedNode box = mArrayBoxes.get(i);
            box.x = (Cons.BOX_WIDTH + OFFSET_X) * x;
            box.y = (Cons.BOX_HEIGHT + OFFSET_Y) * y;
            box.index = i;
            box.vY = 50;
            box.vX = 0;
        }
    }

    /**
     * 获取选中索引
     *
     * @return
     */
    public int getSelectIndex() {
        return selectIndex;
    }

    /**
     * 获取选中值
     *
     * @return
     */
    public E getSelectData() {
        if (selectIndex >= 0) {
            return mArrayBoxes.get(selectIndex).data;
        }
        return null;
    }


}