/*
 * *********************************************************************************************************************
 *
 *          !!
 *        .F88X
 *        X8888Y
 *      .}888888N;
 *        i888888N;        .:!              .I$WI:
 *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
 *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
 *            +888888N;  .8888888Y                                  "&&8Y.}8,
 *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
 *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
 *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
 *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
 *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
 *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
 *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
 *      .:R888888I
 *      .&888888I                                          Copyright (c) 2016-2020.  博锐尚格科技股份有限公司
 *        ~8888'
 *        .!88~                                                                     All rights reserved.
 *
 * *********************************************************************************************************************
 */


import { SColor, SLine, SPainter, SPoint, SRect } from "@persagy-web/draw";
import { SKeyCode, SMouseEvent, SUndoStack } from "@persagy-web/base";
import { SItemStatus } from "@persagy-web/big";;
import { SMathUtil } from "@persagy-web/big/lib/utils/SMathUtil";;
import {
    SGraphPointListDelete,
    SGraphPointListInsert,
    SGraphPointListUpdate,
    SLineStyle,
    SGraphItem
} from "@persagy-web/graph/";
import {SGraphEdit} from ".."
import { Marker } from "../type/Marker";
/**
 * 折线编辑类
 *
 * @author  韩耀龙(han_yao_long@163.com)
 */
export class SBasePolylineEdit extends SGraphEdit {
    /** X坐标最小值  */
    private minX = Number.MAX_SAFE_INTEGER;
    /** X坐标最大值  */
    private maxX = Number.MIN_SAFE_INTEGER;
    /** Y坐标最小值  */
    private minY = Number.MAX_SAFE_INTEGER;
    /** Y坐标最大值  */
    private maxY = Number.MIN_SAFE_INTEGER;
    /** 折点信息    */
    pointList: SPoint[] = [];
    /** 是否绘制完成  */
    _status: SItemStatus = SItemStatus.Normal;
    get status(): SItemStatus {
        return this._status;
    }
    set status(v: SItemStatus) {
        this._status = v;
        this.undoStack.clear();
        this.update();
    }
    /** 鼠标移动时的点 */
    private lastPoint: SPoint | null = null;
    /** 线条颜色    */
    _strokeColor: SColor = SColor.Black;
    get strokeColor(): SColor {
        return this._strokeColor;
    }
    set strokeColor(v: SColor) {
        this._strokeColor = v;
        this.update();
    }
    /** 填充色 */
    _fillColor: SColor = new SColor("#2196f3");
    get fillColor(): SColor {
        return this._fillColor;
    }
    set fillColor(v: SColor) {
        this._fillColor = v;
        this.update();
    }
    /** 边框样式 */
    _lineStyle: SLineStyle = SLineStyle.Solid;
    get lineStyle(): SLineStyle {
        return this._lineStyle;
    }
    set lineStyle(v: SLineStyle) {
        this._lineStyle = v;
        this.update();
    }
    /** 线条宽度    */
    _lineWidth: number = 1;
    get lineWidth(): number {
        return this._lineWidth;
    }
    set lineWidth(v: number) {
        this._lineWidth = v;
        this.update();
    }
    /** 是否垂直水平绘制   */
    private _verAndLeve: Boolean = false;
    get verAndLeve(): Boolean {
        return this._verAndLeve;
    }
    set verAndLeve(bool: Boolean) {
        this._verAndLeve = bool;
        this.update();
    }
    /** 全局灵敏度   */
    dis: number = 10;
    /** 灵敏度转换为场景长度  */
    private sceneDis: number = 10;
    /** 当前点索引   */
    private curIndex: number = -1;
    /** 当前点索引   */
    private curPoint: SPoint | null = null;
    /** undo/redo堆栈 */
    private undoStack: SUndoStack = new SUndoStack();

    /**
     * 构造函数
     *
     * @param   parent  父级
     * @param   list    坐标集合
     * */
    constructor(parent: null | SGraphItem, list: SPoint[]);

    /**
     * 构造函数
     *
     * @param   parent  父级
     * @param   list    第一个坐标
     * */
    constructor(parent: null | SGraphItem, list: SPoint);

    /**
     * 构造函数
     *
     * @param   parent  父级
     * @param   list    第一个坐标|坐标集合
     * */
    constructor(parent: null | SGraphItem, list: SPoint | SPoint[]) {
        super(parent);
        if (list instanceof SPoint) {
            this.pointList.push(list);
        } else {
            this.pointList = list;
        }
    } // Constructor

