import {
    SGraphItem,
    SGraphPointListDelete,
    SGraphPointListInsert,
    SGraphPointListUpdate,
    SLineStyle
} from "@persagy-web/graph/lib";
import { SKeyCode, SMouseEvent, SUndoStack } from "@persagy-web/base/";
import {
    SColor,
    SLine,
    SLineCapStyle,
    SPainter,
    SPoint,
    SPolygonUtil,
    SRect
} from "@persagy-web/draw";
import { SItemStatus } from "@persagy-web/big";
import { SMathUtil } from "@persagy-web/big/lib/utils/SMathUtil";

/**
 * 编辑多边形
 *
 * @author  韩耀龙
 */
export class EditPolygonItem extends SGraphItem {
    /** 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;

    /** 轮廓线坐标  */
    private pointList: SPoint[] = [];
    // 获取当前状态
    get getPointList(): SPoint[] {
        return this.pointList;
    }
    // 编辑当前状态
    set setPointList(arr: SPoint[]) {
        this.pointList = arr;
        this.update();
    }
    /** 是否垂直水平绘制   */
    private _verAndLeve: Boolean = false;
    get verAndLeve(): Boolean {
        return this._verAndLeve;
    }
    set verAndLeve(bool: Boolean) {
        this._verAndLeve = bool;
        this.update();
    }
    // 当前状态
    protected _status: number = SItemStatus.Normal;
    // 获取当前状态
    get status(): SItemStatus {
        return this._status;
    }
    // 编辑当前状态
    set status(value: SItemStatus) {
        this._status = value;
        this.undoStack.clear();
        this.update();
    }

    /** 边框颜色 */
    _strokeColor: SColor = new SColor("#0091FF");
    /**  画笔颜色 */
    get strokeColor(): SColor {
        return this._strokeColor;
    }
    set strokeColor(v: SColor) {
        this._strokeColor = v;
        this.update();
    }

    /** 填充颜色 */
    _fillColor: SColor = new SColor("#1EE887");
    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 = 4;
    get lineWidth(): number {
        return this._lineWidth;
    }
    set lineWidth(v: number) {
        this._lineWidth = v;
        this.update();
    }

    /** 是否闭合    */
    closeFlag: boolean = false;
    /** 鼠标移动点  */
    private lastPoint: SPoint | null = null;
    /** 当前鼠标获取顶点对应索引 */
    private curIndex: number = -1;
    /** 当前鼠标获取顶点对应坐标 */
    private curPoint: null | SPoint = null;
    /** 灵敏像素 */
    private len: number = 10;
    /** 场景像素 内部将灵敏像素换算为场景实际距离  */
    private scenceLen: number = 15;
    /** 场景像素  */
    private isAlt: boolean = false;
    /** undoredo堆栈 */
    protected undoStack: SUndoStack = new SUndoStack();

    /**
     * 构造函数
     *
     * @param parent    指向父对象
     */
    constructor(parent: SGraphItem | null) {
        super(parent);
    }

    //////////////////
    //  以下为对pointList 数组的操作方法

    /**
     * 储存新的多边形顶点
     *
     * @param x   点位得x坐标
     * @param y   点位得y坐标
     * @param i   储存所在索引
     * @return SPoint。添加的顶点
     */
    insertPoint(x: number, y: number, i: number | null = null): SPoint {
        const point = new SPoint(x, y);
        if (i == null) {
            this.pointList.push(point);
        } else {
            this.pointList.splice(i, 0, point);
        }
        this.update();
        return point;
    }

    /**
     * 删除点位
     *
     * @param i   删除点所在的索引
     * @return    SPoint|null。索引不在数组范围则返回null
     */
    deletePoint(i: number | null = null): SPoint | null {
        let point = null;
        if (i != null) {
            if (i >= this.pointList.length || i < 0) {
                point = null;
            } else {
                point = new SPoint(this.pointList[i].x, this.pointList[i].y);
                this.pointList.splice(i, 1);
            }
        } else {
            if (this.pointList.length) {
                point = this.pointList[this.pointList.length - 1];
                this.pointList.pop();
            } else {
                point = null;
            }
        }
        this.curIndex = -1;
        this.curPoint = null;
        this.update();
        return point;
    }

