// loadDrawing = function(identifier){
//         const request = new Request('/copy_and_load/'+identifier, {
//             method: 'GET',
//         });

//         // fetch(request)
//         //     .then(response => response.json())
//         //     .then(data => {
//         //         const metadata_req = new Request(`/annotations/${data.file}`, {
//         //             method: 'GET',
//         //         });
//         //         fetch(metadata_req)
//         //             .then(response => response.ok ? response.json() : null)
//         //             .then(metadata => {
//         //                 if (metadata !== null) {
//         //                     metadata.annotations = metadata.annotations.map((a) => new Annotation(a.tag, a.t_in, a.t_out))
//         //                 }
//         //                 this.loadStrokes(data, metadata)
//         //             })
//         //             .catch(e => console.log(e));
//         //         // do something with the data sent in the request
//         //     });
// }

class Canvas {
    constructor(wrapperEl, preload_id) {
        this.allowDrawing = false;
        this.socket = null; // don't initialise right away
        this.viewbox = { "x": 0, "y": 0, "width": null, "height": null };
        this.url = window.location.origin.replace('http', 'ws') + '/ws?' + window.location.search.substring(1);

        // build the interface
        this.wrapperEl = wrapperEl;
        this.wrapperEl.classList.add('closed');

        this.svgEl = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        this.wrapperEl.appendChild(this.svgEl);

        this.toolboxEl = document.createElement('div');
        this.toolboxEl.classList.add('toolbox')
        this.wrapperEl.appendChild(this.toolboxEl);

        this.filenameEl = document.createElement('div');
        this.filenameEl.classList.add('filename')
        this.wrapperEl.appendChild(this.filenameEl);


        this.fullscreenEl = document.createElement('div');
        this.fullscreenEl.classList.add('button-fullscreen');
        this.fullscreenEl.innerText = "Fullscreen";
        this.wrapperEl.appendChild(this.fullscreenEl);
        this.fullscreenEl.addEventListener('click', (e) => {
            document.body.requestFullscreen();
        });
        document.body.addEventListener('fullscreenchange', (e) => {
            if (document.fullscreenElement) {
                document.body.classList.add('fullscreen');
            } else {
                document.body.classList.remove('fullscreen');
            }
        })


        this.colors = ["black", "#cc1414", "blue", "green"];

        this.resize();

        window.addEventListener('resize', (ev) => this.requestResize());


        this.paths = [];
        this.viewboxes = [];
        this.events = []; // all paths & viewboxes events
        this.isDrawing = false;
        this.hasMouseDown = false;
        this.currentStrokeEl = null;

        this.startTime = null;

        this.isMoving = false;
        document.body.addEventListener('pointermove', (ev) => {
            ev.stopPropagation();
            ev.preventDefault();
            if (ev.pointerType == "touch" || ev.buttons & 2) { // 4: middle mouse button
                this.moveCanvas(ev);
            } else { // pointerType == pen or mouse
                this.draw(ev);
            }
        });
        document.body.addEventListener('pointerup', (ev) => {
            ev.stopPropagation();
            ev.preventDefault();

            if (ev.pointerType == "touch" || ev.buttons & 2 || this.isMoving) { // buttons is 0 on pointerup
                this.endMoveCanvas(ev);
                this.isMoving = false;
            } else { // pointerType == pen or mouse
                location.hash = '#' + this.filename; // only update when drawn.
                this.penup(ev);
            }
        });
        this.svgEl.addEventListener('contextmenu', function (e) {
            // do something here... 
            e.preventDefault();
        }, false);
        this.svgEl.addEventListener('pointerdown', (ev) => {
            ev.stopPropagation();
            ev.preventDefault();
            if (ev.pointerType == "touch" || ev.buttons & 2) { // 4: middle mouse button, 2; right mouse button
                this.isMoving = true;
                this.startMoveCanvas(ev);
            } else if (ev.buttons & 1) { // pointerType == pen or mouse
                this.startStroke(ev);
            }
        });

        this.createToolbox();

        this.setColor(this.colors[0]);


        this.socket = new WebSocket(this.url); // TODO: reconnectingwebsocket
        this.socket.addEventListener('open', (e) => {
            this.sendDimensions();

            if (preload_id) {
                // signal if we want to continue from an existing drawing
                this.socket.send(JSON.stringify({
                    'event': 'preload',
                    'file': preload_id,
                }));
            }
        });
        this.socket.addEventListener('message', (e) => {
            let msg = JSON.parse(e.data);
            console.log('receive', msg);
            if (msg.hasOwnProperty('filename')) {
                console.log('filename', msg.filename);
                this.setFilename(msg.filename);
                this.openTheFloor()
            }
            if (msg.hasOwnProperty('preloaded_svg')) {
                console.log('preloaded', msg);
                if (msg.dimensions[0] != this.viewbox.width || msg.dimensions[1] != this.viewbox.height) {
                    alert(`Loading file with different dimensions. This can lead to odd results. Original: ${msg.dimensions[0]}x${msg.dimensions[1]} Now: ${this.viewbox.width}x${this.viewbox.height}`)
                }
                this.setPreloaded(msg.preloaded_svg);

                // this.setFilename(msg.filename);
                // this.openTheFloor()
            }
        });
        this.socket.addEventListener('close', (e) => {
            this.closeTheFloor();
            alert('Internet connection seems to have problems. Please reload the page to reconnect.');
        });
    }

