class GaugeWidget extends BaseWidget {
    static OUT_OF_RANGE_COLOR = '#8e1414';

    $el;
    #redraw;
    #cstyle;

    constructor(data, renderer) {
        super(data, renderer);
        this.perc = null;
        this.value = 0;
        this.min = 0;
        this.max = 100;
        this.dec = 0;
        this.unit = '';
        this.icon = '';
        this.tout = null;

        switch (this.type) {
            case 'gauge': this.#redraw = this.#redrawGauge; break;
            case 'gauge_l': this.#redraw = this.#redrawGaugeL; break;
            case 'gauge_r': this.#redraw = this.#redrawGaugeR; break;
        }

        this.makeLayout({
            type: 'canvas',
            name: 'el',
            events: {
                resize: () => this.#redraw()
            }
        });
        this.#cstyle = window.getComputedStyle(this.$el);
        
        this.update(data);
        wait2Frame().then(() => this.#redraw());
    }

    close() {
        if (this.tout) clearTimeout(this.tout);
        this.tout = null;
    }

    update(data) {
        super.update(data);
        if ('value' in data) this.value = Number(data.value);
        if ('min' in data) this.min = Number(data.min);
        if ('max' in data) this.max = Number(data.max);
        if ('dec' in data) this.dec = Number(data.dec);
        if ('unit' in data) this.unit = data.unit;
        if ('icon' in data) this.icon = data.icon;
        this.#redraw();
    }

    #redrawGauge() {
        this.color = intToCol(this.data.color) ?? this.#cstyle.getPropertyValue('--prim');
        let cv = this.$el;
        let rw = cv.parentNode.clientWidth;
        if (!rw) return;

        const ratio = window.devicePixelRatio;
        let rh = Math.floor(rw * 0.47);
        cv.style.width = rw + 'px';
        cv.style.height = rh + 'px';
        cv.width = Math.floor(rw * ratio);
        cv.height = Math.floor(rh * ratio);

        let cx = cv.getContext("2d");
        let perc = (this.value - this.min) * 100 / (this.max - this.min);
        if (perc < 0) perc = 0;
        if (perc > 100) perc = 100;
        if (this.perc == null) this.perc = perc;
        else {
            if (Math.abs(this.perc - perc) <= 0.15) this.perc = perc;
            else this.perc += (perc - this.perc) * 0.15;
            if (this.perc != perc) setTimeout(() => this.#redraw(), 20);
        }

        cx.clearRect(0, 0, cv.width, cv.height);
        cx.lineWidth = cv.width / 8;
        cx.strokeStyle = this.#cstyle.getPropertyValue('--dark');
        cx.beginPath();
        cx.arc(cv.width / 2, cv.height * 0.97, cv.width / 2 - cx.lineWidth, Math.PI * (1 + this.perc / 100), Math.PI * 2);
        cx.stroke();

        cx.strokeStyle = this.color;
        cx.beginPath();
        cx.arc(cv.width / 2, cv.height * 0.97, cv.width / 2 - cx.lineWidth, Math.PI, Math.PI * (1 + this.perc / 100));
        cx.stroke();

        let font = this.#cstyle.fontFamily;

        cx.fillStyle = this.color;
        cx.font = '10px ' + font;
        cx.textAlign = "center";

        let text = this.unit;
        let len = Math.max(
            (this.value.toFixed(this.dec) + text).length,
            (this.min.toFixed(this.dec) + text).length,
            (this.max.toFixed(this.dec) + text).length
        );
        if (len == 1) text += '  ';
        else if (len == 2) text += ' ';

        let w = Math.max(
            cx.measureText(this.value.toFixed(this.dec) + text).width,
            cx.measureText(this.min.toFixed(this.dec) + text).width,
            cx.measureText(this.max.toFixed(this.dec) + text).width
        );

        if (this.value > this.max || this.value < this.min) cx.fillStyle = GaugeWidget.OUT_OF_RANGE_COLOR;
        else cx.fillStyle = this.#cstyle.getPropertyValue('--font2');
        cx.font = cv.width * 0.43 * 10 / w + 'px ' + font;
        cx.fillText(this.value.toFixed(this.dec) + this.unit, cv.width / 2, cv.height * 0.93);

        cx.font = '10px ' + font;
        w = Math.max(
            cx.measureText(Math.round(this.min)).width,
            cx.measureText(Math.round(this.max)).width
        );
        cx.fillStyle = this.#cstyle.getPropertyValue('--font');
        cx.font = cx.lineWidth * 0.55 * 10 / w + 'px ' + font;
        cx.fillText(this.min, cx.lineWidth, cv.height * 0.92);
        cx.fillText(this.max, cv.width - cx.lineWidth, cv.height * 0.92);
    }

    #redrawGaugeR() {
        this.color = intToCol(this.data.color) ?? this.#cstyle.getPropertyValue('--prim');
        let cv = this.$el;
        let rw = cv.parentNode.clientWidth;
        if (!rw) return;

        const ratio = window.devicePixelRatio;
        cv.style.width = rw + 'px';
        cv.style.height = cv.style.width;
        cv.width = Math.floor(rw * ratio);
        cv.height = cv.width;

        let cx = cv.getContext("2d");
        let perc = (this.value - this.min) * 100 / (this.max - this.min);
        if (perc < 0) perc = 0;
        if (perc > 100) perc = 100;
        if (this.perc == null) this.perc = perc;
        else {
            if (Math.abs(this.perc - perc) <= 0.15) this.perc = perc;
            else this.perc += (perc - this.perc) * 0.15;
            if (this.perc != perc) setTimeout(() => this.#redraw(), 20);
        }

        let joint = Math.PI * (0.5 + 2 * (this.perc / 100));

        cx.clearRect(0, 0, cv.width, cv.height);
        cx.lineWidth = cv.width / 8;
        cx.strokeStyle = this.#cstyle.getPropertyValue('--dark');
        cx.beginPath();
        cx.arc(cv.width / 2, cv.height / 2, cv.width / 2 - cx.lineWidth, joint, Math.PI * 2.5);
        cx.stroke();
        
        cx.strokeStyle = this.color;
        cx.beginPath();
        cx.arc(cv.width / 2, cv.height / 2, cv.width / 2 - cx.lineWidth, Math.PI / 2, joint);
        cx.stroke();

        let font = this.#cstyle.fontFamily;

        cx.fillStyle = this.color;
        cx.font = '10px ' + font;
        cx.textAlign = "center";
        cx.textBaseline = "middle";

        let text = this.unit;
        let len = Math.max(
            (this.value.toFixed(this.dec) + text).length,
            (this.min.toFixed(this.dec) + text).length,
            (this.max.toFixed(this.dec) + text).length
        );
        if (len == 1) text += '  ';
        else if (len == 2) text += ' ';

        let w = Math.max(
            cx.measureText(this.value.toFixed(this.dec) + text).width,
            cx.measureText(this.min.toFixed(this.dec) + text).width,
            cx.measureText(this.max.toFixed(this.dec) + text).width
        );

        if (this.value > this.max || this.value < this.min) cx.fillStyle = GaugeWidget.OUT_OF_RANGE_COLOR;
        else cx.fillStyle = this.#cstyle.getPropertyValue('--font2');
        cx.font = cv.width * 0.5 * 10 / w + 'px ' + font;
        cx.fillText(this.value.toFixed(this.dec) + this.unit, cv.width / 2, cv.height * 0.52);
    }

    #redrawGaugeL() {
        this.color = intToCol(this.data.color) ?? this.#cstyle.getPropertyValue('--prim');
        let cv = this.$el;
        let rw = cv.parentNode.clientWidth;
        if (!rw) return;

        const ratio = window.devicePixelRatio;
        let height = 30;
        let r = ratio;
        let sw = 2 * r;
        let off = 5 * r;

        cv.style.width = rw + 'px';
        cv.style.height = height + 'px';
        cv.width = Math.floor(rw * r);
        cv.height = Math.floor(height * r);

        const cx = cv.getContext("2d");
        let perc = (this.value - this.min) * 100 / (this.max - this.min);
        if (perc < 0) perc = 0;
        if (perc > 100) perc = 100;
        if (this.perc == null) this.perc = perc;
        else {
            if (Math.abs(this.perc - perc) <= 0.15) this.perc = perc;
            else this.perc += (perc - this.perc) * 0.15;
            if (this.perc != perc) setTimeout(() => this.#redraw(), 20);
        }

        let wid = cv.width - sw - off * 2;

        cx.clearRect(0, 0, cv.width, cv.height);
        cx.fillStyle = this.#cstyle.getPropertyValue('--back');
        cx.beginPath();
        cx.roundRect(off + sw / 2, sw / 2, wid, cv.height - sw, 5 * r);
        cx.fill();

        cx.fillStyle = this.color;
        cx.beginPath();
        cx.roundRect(off + sw / 2, sw / 2, wid * this.perc / 100, cv.height - sw, 5 * r);
        cx.fill();

        if (this.value > this.max || this.value < this.min) cx.fillStyle = GaugeWidget.OUT_OF_RANGE_COLOR;
        else cx.fillStyle = this.#cstyle.getPropertyValue('--font');

        let font = this.#cstyle.fontFamily;

        cx.font = (19 * r) + 'px ' + font;
        cx.textAlign = "center";
        cx.textBaseline = "middle";

        let txt = this.value.toFixed(this.dec) + this.unit;
        cx.fillText(txt, cv.width / 2, cv.height * 0.52);

        if (this.icon) {
            let tw = cx.measureText(txt).width;
            cx.font = (20 * r) + 'px FA5';
            cx.textAlign = "right";
            cx.fillText(getIcon(this.icon), cv.width / 2 - tw / 2 - off, cv.height * 0.52);
        }

        cx.fillStyle = this.#cstyle.getPropertyValue('--font');
        cx.font = (12 * r) + 'px ' + font;
        cx.textAlign = "left";
        cx.fillText(this.min.toFixed(this.dec), off + sw / 2 + off, cv.height * 0.52);

        cx.textAlign = "right";
        cx.fillText(this.max.toFixed(this.dec), cv.width - (off + sw / 2 + off), cv.height * 0.52);
    }
}

Renderer.register('gauge', GaugeWidget);
Renderer.register('gauge_l', GaugeWidget);
Renderer.register('gauge_r', GaugeWidget);
