import { fabric } from 'fabric'
import {EventEmitter} from 'events'

const finishIcon = "data:image/svg+xml;base64,PHN2Zw0KICAgICAgICB0PSIxNjY1OTc1ODY3NzczIg0KICAgICAgICBjbGFzcz0iaWNvbiINCiAgICAgICAgdmlld0JveD0iMCAwIDEwMjQgMTAyNCINCiAgICAgICAgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjEwMzQ2IiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+DQogICAgPHBhdGggZD0iTTc3Mi4yNjY2NjcgMzU2LjI2NjY2N2wtMzU5LjY4IDM2MC45NmEzNC45ODY2NjcgMzQuOTg2NjY3IDAgMCAxLTguNTMzMzM0IDYuODI2NjY2IDM1Ljg0IDM1Ljg0IDAgMCAxLTQ5LjA2NjY2Ni0xMy42NTMzMzNsLTEwOS4yMjY2NjctMTkwLjI5MzMzM2EzNS44NCAzNS44NCAwIDAgMSA2Mi4yOTMzMzMtMzUuODRsODUuMzMzMzM0IDE0OS4zMzMzMzMgMzI3LjY4LTMyOC41MzMzMzNhMzUuODQgMzUuODQgMCAwIDEgNTEuMiA1MC43NzMzMzN6TTUxMiAwYTUxMiA1MTIgMCAxIDAgNTEyIDUxMkE1MTIgNTEyIDAgMCAwIDUxMiAweiINCiAgICAgICAgICBmaWxsPSIjZmYyYTAwIiBwLWlkPSIxMDM0NyI+DQogICAgPC9wYXRoPg0KPC9zdmc+DQo="
const rectangleDefault = [{x: 0.001, y: 0.001}, {x: 0.998, y: 0.001}, {x: 0.998, y: 0.998}, {x: 0.001, y: 0.998}]

// 矩形框绘制
export class rectDraw {
    private cardID: string
    private canvas: any
    private fabricDom: HTMLCanvasElement
    private rectangle: HTMLDivElement
    private currentType: string
    private downObject: any
    private drawing = false
    private proportion: any = []
    private initialPoint: any = []
    private style = {
        color: 'rgba(255, 42, 0, .9)',
        border: 'rgba(255, 42, 0, .4)',
        strokeWidth: 2
    }
    emitter: any