    setPreloaded(json_url) {
        this.preloaded_resource = json_url
        const request = new Request(this.preloaded_resource + '.svg', {
            method: 'GET',
        });

        fetch(request)
            .then(response => response.text())
            .then(body => {
                const parser = new DOMParser()
                const dom = parser.parseFromString(body, "image/svg+xml");
                console.log(dom, dom.getRootNode().querySelectorAll('g'))
                const group = dom.getRootNode().querySelectorAll('g')[0]
                this.svgEl.prepend(group);
            })
    }

    startMoveCanvas(ev) {
        this.moveCanvasPrevPoint = { "x": ev.x, "y": ev.y };
        this.currentMoves = [];
    }

    endMoveCanvas(ev) {
        this.moveCanvasPrevPoint = null;

        // sync viewpoints
        const d = {
            'event': 'viewbox',
            'viewboxes': this.currentMoves
        };
        console.log('send', d);
        this.socket.send(JSON.stringify(d));
    }

    moveCanvas(ev) {
        if (this.moveCanvasPrevPoint === null) {
            return
        }
        const diff = {
            "x": ev.x - this.moveCanvasPrevPoint.x,
            "y": ev.y - this.moveCanvasPrevPoint.y,
        }
        this.viewbox.x -= diff.x;
        this.viewbox.y -= diff.y;
        this.moveCanvasPrevPoint = { "x": ev.x, "y": ev.y };
        this.currentMoves.push(Object.assign({ 't': window.performance.now() - this.startTime }, this.viewbox));

        this.applyViewBox()
    }


    openTheFloor() {
        this.wrapperEl.classList.remove('closed');
    }

    closeTheFloor() {
        this.wrapperEl.classList.add('closed');
    }

    setFilename(filename) {
        this.filename = filename;
        this.filenameEl.innerText = filename;
    }

    createToolbox() {
        const colorsEl = document.createElement('ul');
        colorsEl.classList.add('colors');
        for (let color of this.colors) {
            const colorEl = document.createElement('li');
            colorEl.style.background = color;
            colorEl.addEventListener('click', (e) => {
                console.log('set color', color)
                this.setColor(color);
            })

            colorsEl.appendChild(colorEl);
        }
        this.toolboxEl.appendChild(colorsEl);
    }

    setColor(color) {
        this.currentColor = color;
        const colorEls = this.toolboxEl.querySelectorAll('.colors li');
        for (let colorEl of colorEls) {
            if (colorEl.style.backgroundColor == color) {
                colorEl.classList.add('selected');
            }
            else {
                colorEl.classList.remove('selected');
            }
        }
    }

    resize() {
        this.viewbox.width = window.innerWidth;
        this.viewbox.height = window.innerHeight;

        this.applyViewBox();
        this.sendDimensions();
    }

    sendDimensions() {
        const d = {
            'event': 'dimensions',
            'width': this.viewbox.width,
            'height': this.viewbox.height
        };
        if (this.socket === null) {
            // ignore ...
        } else if (this.socket.readyState) {
            this.socket.send(JSON.stringify(d));
        } else {
            this.socket.addEventListener('open', (ev) => {
                this.socket.send(JSON.stringify(d));
            })
        }
    }