    /**
     * 添加点至数组中
     *
     * @param   p       添加的点
     * @param   index   添加到的索引
     * */
    private addPoint(p: SPoint, index?: number): void {
        if (index && this.canHandle(index)) {
            this.pointList.splice(index, 0, p);
            this.recordAction(SGraphPointListInsert, [
                this.pointList,
                p,
                index
            ]);
        } else {
            this.pointList.push(p);
            this.recordAction(SGraphPointListInsert, [this.pointList, p]);
        }
        this.update();
    } // Function addPoint()

    /**
     * 是否可以添加点到数组中
     *
     * @param   index   要添加到的索引
     * @return  boolean 是否可添加
     * */
    private canHandle(index: number): boolean {
        return index >= 0 && index <= this.pointList.length;
    } // Function canHandle()

    /**
     * 根据索引删除点
     *
     * @param   index   删除点
     * */
    deletePoint(index: number): void {
        if (this.canHandle(index) && this.pointList.length > 2) {
            const p = new SPoint(
                this.pointList[this.curIndex].x,
                this.pointList[this.curIndex].y
            );
            this.pointList.splice(index, 1);
            this.recordAction(SGraphPointListDelete, [
                this.pointList,
                p,
                index
            ]);
            this.curIndex = -1;
            this.curPoint = null;
            this.update();
        }
    } // Function deletePoint

    /**
     * 鼠标按下事件
     *
     * @param   event   鼠标事件
     * @return  boolean 是否处理事件
     * */
    onMouseDown(event: SMouseEvent): boolean {
        this.curIndex = -1;
        this.curPoint = null;
        if (event.shiftKey || this.verAndLeve) {
            event = this.compare(event);
        }
        if (event.buttons == 1) {
            if (this.status == SItemStatus.Create) {
                this.addPoint(new SPoint(event.x, event.y));
                return true;
            } else if (this.status == SItemStatus.Edit) {
                // 查询鼠标最近的索引
                this.findNearestPoint(new SPoint(event.x, event.y));
                // 增加点
                if (this.curIndex < 0) {
                    this.findAddPos(new SPoint(event.x, event.y));
                }
                // 删除点
                if (event.altKey && this.canHandle(this.curIndex)) {
                    this.deletePoint(this.curIndex);
                }
                this.update();
                return true;
            } else {
                return super.onMouseDown(event);
            }
        }
        return super.onMouseDown(event);
    } // Function onMouseDown()

    /**
     * 鼠标移动事件
     *
     * @param   event   鼠标事件
     * @return  boolean 是否处理事件
     * */
    onMouseMove(event: SMouseEvent): boolean {
        if (event.shiftKey || this.verAndLeve) {
            event = this.compare(event);
        }
        if (this.status == SItemStatus.Create) {
            if (this.lastPoint) {
                this.lastPoint.x = event.x;
                this.lastPoint.y = event.y;
            } else {
                this.lastPoint = new SPoint(event.x, event.y);
            }
            this.update();
            return true;
        } else if (this.status == SItemStatus.Edit) {
            if (event.buttons == 1) {
                if (this.canHandle(this.curIndex)) {
                    this.pointList[this.curIndex].x = event.x;
                    this.pointList[this.curIndex].y = event.y;
                }
            }
            this.update();
            return true;
        } else {
            return super.onMouseMove(event);
        }
    } // Function onMouseMove()

    /**
     * 鼠标移动事件
     *
     * @param   event   鼠标事件
     * @return  boolean 是否处理事件
     * */
    onMouseUp(event: SMouseEvent): boolean {
        if (this.status == SItemStatus.Edit) {
            if (this.curIndex > -1) {
                const p = new SPoint(
                    this.pointList[this.curIndex].x,
                    this.pointList[this.curIndex].y
                );
                this.recordAction(SGraphPointListUpdate, [
                    this.pointList,
                    this.curPoint,
                    p,
                    this.curIndex
                ]);
            }
        } else if (this.status == SItemStatus.Normal) {
            this.moveToOrigin(this.x, this.y);
            return super.onMouseUp(event);
        }
        return true;
    } // Function onMouseUp()

