import {
    CanvasInputPos,
    InputDelegateConstructorViewArgs,
    PeekCanvasInputDelegate
} from "./PeekCanvasInputDelegate.web";
import {EditorToolType} from "../canvas/PeekCanvasEditorToolType.web";
import {DispPoly} from "../canvas-shapes/DispPoly";
import {DispBaseT, DispHandleTypeE, PointI} from "../canvas-shapes/DispBase";
import {DispPolygon} from "../canvas-shapes/DispPolygon";
import {DispPolyline, DispPolylineEndTypeE} from "../canvas-shapes/DispPolyline";
import {DrawModeE} from "../canvas-render/PeekDispRenderDelegateABC.web";
import {PeekCanvasBounds} from "../canvas/PeekCanvasBounds";
import {InputDelegateConstructorEditArgs} from "./PeekCanvasInputDelegateUtil.web";

/**
 * This input delegate handles :
 * Zooming (touch and mouse)
 * Panning (touch and mouse)
 * Selecting at a point (touch and mouse)
 *
 */
export class PeekCanvasInputEditMakeDispPolyDelegate extends PeekCanvasInputDelegate {


    // Stores the rectangle being created
    protected _creating = null;

    // Used to detect dragging and its the mouse position we use
    protected _startMousePos: CanvasInputPos | null = null;
    protected _startNodeDisp = null;
    protected _endNodeDisp = null;

    protected _nodes = []; //canvasInput._scope.pageData.modelRenderables;

    constructor(viewArgs: InputDelegateConstructorViewArgs,
                editArgs: InputDelegateConstructorEditArgs,
                tool: EditorToolType) {
        super(viewArgs, editArgs, tool);

        this.viewArgs.model.selection.clearSelection();
        this._reset();
    }

    _reset() {
        this._creating = null;
        this._startNodeDisp = null;
        this._endNodeDisp = null;

        // See mousedown and mousemove events for explanation
        this._startMousePos = null;
        this._lastMousePos = new CanvasInputPos();
        ;
    }


    keyUp(event) {
        if (!this._creating)
            return;

        // Cancel creating object
        if (event.keyCode == 46 // delete
            || event.keyCode == 27) { // escape
            this._reset();
            return;
        }

        if (event.keyCode == 8) { // Backspace
            // We want to keep at least two points at all times
            if (DispPoly.pointCount(this._creating) < 3)
                return;
            // Remove last point
            DispPoly.popPoint(this._creating);
            this.viewArgs.config.invalidate();
            return;
        }

        if (event.keyCode == 13) { // Enter
            this._finaliseCreate();
            return;
        }
    }


    private _nodeDispClickedOn(point: PointI): DispBaseT | null {

        for (let i = this._nodes.length - 1; 0 <= i; i--) {
            let disp = this._nodes[i];
            if (disp.bounds != null && disp.bounds.contains(point.x, point.y)) {
                return disp;
            }
        }

        return null;
    }


    // ---------------
    // Map mouse events
    mouseDown(event: MouseEvent, inputPos: CanvasInputPos) {
        this.inputStart(inputPos);
    }


    mouseMove(event: MouseEvent, inputPos: CanvasInputPos) {
        this.inputMove(inputPos);
    }

    mouseUp(event: MouseEvent, inputPos: CanvasInputPos) {
        if (event.button == 2) {
            this._finaliseCreate();
            return;
        }
        this.inputEnd(inputPos, event.shiftKey);
    }

    mouseDoubleClick(event: MouseEvent, inputPos: CanvasInputPos) {
        // The double click will cause two "MouseUp" events
        DispPoly.popPoint(this._creating);
        DispPoly.popPoint(this._creating);
        this._finaliseCreate();
    }


    // ---------------
    // Map touch events
    touchStart(event: TouchEvent, inputPos: CanvasInputPos) {
        if (event.touches.length == 2) {
            this._finaliseCreate();
            return;
        }

        this.inputStart(inputPos);
    };

    touchMove(event: TouchEvent, inputPos: CanvasInputPos) {
        this.inputMove(inputPos);
    };

    touchEnd(event: TouchEvent, inputPos: CanvasInputPos) {
        this.inputEnd(inputPos);
    };


    // ---------------
    // Start logic
    private inputStart(inputPos: CanvasInputPos) {

        /*
                if (this._startNodeDisp) {
                    this._startMousePos = inputPos;
                    return;
                }


                this._startNodeDisp = this._nodeDispClickedOn(inputPos);

                if (!this._startNodeDisp) {
                    this.editArgs.balloonMsg.showWarning("A conductor must start on a node");
                    this._reset();
                    // this.canvasInput._scope.pageMethods.cableCreateCallback();
                    return;
                }
        */
        this._lastMousePos = inputPos;
        if (!this._creating) {
            this._startMousePos = inputPos;
            this.createDisp(inputPos);
        }
    }


