/*
 * *********************************************************************************************************************
 *
 *          !!
 *        .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 { SMouseButton, SMouseEvent, SUndoStack } from "@persagy-web/base";
import { SMathUtil } from "@persagy-web/big/lib/utils/SMathUtil";
import { SItemStatus } from "@persagy-web/big";
import {
    SGraphPointListInsert,
    SGraphPointListUpdate,
    SLineStyle,
    SGraphItem
} from "@persagy-web/graph/";
import { SGraphEdit } from "./../"

/**
 * 直线编辑类
 *
 * @author  韩耀龙(han_yao_long@163.com)
 */
export class SLineEdit 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;

    /** 线段   */
    private _line: SPoint[] = [];
    get line(): SPoint[] {
        return this._line;
    }
    set line(arr: SPoint[]) {
        this._line = arr;
        this.update();
    }

    /** 是否垂直水平绘制   */
    private _verAndLeve: Boolean = false;
    get verAndLeve(): Boolean {
        return this._verAndLeve;
    }
    set verAndLeve(bool: Boolean) {
        this._verAndLeve = bool;
        this.update();
    }

    /** 是否完成绘制  */
    protected _status: SItemStatus = SItemStatus.Normal;
    get status(): SItemStatus {
        return this._status;
    }
    set status(v: SItemStatus) {
        this._status = v;
        this.undoStack.clear();
        this.update();
    }

    /** 线条颜色    */
    private _strokeColor: SColor = SColor.Black;
    get strokeColor(): SColor {
        return this._strokeColor;
    }
    set strokeColor(v: SColor) {
        this._strokeColor = v;
        this.update();
    }

    /** 线条样式    */
    private _lineStyle: SLineStyle = SLineStyle.Solid;
    get lineStyle(): SLineStyle {
        return this._lineStyle;
    }
    set lineStyle(v: SLineStyle) {
        this._lineStyle = v;
        this.update();
    }

    /** 端点填充色 */
    private _fillColor: SColor = SColor.White;
    get fillColor(): SColor {
        return this._fillColor;
    }
    set fillColor(v: SColor) {
        this._fillColor = v;
        this.update();
    }

    /** 选中端点填充色 */
    private _activeFillColor: SColor = new SColor("#2196f3");
    get activeFillColor(): SColor {
        return this._activeFillColor;
    }
    set activeFillColor(v: SColor) {
        this._activeFillColor = v;
        this.update();
    }

    /** 线条宽度    */
    private _lineWidth: number = 1;
    get lineWidth(): number {
        return this._lineWidth;
    }
    set lineWidth(v: number) {
        console.log('lineWidth',v)
        this._lineWidth = v;
        this.update();
    }

    /** 拖动灵敏度   */
    dis: number = 5;

    /** 拖动灵敏度   */
    private sceneDis: number = 5;

    /** 当前点索引   */
    curIndex: number = -1;

    /** 当前点坐标   */
    private curPoint: SPoint | null = null;

    /** undo/redo堆栈 */
    private undoStack: SUndoStack = new SUndoStack();

    /**
     * 构造函数
     *
     * @param   parent  父级
     * */
    constructor(parent: SGraphItem | null);

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

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

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

    /**
     * 添加点至数组中
     *
     * @param   p       添加的点
     * */
    private addPoint(p: SPoint): void {
        if (this.line.length < 2) {
            this.line.push(p);
            this.recordAction(SGraphPointListInsert, [this.line, p]);
        } else {
            this.line[1] = p;
            this.recordAction(SGraphPointListInsert, [this.line, p, 1]);
            this.status = SItemStatus.Normal;
            this.releaseItem();
            this.$emit("finishCreated");
        }
        this.update();
    } // Function addPoint()

    /**
     * 鼠标双击事件
     *
     * @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();
        }
        this.update();
        return true;
    } // Function onDoubleClick()

    /**
     * 鼠标按下事件
     *
     * @param   event   鼠标事件
     * @return  是否处理事件
     * */
    onMouseDown(event: SMouseEvent): boolean {
        this.curIndex = -1;
        this.curPoint = null;
        if (event.shiftKey || this._verAndLeve) {
            event = this.compare(event);
        }
        if (event.buttons == SMouseButton.LeftButton) {
            if (this.status == SItemStatus.Normal) {
                return super.onMouseDown(event);
            } else if (this.status == SItemStatus.Edit) {
                // 判断是否点击到端点上(获取端点索引值)
                this.findNearestPoint(new SPoint(event.x, event.y));
            } else if (this.status == SItemStatus.Create) {
                this.addPoint(new SPoint(event.x, event.y));
                return true;
            }
        }
        return true;
    } // Function onMouseDown()

    /**
     * 鼠标抬起事件
     *
     * @param	event         事件参数
     * @return	boolean
     */
    onMouseUp(event: SMouseEvent): boolean {
        if (this.status == SItemStatus.Edit) {
            if (this.curIndex > -1) {
                const p = new SPoint(
                    this.line[this.curIndex].x,
                    this.line[this.curIndex].y
                );
                this.recordAction(SGraphPointListUpdate, [
                    this.line,
                    this.curPoint,
                    p,
                    this.curIndex
                ]);
            }
        } else if (this.status == SItemStatus.Normal) {
            this.moveToOrigin(this.x, this.y);
            return super.onMouseUp(event);
        }
        this.curIndex = -1;
        this.curPoint = null;
        return true;
    } // Function onMouseUp()

    /**
      * 鼠标抬起事件
      *
      * @param	event         事件参数
      * @return	boolean
      */
    onKeyUp(event: KeyboardEvent): void {
        // 当 ESC 时
        if (27 == event.keyCode) {
            if (this.status == SItemStatus.Edit) {
                this.status = SItemStatus.Normal;
                this.releaseItem();
            } else if (this.status == SItemStatus.Create) {
                this.update()
                this.releaseItem();
                this.parent = null;
            }
        }
    } // Function onKeyUp()

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

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

    /**
     * 记录相关动作并推入栈中
     *
     * @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()

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

    /**
     * shift垂直水平创建或编辑
     *
     * @param   event   事件
     * */
    compare(event: SMouseEvent): SMouseEvent {
        if (this.line.length) {
            let last = new SPoint(event.x, event.y);
            if (this.status == SItemStatus.Create) {
                last = this.line[0];
            } else if (this.status == SItemStatus.Edit) {
                if (this.curIndex == 1) {
                    last = this.line[0];
                } else if (this.curIndex == 0) {
                    last = this.line[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   x
     * @param   y
     * @return  true-是
     */
    contains(x: number, y: number): boolean {
        if (this.line.length == 2) {
            let p = new SPoint(x, y);
            if (
                SMathUtil.pointToLine(p, new SLine(this.line[0], this.line[1]))
                    .MinDis < this.dis
            ) {
                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()

    /**
     * 取消操作item事件
     *
     * */
    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()

    /**
     * Item对象边界区域
     *
     * @return	SRect
     */
    boundingRect(): SRect {
        if (this.line.length) {
            this.minX = this.line[0].x;
            this.maxX = this.line[0].x;
            this.minY = this.line[0].y;
            this.maxY = this.line[0].y;
            this.line.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()

    /**
     * Item绘制操作
     *
     * @param   painter painter对象
     */
    onDraw(painter: SPainter): void {
        this.sceneDis = painter.toPx(this.dis);
        painter.pen.lineWidth = painter.toPx(this.lineWidth);
        painter.pen.color = this.strokeColor;
        if (this.line.length == 2) {
            // 绘制直线
            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(2 * this.lineWidth),
                    painter.toPx(2 * this.lineWidth)
                ];
            }
            if (this.selected && this.status == SItemStatus.Normal) {
                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;
            }
            painter.drawLine(this.line[0], this.line[1]);
            if (
                this.status == SItemStatus.Edit ||
                this.status == SItemStatus.Create
            ) {
                // 绘制端点
                this.line.forEach((p, i): void => {
                    painter.brush.color = this.fillColor;
                    if (i == this.curIndex) {
                        painter.brush.color = this.activeFillColor;
                    }
                    painter.drawCircle(p.x, p.y, painter.toPx(5));
                });
            }
        } else if (this.line.length == 1) {
            if (
                this.status == SItemStatus.Edit ||
                this.status == SItemStatus.Create
            ) {
                // 绘制端点
                painter.brush.color = this.fillColor;
                painter.drawCircle(
                    this.line[0].x,
                    this.line[0].y,
                    painter.toPx(5)
                );
            }
        }
    } // Function onDraw()
} // Class SLineItem