    /**
     * 鼠标双击事件
     *
     * @param	event         事件参数
     * @return	boolean
     */
    onDoubleClick(event: SMouseEvent): boolean {
        if (this.status == SItemStatus.Normal) {
            this.status = SItemStatus.Edit;
            this.grabItem(this);
        } else if (this.status == SItemStatus.Edit) {
            this.status = SItemStatus.Normal;
            this.releaseItem();
        } else if (this.status == SItemStatus.Create) {
            if (this.pointList.length > 1) {
                this.status = SItemStatus.Normal;
                this.releaseItem();
                this.$emit("finishCreated");
            }
        }
        this.$emit("onDoubleClick", event);
        return true;
    } // Function onDoubleClick()

    /***
     * 键盘按键弹起事件
     *
     * @param	event         事件参数
     */
    onKeyUp(event: KeyboardEvent): void {
        if (event.keyCode == SKeyCode.Enter) {
            if (this.pointList.length > 1) {
                if (this.status == SItemStatus.Create) {
                    this.$emit("finishCreated");
                }
                this.status = SItemStatus.Normal;
                this.releaseItem();
            }
        }
        // delete删除点
        if (
            event.keyCode == SKeyCode.Delete &&
            this.status == SItemStatus.Edit
        ) {
            this.deletePoint(this.curIndex);
        }
    } // Function onKeyUp()

    /**
     * 移动后处理所有坐标,并肩原点置为场景原点
     *
     * @param   x   x坐标
     * @param   y   y坐标
     * */
    moveToOrigin(x: number, y: number): void {
        super.moveToOrigin(x, y);
        this.pointList = this.pointList.map(t => {
            t.x = t.x + x;
            t.y = t.y + y;
            return t;
        });
        this.x = 0;
        this.y = 0;
    } // Function moveToOrigin()

    /**
     * 获取点击点与点集中距离最近点
     *
     * @param   p   鼠标点击点
     * */
    findNearestPoint(p: SPoint): void {
        let len = this.sceneDis;
        for (let i = 0; i < this.pointList.length; i++) {
            let dis = SMathUtil.pointDistance(
                p.x,
                p.y,
                this.pointList[i].x,
                this.pointList[i].y
            );
            if (dis < len) {
                len = dis;
                this.curIndex = i;
                this.curPoint = new SPoint(
                    this.pointList[this.curIndex].x,
                    this.pointList[this.curIndex].y
                );
            }
        }
    } // Function findNearestPoint()

    /**
     * 计算增加点的位置
     *
     * @param   p   鼠标点击点
     * */
    findAddPos(p: SPoint): void {
        let len = SMathUtil.pointToLine(
            p,
            new SLine(this.pointList[0], this.pointList[1])
        ),
            index = 0;
        if (this.pointList.length > 2) {
            for (let i = 1; i < this.pointList.length - 1; i++) {
                let dis = SMathUtil.pointToLine(
                    p,
                    new SLine(this.pointList[i], this.pointList[i + 1])
                );
                if (dis.MinDis < len.MinDis) {
                    len = dis;
                    index = i;
                }
            }
        }
        if (len.MinDis < this.sceneDis) {
            if (len.Point) {
                this.addPoint(len.Point, index + 1);
            }
        }
    } // Function findAddPos()

    /**
     * shift垂直水平创建或编辑
     *
     * @param   event   事件
     * */
    compare(event: SMouseEvent): SMouseEvent {
        if (this.pointList.length) {
            let last = new SPoint(event.x, event.y);
            if (this.status == SItemStatus.Create) {
                last = this.pointList[this.pointList.length - 1];
            } else if (this.status == SItemStatus.Edit) {
                if (this.curIndex > 1) {
                    last = this.pointList[this.curIndex - 1];
                }
            }
            const dx = Math.abs(event.x - last.x);
            const dy = Math.abs(event.y - last.y);
            if (dy > dx) {
                event.x = last.x;
            } else {
                event.y = last.y;
            }
        }
        return event;
    } // Function compare()

    /**
     * 记录相关动作并推入栈中
     *
     * @param	SGraphCommand   相关命令类
     * @param	any             对应传入参数
     */
    protected recordAction(SGraphCommand: any, any: any[]): void {
        // 记录相关命令并推入堆栈中
        const command = new SGraphCommand(this.scene, this, ...any);
        this.undoStack.push(command);
    } // Function recordAction()