    private inputMove(inputPos: CanvasInputPos) {
        if (this._startMousePos == null)
            return;

        const handleBounds = new PeekCanvasBounds(inputPos.x, inputPos.y, 0, 0);
        const delta = this._setLastMousePos(inputPos);
        DispPoly.deltaMoveHandle(
            {
                disp: this._creating,
                center: handleBounds.center(),
                box: handleBounds,
                handleType:DispHandleTypeE.movePoint,
                handleIndex: DispPoly.pointCount(this._creating) - 1,
            },
            delta.dx, delta.dy
        );
        this.viewArgs.config.invalidate();
    }

    private inputEnd(inputPos: CanvasInputPos, shiftKey: boolean = false) {
        if (!this._startMousePos)
            return;

        if (!this._hasPassedDragThreshold(this._startMousePos, inputPos))
            return;

        if (this.NAME == EditorToolType.EDIT_MAKE_LINE_WITH_ARROW
            && DispPoly.pointCount(this._creating) == 2) {
            this._finaliseCreate();

        } else {
            let point = this._coord(this._lastMousePos, shiftKey);
            DispPoly.addPoint(this._creating, point);

        }

        this.viewArgs.config.invalidate();
    }

    delegateWillBeTornDown() {
        //this._finaliseCreate();
    }

    draw(ctx, zoom: number, pan: PointI, drawMode: DrawModeE) {
    }


    protected createDisp(inputPos: CanvasInputPos) {

        // Create the Disp
        if (this.NAME == EditorToolType.EDIT_MAKE_POLYGON)
            this._creating = DispPolygon.create(this.viewArgs.config.coordSet);
        else
            this._creating = DispPolyline.create(this.viewArgs.config.coordSet);

        if (this.NAME == EditorToolType.EDIT_MAKE_LINE_WITH_ARROW)
            DispPolyline.setEndEndType(this._creating, DispPolylineEndTypeE.Arrow);

        DispPoly.addPoint(this._creating, this._startMousePos);
        this._setLastMousePos(this._startMousePos);


        DispPoly.addPoint(this._creating, inputPos);

        // Link the Disp
        this.editArgs.lookupService._linkDispLookups(this._creating);

        // Add the shape to the branch
        this._creating = this.editArgs.branchContext.branchTuple
            .addOrUpdateDisp(this._creating, true);

        // TODO, Snap the coordinates if required
        // if (this.viewArgs.config.editor.snapToGrid)
        //     DispText.snap(this._creating, this.viewArgs.config.editor.snapSize);

        // Let the canvas editor know something has happened.
        // this.editArgs.dispPropsUpdated();

        this.viewArgs.model.recompileModel();

        this.viewArgs.model.selection.replaceSelection(this._creating);

        this._addBranchAnchor(inputPos.x, inputPos.y);
    }

    private _finaliseCreate() {
        if (this._creating == null)
            return;

        let poly = this._creating;
        let startNodeDisp = this._startNodeDisp;
        let endNodeDisp = null;

        this._reset();

        if (poly) {
            let lastPointCoord = DispPoly.lastPoint(poly);
            endNodeDisp = this._nodeDispClickedOn(lastPointCoord);
        }

        if (!endNodeDisp) {
            // this.editArgs.balloonMsg.showWarning("A conductor must end on a node");
            poly = null;
        }

        // this.canvasInput._scope.pageMethods.cableCreateCallback(poly, startNodeDisp, endNodeDisp);

        this.editArgs.branchContext.branchTuple.touchUpdateDate(true);
        this.editArgs.branchContext.branchTuple.touchUndo();
        this.viewArgs.config.invalidate();
        this.editArgs.setEditorSelectTool();
    }

    private _coord(mouse: CanvasInputPos, shiftKey: boolean = false): PointI {
        let point = {x: mouse.x, y: mouse.y};

        // When the shift key is pressed, we will align to x or y axis
        if (this._creating != null && shiftKey) {
            let lastPoint = DispPoly.lastPoint(this._creating);
            let dx = Math.abs(point.x - lastPoint.x);
            let dy = Math.abs(point.y - lastPoint.y);

            if (dx > dy)
                point.y = lastPoint.y;
            else
                point.x = lastPoint.x;
        }

        // return
        return point;
    }

}