    applyViewBox() {
        const viewBox = `${this.viewbox.x} ${this.viewbox.y} ${this.viewbox.width} ${this.viewbox.height}`;
        this.svgEl.setAttribute('viewBox', viewBox);
        this.svgEl.setAttribute('width', this.viewbox.width + 'mm');
        this.svgEl.setAttribute('height', this.viewbox.height + 'mm');

        // todo save drag event;
        // const newViewbox = Object.assign({}, this.viewbox, {'t': window.performance.now() - this.startTime});
        // const lastViewbox = this.viewboxes[this.viewboxes.length - 1];
        // if(newViewbox.x == lastViewbox.x && newViewbox.y == lastViewbox.y && newViewbox.width == lastViewbox.width && newViewbox.height == lastViewbox.height){
        //     // do nothing, avoiding duplicate
        // } else {
        //     this.viewboxes.push(newViewbox);
        //     this.events.push(newViewbox);
        // }
    }

    requestResize() {
        this.resize();
        // alert('Resize not implemented yet. Please reloade the page');
    }


    getCoordinates(e) {
        // convert event coordinates into relative positions on x & y axis
        let box = this.svgEl.getBoundingClientRect();
        let x = (e.x - box['left'] + this.viewbox.x);
        let y = (e.y - box['top'] + this.viewbox.y);
        return { 'x': x, 'y': y };
    }

    // isInsideBounds(pos) {
    //     let box = this.svgEl.getBoundingClientRect();
    //     return !(pos['x'] < 0 || pos['y'] < 0 || pos['x'] > box['width'] || pos['y'] > box['height']);
    // }


    draw(e) {
        let pos = this.getCoordinates(e);

        if (!this.isDrawing && this.hasMouseDown /*&& this.isInsideBounds(pos)*/) {
            this.isDrawing = true;
        }

        if (this.isDrawing) {
            this.paths[this.paths.length - 1].points.push([pos['x'], pos['y'], 0, window.performance.now() - this.startTime]);
            let d = this.strokes2D(this.paths[this.paths.length - 1].points);
            this.currentStrokeEl.setAttribute('d', d);
        }
    }

    startStroke(e) {
        this.hasMouseDown = true;

        const strokeEl = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        strokeEl.style.stroke = this.currentColor;
        this.svgEl.appendChild(strokeEl);
        this.currentStrokeEl = strokeEl;
        let path = {
            'el': strokeEl,
            'color': this.currentColor,
            'points': []
        };
        this.paths.push(path);
        this.events.push(path); // same ref.

        if (this.startTime === null) {
            // initiate timer on first stroke
            this.startTime = window.performance.now();
        }
    }

    endStroke(pos) {
        if (!this.isDrawing) {
            return;
        }

        this.isDrawing = false;
        //document.body.removeEventListener('mousemove', draw);


        if (this.paths[this.paths.length - 1].points.length > 0) {
            // mark point as last of stroke
            this.paths[this.paths.length - 1].points[this.paths[this.paths.length - 1].points.length - 1][2] = 1;
        }
        const stroke = this.paths[this.paths.length - 1];
        const d = {
            'event': 'stroke',
            'color': stroke.color,
            'points': stroke.points
        };
        console.log('send', d);
        this.socket.send(JSON.stringify(d));
    }

    penup(e) {
        if (!this.hasMouseDown) {
            return;
        }
        this.hasMouseDown = false;

        let pos = this.getCoordinates(e);
        this.endStroke(pos);
    }

    strokes2D(strokes) {
        // strokes to a d attribute for a path
        let d = "";
        let last_stroke = undefined;
        let cmd = "";
        for (let stroke of strokes) {
            if (!last_stroke) {
                d += `M${stroke[0]},${stroke[1]} `;
                cmd = 'M';
            } else {
                if (last_stroke[2] == 1) {
                    d += " m";
                    cmd = 'm';
                } else if (cmd != 'l') {
                    d += ' l ';
                    cmd = 'l';
                }
                let rel_stroke = [stroke[0] - last_stroke[0], stroke[1] - last_stroke[1]];
                d += `${rel_stroke[0]},${rel_stroke[1]} `;
            }
            last_stroke = stroke;

        }
        return d;
    }

}