    /**
     * Item对象边界区域
     *
     * @return  SRect   外接矩阵
     * */
    boundingRect(): SRect {
        if (this.pointList.length) {
            this.minX = this.pointList[0].x;
            this.maxX = this.pointList[0].x;
            this.minY = this.pointList[0].y;
            this.maxY = this.pointList[0].y;
            this.pointList.forEach(it => {
                let x = it.x,
                    y = it.y;
                if (x < this.minX) {
                    this.minX = x;
                }
                if (y < this.minY) {
                    this.minY = y;
                }
                if (x > this.maxX) {
                    this.maxX = x;
                }
                if (y > this.maxY) {
                    this.maxY = y;
                }
            });
        }
        return new SRect(
            this.minX,
            this.minY,
            this.maxX - this.minX,
            this.maxY - this.minY
        );
    } // Function boundingRect()

    /**
     * 判断点是否在区域内
     *
     * @param   x
     * @param   y
     * @return  true-是
     */
    contains(x: number, y: number): boolean {
        let p = new SPoint(x, y);
        for (let i = 1; i < this.pointList.length; i++) {
            let PTL = SMathUtil.pointToLine(
                p,
                new SLine(
                    this.pointList[i - 1].x,
                    this.pointList[i - 1].y,
                    this.pointList[i].x,
                    this.pointList[i].y
                )
            );
            if (PTL.MinDis < this.sceneDis) {
                return true;
            }
        }
        return false;
    } // Function contains()

    /**
     * 撤销操作
     *
     */
    undo(): void {
        if (this._status != SItemStatus.Normal) {
            this.undoStack.undo();
        }
    } // Function undo()

    /**
     * 重做操作
     *
     */
    redo(): void {
        if (this._status != SItemStatus.Normal) {
            this.undoStack.redo();
        }
    } // Function redo()

    /**
     * 取消操作执行
     *
     * */
    cancelOperate(): void {
        if (this.status == SItemStatus.Create) {
            this.parent = null;
            this.releaseItem();
        } else if (this.status == SItemStatus.Edit) {
            this.status = SItemStatus.Normal;
            this.releaseItem();
        }
    } // Function cancelOperate()

    /**
     * 绘制基本图形
     * */
    drawBaseLine(painter: SPainter): void {
        // 绘制基本图形
        painter.pen.color = this.strokeColor;
        if (this.lineStyle == SLineStyle.Dashed) {
            painter.pen.lineDash = [
                painter.toPx(this.lineWidth * 3),
                painter.toPx(this.lineWidth * 7)
            ];
        } else if (this.lineStyle == SLineStyle.Dotted) {
            painter.pen.lineDash = [
                painter.toPx(this.lineWidth),
                painter.toPx(this.lineWidth)
            ];
        }
        painter.drawPolyline(this.pointList);
    } // Function drawBaseLine()

    /**
     * Item绘制操作
     *
     * @param   painter painter对象
     */
    onDraw(painter: SPainter): void {
        // 缓存场景长度
        this.sceneDis = painter.toPx(this.dis);
        // 创建状态
        painter.pen.lineWidth = painter.toPx(this.lineWidth);
        if (this.status == SItemStatus.Create && this.lastPoint) {
            // 绘制基本图形
            this.drawBaseLine(painter);
            painter.drawLine(
                this.pointList[this.pointList.length - 1],
                this.lastPoint
            );
            // 编辑状态
            this.pointList.forEach((t, i): void => {
                painter.brush.color = SColor.White;
                if (i == this.curIndex) {
                    painter.brush.color = this.fillColor;
                }
                painter.drawCircle(t.x, t.y, painter.toPx(5));
            });
        } else if (this.status == SItemStatus.Edit) {
            // 绘制基本图形
            this.drawBaseLine(painter);
            // 编辑状态
            this.pointList.forEach((t, i): void => {
                painter.brush.color = SColor.White;
                if (i == this.curIndex) {
                    painter.brush.color = this.fillColor;
                }
                painter.drawCircle(t.x, t.y, painter.toPx(5));
            });
        } else {
            // 查看状态
            if (this.selected) {
                painter.pen.lineWidth = painter.toPx(this.lineWidth * 2);
                painter.shadow.shadowBlur = 10;
                painter.shadow.shadowColor = new SColor(`#00000033`);
                painter.shadow.shadowOffsetX = 5;
                painter.shadow.shadowOffsetY = 5;
            }
            // 绘制基本图形
            this.drawBaseLine(painter);
        }
    } // Function onDraw()
} // Class SPolylineItem