    // 确认图标
    renderIcon(icon) {
        return function (ctx, left, top, styleOverride, fabricObject) {
            const size = this.cornerSize;
            ctx.save();
            ctx.translate(left, top);
            ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle));
            ctx.drawImage(icon, -size / 2, -size / 2, size, size);
            ctx.restore();
        }
    }

    constructor(videoDom, cardId, dom) {
        const [rectangle, fabricDom] = dom
        this.cardID = cardId
        this.rectangle = rectangle
        this.fabricDom = fabricDom
        videoDom.appendChild(rectangle)
        this.emitter = new EventEmitter()
        /* const handleIcon = (eventData, transform) => {
            this.emitter.emit('complete')
        }
        const finishImg = document.createElement('img')
        finishImg.src = finishIcon
        fabric.Object.prototype.controls.deleteControl = new fabric.Control({
            x: 0.5,
            y: -0.5,
            offsetY: -15,
            offsetX: 15,
            cursorStyle: 'pointer',
            mouseUpHandler: handleIcon,
            render: this.renderIcon(finishImg),
            cornerSize: 18
        }) */
    }

    on(event:any, listener:any) {
        this.emitter.addListener(event, listener);
    }

    off(event:any, listener:any) {
        this.emitter.removeListener(event, listener);
    }

    /*创建绘画/编辑绘画*/
    async begin() {
        this.drawing = true
        this.rectangle.style.zIndex = '9'
        this.drawType('polygon')
        // 赋值初始值
        this.initialPoint = [...this.proportion]
    }

    setDom(width, height) {
        if (this.canvas) {
            this.canvas.dispose()
            this.canvas = undefined
        }
        this.fabricDom.width = width
        this.fabricDom.height = height
        this.canvas = new fabric.Canvas(this.cardID, {
            uniformScaling: false,
            preserveObjectStacking: true
        })
        this.setInfo()
    }

    setInfo() {
        /*
            const canvasMouseDown = (e) => {
                this.downObject = e
                this.downPoint = e.absolutePointer
            }
            const canvasMouseUp = (e) => {
                if (this.currentType === 'rect') {
                    // 创建矩形
                    createRect(e.absolutePointer)
                }
            }
            // 创建矩形
            const createRect = (pointer) => {
                const {downPoint, canvas, style} = this
                // 矩形参数计算
                const top = Math.min(downPoint.y, pointer.y)
                const left = Math.min(downPoint.x, pointer.x)
                const width = Math.abs(downPoint.x - pointer.x)
                const height = Math.abs(downPoint.y - pointer.y)
                // 矩形对象
                const rect = new fabric.Rect({
                    top,
                    left,
                    width,
                    height,
                    fill: 'transparent',
                    strokeWidth: style.strokeWidth,
                    stroke: style.color,
                    scaleX: 1,
                    scaleY: 1,
                    strokeUniform: true, // 变形后保持宽度
                    objectCaching: false // 高速缓存，可在变形时减少虚化
                })
                // 将矩形添加到画布上
                rect.setControlsVisibility({mtr: false}) // 取消旋转手柄
                canvas.add(rect)
                this.downPoint = null
                if (canvas.getObjects().length > 0) {
                    this.drawType('select')
                }
            }
            this.canvas.on('mouse:down', canvasMouseDown) // 鼠标在画布上按下
            this.canvas.on('mouse:up', canvasMouseUp) // 鼠标在画布上松开
        */

        // 双击事件
        const canvasDlclick = (e) => {
            const ePointer = e.pointer
            const oCoords = e.target.oCoords
            const polygons = e.target.points
            const offsetX = oCoords.p0.x - polygons[0].x
            const offsetY = oCoords.p0.y - polygons[0].y
            const offsetPointer = {
                x: ePointer.x - offsetX,
                y: ePointer.y - offsetY
            }
            // 双击位置是否与点重叠
            const index = polygons.reduce((prv, v, i) => {
                if (v.x + 3 > offsetPointer.x && offsetPointer.x > v.x - 3 && v.y + 3 > offsetPointer.y && offsetPointer.y > v.y - 3) {
                    prv = i
                }
                return prv
            }, -1)
            // 添加～删除～点
            if (index < 0) {
                // 添加点
                const integers = polygons.map(v => {
                    return {
                        x: ~~v.x,
                        y: ~~v.y
                    }
                })
                const operateObj = pointIsAdd(offsetPointer, integers)
                if (operateObj) {
                    polygons.splice(operateObj.index, 0, offsetPointer)
                    this.drawType('polygon')
                }
            } else if (polygons.length > 3) {
                // 删除点
                polygons.splice(index, 1)
                this.drawType('polygon')
            }

            // 判断是否有添加点
            function pointIsAdd(point, segments) {
                const mistake = []
                for (let i = 1; i < segments.length + 1; i++) {
                    const ps = segments[i - 1]
                    const pe = i === segments.length ? segments[0] : segments[i]
                    const extent: number = distanceOfPointAndLine(ps.x, ps.y, pe.x, pe.y, point.x, point.y)
                    // 距离在2.5之内
                    if (extent < 2.5) {
                        mistake.push({
                            extent,
                            pStart: ps,
                            pEnd: pe,
                            index: i
                        })
                    }
                }
                if (mistake.length > 0) {
                    return mistake.reduce((prv, cur) => {
                        return prv.extent < cur.extent ? prv : cur
                    }, mistake[0])
                }
                return null
            }

            // 点到线段距离
            function distanceOfPointAndLine(x1, y1, x2, y2, x, y) {
                const A = x - x1;
                const B = y - y1;
                const C = x2 - x1;
                const D = y2 - y1;
                const dot = A * C + B * D;
                const len_sq = C * C + D * D;
                let param = -1;
                if (len_sq != 0) { //线段长度不能为0
                    param = dot / len_sq;
                }
                let xx, yy;
                if (param < 0) {
                    xx = x1;
                    yy = y1;
                } else if (param > 1) {
                    xx = x2;
                    yy = y2;
                } else {
                    xx = x1 + param * C;
                    yy = y1 + param * D;
                }
                const dx = x - xx;
                const dy = y - yy;
                return Math.sqrt(dx * dx + dy * dy);
            }
        }
        this.canvas.on('mouse:dblclick', canvasDlclick)
        this.rectangle.style.zIndex = '1'
        // 有参数时自动绘制
        if (this.proportion.length > 0) {
            this.resetRect()
        }
    }

    // 绘画多边形
    resetRect() {
        const {canvas, style, proportion} = this
        const {clientHeight, clientWidth} = this.fabricDom
        canvas.remove(canvas.getActiveObject())
        const points = proportion.map(v => {
            return {
                x: v.x * clientWidth,
                y: v.y * clientHeight
            }
        })
        const polygon = new fabric.Polygon(points, {
            fill: 'transparent',
            strokeWidth: style.strokeWidth,
            stroke: style.color,
            scaleX: 1,
            scaleY: 1,
            objectCaching: false,
            transparentCorners: false,
            cornerColor: 'blue',
        });
        canvas.add(polygon);
        /*const {canvas, proportion, style} = this
        const {clientHeight, clientWidth} = this.fabricDom
        const {x, y, width, height} = proportion
        const rect = new fabric.Rect({
            top: y * clientHeight,
            left: x * clientWidth,
            width: width * clientWidth,
            height: height * clientHeight,
            fill: 'transparent',
            strokeWidth: style.strokeWidth,
            stroke: style.color,
            scaleX: 1,
            scaleY: 1,
            strokeUniform: true, // 变形后保持宽度
            objectCaching: false // 高速缓存，可在变形时减少虚化
        })
        rect.setControlsVisibility({mtr: false})
        canvas.add(rect)*/
        this.drawType('')
    }

    drawType(type: string) {
        this.currentType = type
        const {canvas, style} = this
        switch (type) {
            case '':
                canvas.selection = false
                canvas.skipTargetFind = true
                break
            case 'select':
                canvas.selection = true
                canvas.selectionColor = 'rgba(255, 255, 255, 0.01)'
                canvas.selectionBorderColor = 'rgba(255, 255, 255, 0.5)'
                canvas.skipTargetFind = false // 允许选中
                break
            case 'rect':
                canvas.selectionColor = 'transparent'
                canvas.selectionBorderColor = style.border
                canvas.skipTargetFind = true // 禁止选中
                break
            case 'polygon':
                polygonEdit()
                canvas.skipTargetFind = false
                break
        }
        // 定义一个可以定位控件的函数.
        // 该函数将用于绘图和交互.
        function polygonPositionHandler(dim, finalMatrix, fabricObject) {
            const x = (fabricObject.points[this.pointIndex].x - fabricObject.pathOffset.x),
                y = (fabricObject.points[this.pointIndex].y - fabricObject.pathOffset.y);
            return fabric.util.transformPoint(
                {x: x, y: y},
                fabric.util.multiplyTransformMatrices(
                    fabricObject.canvas.viewportTransform,
                    fabricObject.calcTransformMatrix()
                )
            );
        }

        function getObjectSizeWithStroke(object) {
            const stroke = new fabric.Point(
                object.strokeUniform ? 1 / object.scaleX : 1,
                object.strokeUniform ? 1 / object.scaleY : 1
            ).multiply(object.strokeWidth);
            return new fabric.Point(object.width + stroke.x, object.height + stroke.y);
        }

        // 定义一个函数，该函数将定义控件的作用
        // 该函数将在每次鼠标移动时调用
        // 单击并正在拖动
        // 函数接收鼠标事件作为参数，当前transformat对象
        // 以及画布坐标中的当前位置
        // transform.target是对正在转换的当前对象的引用,
        function actionHandler(eventData, transform, x, y) {
            const polygon = transform.target,
                currentControl = polygon.controls[polygon.__corner],
                mouseLocalPosition = polygon.toLocalPoint(new fabric.Point(x, y), 'center', 'center'),
                polygonBaseSize = getObjectSizeWithStroke(polygon),
                size = polygon._getTransformedDimensions(0, 0),
                finalPointPosition = {
                    x: mouseLocalPosition.x * polygonBaseSize.x / size.x + polygon.pathOffset.x,
                    y: mouseLocalPosition.y * polygonBaseSize.y / size.y + polygon.pathOffset.y
                };
            polygon.points[currentControl.pointIndex] = finalPointPosition;
            return true;
        }

        // 定义一个函数，该函数可以在我们更改其
        // width/height/top/left.
        function anchorWrapper(anchorIndex, fn) {
            return function (eventData, transform, x, y) {
                const fabricObject = transform.target,
                    absolutePoint = fabric.util.transformPoint({
                        x: (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x),
                        y: (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y),
                    }, fabricObject.calcTransformMatrix()),
                    actionPerformed = fn(eventData, transform, x, y),
                    newDim = fabricObject._setPositionDimensions({}),
                    polygonBaseSize = getObjectSizeWithStroke(fabricObject),
                    newX = (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x) / polygonBaseSize.x,
                    newY = (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y) / polygonBaseSize.y;
                fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5);
                return actionPerformed;
            }
        }

        function polygonEdit() {
            // 克隆自你以来你在复制什么
            // 可能需要在不同时刻复制和粘贴.
            // 你不希望发生变化
            // 稍后再反思副本.
            const poly = canvas.getObjects()[0];
            canvas.setActiveObject(poly);
            const lastControl = poly.points.length - 1;
            poly.cornerStyle = 'circle';
            poly.cornerColor = 'rgba(0,43,255,0.9)';
            poly.cornerSize = 12
            poly.controls = poly.points.reduce(function (acc, point, index) {
                acc['p' + index] = new fabric.Control({
                    positionHandler: polygonPositionHandler,
                    actionHandler: anchorWrapper(index > 0 ? index - 1 : lastControl, actionHandler),
                    actionName: 'modifyPolygon',
                    pointIndex: index
                });
                return acc;
            }, {});
            poly.hasBorders = false;
            canvas.requestRenderAll();
        }
    }

    finish() {
        this.drawing = false
        this.rectangle.style.zIndex = '1'
        const {canvas} = this
        this.drawType('')
        if (canvas.getObjects().length > 0) {
            canvas.discardActiveObject().renderAll()
            this.setRatio()
            return true
        } else {
            this.proportion = rectangleDefault
            return false
        }
    }

    remove() {
        const {canvas, downObject} = this
        if (downObject) {
            canvas.remove(downObject.target)
        }
        if (canvas.getObjects().length === 0) {
            this.drawType('rect')
        }
    }

    async setRatio() { // 计算参数
        const data = await this.canvas.getObjects()
        if (data.length > 0) {
            const {clientHeight, clientWidth} = this.fabricDom
            const arr = Object.keys(data[0].oCoords)
            const oCoords = arr[0] === 'p0' ? data[0].oCoords : data[0].points
            this.proportion = []
            for (let field in oCoords) {
                this.proportion.push({x: oCoords[field].x / clientWidth, y: oCoords[field].y / clientHeight})
            }
        }
    }

    getRatio() {
        return this.proportion
    }

    setProportion(shape?:{x:any,y:any}[]) {
        let isErr = false
        if (shape && shape instanceof Array && shape.length > 2) {
            // 判断数据格式是否正确
            isErr = shape.reduce((per, item) => {
                const isNan = parseFloat(item.x).toString() !== "NaN" && parseFloat(item.y).toString() !== "NaN"
                if (isNan) {
                    if ((item.x < 0 && item.x > 1) || (item.y < 0 && item.y > 1)) {
                        per = false
                    }
                } else {
                    per = false
                }
                return per
            }, true)
        }
        this.proportion = isErr ? shape : rectangleDefault
        if (!isErr && shape !== undefined) {
            console.log('错误：绘制多边形数据或格式不正确')
        }
    }

    // 取消绘制
    async cancelRatio() {
        this.proportion = [...this.initialPoint]
        this.resetRect()
    }

    destroy() {
        if (this.canvas) {
            this.canvas.dispose()
            this.canvas = undefined
        }
        this.proportion = []
        if(this.emitter){
            this.emitter.removeAllListeners()
            this.emitter = null;
        }
    }
}
