import Konva from "konva";
import * as OT from "@mescius/js-collaboration-ot-client";
import {Presence} from "@mescius/js-collaboration-presence-client";
import {bindPresence, IPresence} from "./presence";
import {Vector2d} from "konva/lib/types";
import {uuid} from "./utils";

export function bindKonvaAndPresence (stage: Konva.Stage, doc: OT.SharedDoc, presence: Presence<IPresence>, user: any, userHostId: string, hostId: string) {
    bind(doc, stage, user.color);
    bindPresence(presence, user, userHostId, hostId);
}

export interface IOp {
    type: 'addLine' | 'addPoint',
    data: any;
}
function bind(doc: OT.SharedDoc, stage: Konva.Stage, color: string) {
    const layer = new Konva.Layer();
    stage.add(layer);

    let line: Konva.Line | null;
    let lastPointerPosition: Vector2d;

    doc.on('op', (op, source) => {
        if (source === doc.connection.id) return;
        applyOps(layer, op as IOp[]);
    });

    doc.subscribe().then(() => {
        if (doc.data) {
            fromJSON(stage, doc.data);
        }
    });

    stage.on('mousedown touchstart', function (e) {
        lastPointerPosition = stage.getPointerPosition() as Vector2d;

        line = new Konva.Line({
            id: uuid(),
            stroke: color,
            strokeWidth: 5,
            points: [lastPointerPosition.x, lastPointerPosition.y]
        });
        layer.add(line);

        doc.submitOp([{ type: 'addLine', data: JSON.parse(line.toJSON()) }], { source: doc.connection.id });
    });

    stage.on('mouseup touchend', function () {
        line = null;
    });

    stage.on('mousemove touchmove', function () {
        if (!line) return;
        const { x, y } = stage.getPointerPosition() as Vector2d;

        line.points(line.points().concat([x, y]));

        doc.submitOp([{ type: 'addPoint', data: { lineId: line.attrs.id, x, y } }], { source: doc.connection.id });

        lastPointerPosition = { x, y };
        layer.batchDraw();
    });
}

function applyOp(layer: Konva.Layer, op: IOp) {
    if (op.type === 'addLine') {
        layer.add(new Konva.Line(op.data));
    } else if (op.type === 'addPoint') {
        const { lineId, x, y } = op.data;
        const line = layer.findOne((c: any) => c.attrs.id === lineId) as Konva.Line;
        line.points(line.points().concat([x, y]));
    }
    layer.batchDraw();
}

function applyOps(layer: Konva.Layer, ops: IOp[]) {
    ops.forEach(op => applyOp(layer, op));
}

function fromJSON(stage: Konva.Stage, data: any) {
    const layer = stage.getLayers()[0];

    data.forEach((op) => applyOp(layer, op));
}