    /**
     * 多边形顶点的移动位置
     *
     * @param x   点位得x坐标
     * @param y   点位得y坐标
     * @param i   点位得i坐标
     * @return    移动对应得点。如果索引无法找到移动顶点，则返回null
     */
    movePoint(x: number, y: number, i: number): SPoint | null {
        let point = null;
        if (i >= this.pointList.length || i < 0) {
            return null;
        }
        if (this.pointList.length) {
            this.pointList[i].x = x;
            this.pointList[i].y = y;
        }
        point = this.pointList[i];
        return point;
    }

    /**
     * 打印出多边形数组
     *
     * @return  顶点数组
     */
    PrintPointList(): SPoint[] {
        return this.pointList;
    }

    ////////////
    //  以下为三种状态下的绘制多边形方法

    /**
     * 展示状态 --绘制多边形数组
     *
     * @param painter      绘制类
     * @param pointList    绘制多边形数组
     */
    protected drawShowPolygon(painter: SPainter, pointList: SPoint[]): void {
        painter.save();
        painter.pen.lineCapStyle = SLineCapStyle.Square;
        painter.pen.color = this.strokeColor;
        painter.brush.color = this.fillColor;
        painter.pen.lineWidth = painter.toPx(this.lineWidth);
        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)
            ];
        }
        if (this.selected) {
            painter.shadow.shadowBlur = 10;
            painter.shadow.shadowColor = new SColor(`#00000033`);
            painter.shadow.shadowOffsetX = 5;
            painter.shadow.shadowOffsetY = 5;
        } else {
            painter.shadow.shadowColor = SColor.Transparent;
        }
        painter.drawPolygon([...pointList]);
        painter.restore();
    }

    /**
     * 创建状态 --绘制多边形数组
     *
     * @param painter      绘制类
     * @param pointList    绘制多边形数组
     */
    protected drawCreatePolygon(painter: SPainter, pointList: SPoint[]): void {
        painter.pen.lineCapStyle = SLineCapStyle.Square;
        painter.pen.color = this.strokeColor;
        painter.pen.lineWidth = painter.toPx(this.lineWidth);
        if (this.lastPoint && pointList.length) {
            painter.drawLine(
                pointList[pointList.length - 1].x,
                pointList[pointList.length - 1].y,
                this.lastPoint.x,
                this.lastPoint.y
            );
        }
        painter.drawPolyline(pointList);
        painter.pen.color = SColor.Transparent;
        painter.brush.color = new SColor(this.fillColor.value);
        painter.pen.lineWidth = painter.toPx(this.lineWidth);

        if (this.lastPoint) {
            painter.drawPolygon([...pointList, this.lastPoint]);
            // 绘制顶点块
            painter.pen.color = SColor.Black;
            painter.brush.color = SColor.White;
            pointList.forEach(item => {
                painter.drawCircle(item.x, item.y, painter.toPx(this.len / 2));
            });
            // 如果最后一个点在第一个点的灵敏度范围内,第一个点填充变红
            if (this.pointList.length) {
                if (
                    SMathUtil.pointDistance(
                        this.lastPoint.x,
                        this.lastPoint.y,
                        this.pointList[0].x,
                        this.pointList[0].y
                    ) < this.scenceLen
                ) {
                    // 绘制第一个点的顶点块
                    painter.pen.color = SColor.Black;
                    painter.brush.color = SColor.Red;
                    painter.drawCircle(
                        this.pointList[0].x,
                        this.pointList[0].y,
                        painter.toPx(this.len / 2)
                    );
                }
            }
        } else {
            painter.drawPolygon(pointList);
        }
    }

    /**
     *
     * 编辑状态 --绘制多边形数组
     *
     * @param painter    绘制类
     * @param pointList    绘制多边形数组
     */
    protected drawEditPolygon(painter: SPainter, pointList: SPoint[]): void {
        // 展示多边形
        painter.pen.lineCapStyle = SLineCapStyle.Square;
        painter.pen.color = this.strokeColor;
        painter.pen.lineWidth = painter.toPx(this.lineWidth);
        painter.brush.color = new SColor(this.fillColor.value);
        painter.drawPolygon([...pointList]);
        // 绘制顶点块
        painter.pen.color = SColor.Black;
        painter.brush.color = SColor.White;
        pointList.forEach((item, index) => {
            painter.brush.color = SColor.White;
            if (index == this.curIndex) {
                painter.brush.color = new SColor("#2196f3");
            }
            painter.drawCircle(item.x, item.y, painter.toPx(this.len / 2));
        });
    }

    /**
     * 编辑状态操作多边形数组
     *
     * @param event    鼠标事件
     *
     *
     */
    protected editPolygonPoint(event: SMouseEvent): void {
        //  判断是否为删除状态 isAlt = true为删除状态
        if (this.isAlt) {
            // 1 判断是否点击在多边形顶点
            let lenIndex = -1; // 当前点击到的点位索引；
            let curenLen = this.scenceLen; // 当前的灵敏度
            this.pointList.forEach((item, index) => {
                let dis = SMathUtil.pointDistance(
                    event.x,
                    event.y,
                    item.x,
                    item.y
                );
                if (dis < curenLen) {
                    curenLen = dis;
                    lenIndex = index;
                }
            });
            // 若点击到，对该索引对应的点做删除
            if (lenIndex != -1) {
                if (this.pointList.length <= 3) {
                    return;
                }
                const delePoint = new SPoint(
                    this.pointList[lenIndex].x,
                    this.pointList[lenIndex].y
                );
                this.deletePoint(lenIndex);
                // 记录顶点操作记录压入堆栈
                this.recordAction(SGraphPointListDelete, [
                    this.pointList,
                    delePoint,
                    lenIndex
                ]);
            }
        } else {
            // 1 判断是否点击在多边形顶点
            this.curIndex = -1;
            this.curPoint = null;
            let lenIndex = -1; // 当前点击到的点位索引；
            let curenLen = this.scenceLen; // 当前的灵敏度
            this.pointList.forEach((item, index) => {
                let dis = SMathUtil.pointDistance(
                    event.x,
                    event.y,
                    item.x,
                    item.y
                );
                if (dis < curenLen) {
                    curenLen = dis;
                    lenIndex = index;
                }
            });
            this.curIndex = lenIndex;
            // 2判断是否点击在多边形得边上
            if (-1 == lenIndex) {
                let len = SMathUtil.pointToLine(
                    new SPoint(event.x, event.y),
                    new SLine(this.pointList[0], this.pointList[1])
                ),
                    index = 0;
                if (this.pointList.length > 2) {
                    for (let i = 1; i < this.pointList.length; i++) {
                        let dis = SMathUtil.pointToLine(
                            new SPoint(event.x, event.y),
                            new SLine(this.pointList[i], this.pointList[i + 1])
                        );
                        if (i + 1 == this.pointList.length) {
                            dis = SMathUtil.pointToLine(
                                new SPoint(event.x, event.y),
                                new SLine(this.pointList[i], this.pointList[0])
                            );
                        }
                        if (dis.MinDis < len.MinDis) {
                            len = dis;
                            index = i;
                        }
                    }
                }
                // 判断是否有点
                if (len.Point) {
                    // 点在了多边形的边上
                    if (len.MinDis <= this.scenceLen) {
                        this.pointList.splice(index + 1, 0, len.Point);
                        // 记录新增顶点操作记录压入堆栈
                        this.recordAction(SGraphPointListInsert, [
                            this.pointList,
                            len.Point,
                            index + 1
                        ]);
                    } else {
                        //没点在多边形边上也没点在多边形顶点上
                        super.onMouseDown(event);
                    }
                }
            } else {
                // 当捕捉到顶点后 ，记录当前点的xy坐标，用于undo、redo操作
                this.curPoint = new SPoint(
                    this.pointList[this.curIndex].x,
                    this.pointList[this.curIndex].y
                );
            }
            // 刷新视图
            this.update();
        }
    }

    /////////////////////
    // undo、redo相关操作

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

    /**
     * 执行取消操作执行
     */
    undo(): void {
        if (this.status == SItemStatus.Normal) {
            return;
        }
        this.undoStack.undo();
    }

    /**
     * 执行重做操作执行
     */
    redo(): void {
        if (this.status == SItemStatus.Normal) {
            return;
        }
        this.undoStack.redo();
    }

    ///////////////////////////////
    // 以下为鼠标事件

    /**
     * 鼠标双击事件
     *
     * @param	event         事件参数
     * @return	boolean
     */
    onDoubleClick(event: SMouseEvent): boolean {
        // 如果位show状态 双击改对象则需改为编辑状态
        if (SItemStatus.Normal == this.status) {
            this.status = SItemStatus.Edit;
            this.grabItem(this);
        } else if (SItemStatus.Edit == this.status) {
            this.status = SItemStatus.Normal;
            this.releaseItem();
        }
        this.update();
        return true;
    } // Function onDoubleClick()

    /**
     * 键盘事件
     *
     * @param	event         事件参数
     * @return	boolean
     */

    onKeyDown(event: KeyboardEvent): boolean {
        if (this.status == SItemStatus.Normal) {
            return false;
        } else if (this.status == SItemStatus.Create) {
            if (event.code == "Enter") {
                // 当顶点大于二个时才又条件执行闭合操作并清空堆栈
                if (this.pointList.length > 2) {
                    this.status = SItemStatus.Normal;
                    //3 传递完成事件状态
                    this.$emit("finishCreated");
                    //1 grabItem 置为null
                    this.releaseItem();
                }
            }
        } else if (this.status == SItemStatus.Edit) {
            if (event.key == "Alt") {
                this.isAlt = true;
            }
        }
        this.update();
        return true;
    } // Function onKeyDown()

    /**
     * 键盘键抬起事件
     *
     * @param	event         事件参数
     * @return	boolean
     */
    onKeyUp(event: KeyboardEvent): void {
        if (this.status == SItemStatus.Edit) {
            if (event.key == "Alt") {
                this.isAlt = false;
            } else if (event.keyCode == SKeyCode.Delete) {
                // 当多边形的顶点大于三个允许删除点
                if (this.pointList.length > 3) {
                    this.deletePoint(this.curIndex);
                }
            }
        }
        this.update();
    } // Function onKeyUp()

    /**
     * 鼠标按下事件
     *
     * @param	event         事件参数
     * @return	boolean
     */
    onMouseDown(event: SMouseEvent): boolean {
        if (event.shiftKey || this.verAndLeve) {
            event = this.compare(event);
        }
        // 如果状态为编辑状态则添加点
        if (this.status == SItemStatus.Create) {
            // 新增顶点
            let len = -1;
            if (this.pointList.length) {
                len = SMathUtil.pointDistance(
                    event.x,
                    event.y,
                    this.pointList[0].x,
                    this.pointList[0].y
                );
            }
            if (this.pointList.length > 2 && len > 0 && len < this.scenceLen) {
                this.status = SItemStatus.Normal;
                //3 传递完成事件状态
                this.$emit("finishCreated");
                //1 grabItem 置为null
                this.releaseItem();
            } else {
                this.insertPoint(event.x, event.y);
                // 记录新增顶点操作记录压入堆栈
                let pos = new SPoint(event.x, event.y);
                this.recordAction(SGraphPointListInsert, [this.pointList, pos]);
            }
        } else if (this.status == SItemStatus.Edit) {
            // 对多边形数组做编辑操作
            this.editPolygonPoint(event);
        } else {
            return super.onMouseDown(event);
        }
        return true;
    } // Function onMouseDown()

    /**
     * 鼠标移入事件
     *
     * @param	event         事件参数
     * @return	boolean
     */
    onMouseEnter(event: SMouseEvent): boolean {
        return true;
    } // Function onMouseEnter()

    /**
     * 鼠标移出事件
     *
     * @param	event         事件参数
     * @return	boolean
     */

    onMouseLeave(event: SMouseEvent): boolean {
        return true;
    } // Function onMouseLeave()

    /**
     * 鼠标移动事件
     *
     * @param	event         事件参数
     * @return	boolean
     */

    onMouseMove(event: SMouseEvent): boolean {
        if (event.shiftKey || this.verAndLeve) {
            event = this.compare(event);
        }
        if (this.status == SItemStatus.Create) {
            this.lastPoint = new SPoint();
            this.lastPoint.x = event.x;
            this.lastPoint.y = event.y;
            this.update();
        } else if (this.status == SItemStatus.Edit) {
            if (event.buttons == 1) {
                if (-1 != this.curIndex) {
                    this.pointList[this.curIndex].x = event.x;
                    this.pointList[this.curIndex].y = event.y;
                }
            }
            this.update();
        } else {
            return super.onMouseMove(event);
        }
        return true;
    } // Function onMouseMove()

    /**
     * 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	event         事件参数
     * @return	boolean
     */
    onMouseUp(event: SMouseEvent): boolean {
        if (this.status == SItemStatus.Edit) {
            if (-1 != this.curIndex) {
                const pos = new SPoint(
                    this.pointList[this.curIndex].x,
                    this.pointList[this.curIndex].y
                );
                this.recordAction(SGraphPointListUpdate, [
                    this.pointList,
                    this.curPoint,
                    pos,
                    this.curIndex
                ]);
            }
        } else if (this.status == SItemStatus.Normal) {
            this.moveToOrigin(this.x, this.y);
            return super.onMouseUp(event);
        }
        return true;
    } // Function onMouseUp()

    /**
     * 移动后处理所有坐标,并肩原点置为场景原点
     *
     * @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	event         事件参数
     * @return	boolean
     */
    onResize(event: SMouseEvent): boolean {
        return true;
    } // Function onResize()

    /**
     * 取消操作
     *
     */
    cancelOperate(): void {
        // 当状态为展示状态
        if (this.status == SItemStatus.Create) {
            // 闭合多边形
            this.parent = null;
        } else if (this.status == SItemStatus.Edit) {
            // 编辑状态
            this.status = SItemStatus.Normal;
        }
        this.update();
    } // Function cancelOperate()

    /**
     * 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
     */
    contains(x: number, y: number): boolean {
        let arr = this.pointList;
        if (arr.length < 3 || !SPolygonUtil.pointIn(x, y, arr)) {
            return false;
        }
        return true;
    } // Function contains()

    /**
     * Item绘制操作
     *
     * @param   painter       painter对象
     */
    onDraw(painter: SPainter): void {
        this.scenceLen = painter.toPx(this.len);
        // 当状态为展示状态
        if (this.status == SItemStatus.Normal) {
            // 闭合多边形
            this.drawShowPolygon(painter, this.pointList);
        } else if (this.status == SItemStatus.Create) {
            // 创建状态
            this.drawCreatePolygon(painter, this.pointList);
        } else {
            // 编辑状态
            this.drawEditPolygon(painter, this.pointList);
        }
    } // Function onDraw()
}