class PlotWidget extends BaseWidget {
    $el;
    #cv;

    constructor(data, renderer) {
        super(data, renderer);

        this.makeLayout({
            type: 'div',
            class: '',
            children: [
                {
                    type: 'canvas',
                    name: 'el',
                }
            ]
        });

        this.#cv = new Plot(data.id, this.$el, data.height ?? 150, data.text, data.type, true);

        this.$el.parentNode.addEventListener('resize', () => {
            this.#cv.resize();
        });

        wait2Frame().then(() => {
            this.#cv.redraw();
        });

        this.update(data.value);
    }

    update(data) {
        super.update(data);
        
        if ('value' in data) {
            this.#cv.update(data.value);
            this.#cv.redraw();
        }
    }

    close() {
        this.#cv.stop();
    }
}

Renderer.register('plot', PlotWidget);


class Plot {
    constructor(id, cv, height, labels, type, dark) {
        this.id = id;
        this.cv = cv;
        this.height = height;
        this.labels = labels ? labels.split(';') : null;
        this.type = type;
        this.dark = dark;
        this.data = [];
        this.resize();
        cv.onclick = (event) => this._click(event);

        if ("ontouchstart" in document.documentElement) {
            document.addEventListener("touchmove", this._onTouchMove, { passive: false });
        } else {
            document.addEventListener("mousemove", this._onMouseMove);
        }
    }

    stop() {
        if ("ontouchstart" in document.documentElement) {
            document.removeEventListener("touchmove", this._onTouchMove);
        } else {
            document.removeEventListener("mousemove", this._onMouseMove);
        }
    }

    clear() {
        this.data = [];
    }

    resize() {
        let cv = this.cv;
        let rw = cv.parentNode.clientWidth;
        if (!rw) return;
        let r = window.devicePixelRatio;
        cv.style.width = rw + 'px';
        cv.style.height = this.height + 'px';
        cv.width = Math.floor(rw * r);
        cv.height = Math.floor(this.height * r);
        this.redraw(this.data);
    }

    update(data) {
        this.data = this.data.concat(data);
    }

    redraw() {
        let cv = this.cv;
        let cx = cv.getContext("2d");
        let r = window.devicePixelRatio;

        cx.fillRect(0, 0, cv.width, cv.height);
    }

    // PRIVATE
    _click(e) {
        let rect = this.cv.getBoundingClientRect();
        let x = Math.round(e.clientX - rect.left);
        if (x < 0) x = 0;
        let y = Math.round(e.clientY - rect.top);
        if (y < 0) y = 0;
    }

    _onTouchMove = (event) => {
        event.preventDefault();
        for (let t of event.changedTouches) {
            if (t.target === this.cv) this._move(t);
        }
    }

    _onMouseMove = (event) => {
        if (event.target == this.cv) this._move(event);
    }

    _move(rect) {
        let x = rect.pageX;
        let y = rect.pageY;
        if (this.cv.offsetParent.tagName.toUpperCase() === "BODY") {
            x -= this.cv.offsetLeft;
            y -= this.cv.offsetTop;
        } else {
            x -= this.cv.offsetParent.offsetLeft;
            y -= this.cv.offsetParent.offsetTop;
        }
        x = this._constrain(x, 0, this.cv.width / window.devicePixelRatio);
        y = this._constrain(y, 0, this.cv.height / window.devicePixelRatio);
        this.redraw();
    }

    _constrain(x, min, max) {
        if (x < min) return min;
        if (x > max) return max;
        return x;
    }
};