import Vector2 from "../../../../../src/core/geom/Vector2.js";
import { canvas2d_plot_data_line } from "../../../../../src/engine/graphics/canvas/canvas2d_plot_data_line.js";
import { MouseEvents } from "../../../../../src/engine/input/devices/events/MouseEvents.js";
import { CanvasView } from "../../../../../src/view/elements/CanvasView.js";
import EmptyView from "../../../../../src/view/elements/EmptyView.js";
import { TypeEditor } from "../../TypeEditor.js";

export class ParameterLookupTableEditor extends TypeEditor {
    get schema() {
        return {
            properties: {
                on: {
                    transient: true
                }
            }
        };
    }

    build(parent, field, registry) {

        /**
         * @type {ParameterLookupTable}
         */
        const lut = field.adapter.read(parent, field.name);
        const trackName = field.adapter.read(parent, "name");

        // if it is a track scale
        if (trackName === "scale") {
            const canvasView = new CanvasView();
            canvasView.size.set(200, 100);
            const ctx = canvasView.context2d;

            const data = [];

            const sample = [];
            for (let i = 0; i < canvasView.size.x; i++) {
                const f = i / (canvasView.size.x - 1);

                lut.sample(f, sample);

                data[i] = sample[0];
            }

            canvas2d_plot_data_line({
                ctx,
                data,
                width: canvasView.size.x,
                height: canvasView.size.y,
                margin: new Vector2(4, 4),
                range_x: [lut.positions[0], lut.positions[lut.positions.length - 1]],
            });

            return canvasView;
        } else if (trackName === "color") {
            // if it is color track
            const resultView = new EmptyView();
            const canvasView = new CanvasView();
            canvasView.css({ borderColor: 'white', borderStyle: 'solid', borderWidth: '1px', });
            canvasView.size.set(200, 70);

            const ctx = canvasView.context2d;

            const colorView = new EmptyView({
                tag: "input",
                attr: { type: "color" },
                css: { padding: "0px", border: "0px" }
            });

            resultView.addChild(canvasView);
            resultView.addChild(colorView);

            //canvas internal configuration, such as background
            let config = {
                tileSize: new Vector2(10, 10),
                tileColor1: "rgb(255, 255, 255)",
                tileColor2: "rgb(119,119,119)",
                gradientBoxYRange: new Vector2(30, canvasView.size.y),
            };

            this.DrawBackground(canvasView, ctx, config);
            this.DrawGradient(canvasView, lut, ctx, config);
            let stops = this.DrawStops(canvasView, lut, ctx, colorView, config, 0);

            canvasView.el.addEventListener(MouseEvents.Click, (e) => {
                const rect = canvasView.el.getBoundingClientRect();
                let x = e.clientX - rect.left,
                    y = e.clientY - rect.top;
                for (let i = 0; i < stops.length; i++) {
                    if (stops[i].x <= x && stops[i].x + stops[i].width >= x &&
                        stops[i].y <= y && stops[i].y + stops[i].height >= y) {
                        let s = [];
                        lut.sample(lut.positions[i], s);

                        this.convertRGB(s);

                        colorView.el.value = this.RGBToHex(s[0], s[1], s[2]);
                        canvasView.clear();

                        this.DrawBackground(canvasView, ctx, config);
                        this.DrawGradient(canvasView, lut, ctx, config);
                        stops = this.DrawStops(canvasView, lut, ctx, colorView, config, i);

                        break;
                    }
                }
            });

            return resultView;
        }
    }

    /**
     * converts rgb to hex string
     * @param {Float} r
     * @param {Float} g
     * @param {Float} b
     */
    RGBToHex(r, g, b) {
        r = r.toString(16);
        g = g.toString(16);
        b = b.toString(16);

        if (r.length == 1)
            r = "0" + r;
        if (g.length == 1)
            g = "0" + g;
        if (b.length == 1)
            b = "0" + b;

        return "#" + r + g + b;
    }

    /**
     * converts lut color sample to RGB
     * @param {number[]} sample
     */
    convertRGB(sample) {
        sample[0] = Math.floor(sample[0] * 255);
        sample[1] = Math.floor(sample[1] * 255);
        sample[2] = Math.floor(sample[2] * 255);
    }

    /**
     * draws tiled background
     * @param {CanvasView} canvasView
     * @param {CanvasRenderingContext2D} ctx
     * @param {{}} config
     */
    DrawBackground(canvasView, ctx, config) {
        for (let i = 0; i < canvasView.size.x; i += config.tileSize.x) {
            for (let j = config.gradientBoxYRange.x; j < config.gradientBoxYRange.y; j += config.tileSize.y) {
                ctx.fillStyle = (((i / config.tileSize.x) + (j / config.tileSize.y)) % 2 === 0 ? config.tileColor1 : config.tileColor2);
                ctx.fillRect(i, j, config.tileSize.x, config.tileSize.y);
            }
        }
    }

    /**
     * draws gradient
     * @param {CanvasView} canvasView
     * @param {ParameterLookupTable} lut
     * @param {CanvasRenderingContext2D} ctx
     * @param {{}} config
     */
    DrawGradient(canvasView, lut, ctx, config) {
        const sample = [];
        for (let i = 0; i < canvasView.size.x; i++) {
            const f = i / (canvasView.size.x - 1);
            lut.sample(f, sample);

            this.convertRGB(sample);

            ctx.fillStyle = `rgba(${sample[0]},${sample[1]},${sample[2]}, ${sample[3]})`;
            ctx.fillRect(i, config.gradientBoxYRange.x, 1, config.gradientBoxYRange.y - config.gradientBoxYRange.x);
        }
    }

    /**
     * draws gradient stops
     * @param {CanvasView} canvasView
     * @param {ParameterLookupTable} lut
     * @param {CanvasRenderingContext2D} ctx
     * @param {EmptyView} colorView
     * @param {{}} config
     * @param {Integer} selectedInd
     * @returns {*[]}
     */
    DrawStops(canvasView, lut, ctx, colorView, config, selectedInd) {
        const sample = [];

        let stops = [];
        for (let i = 0; i < lut.positions.length; i++) {
            const posX = lut.positions[i] * canvasView.size.x;
            if (i === selectedInd) {
                lut.sample(lut.positions[i], sample);

                this.convertRGB(sample);

                colorView.el.value = this.RGBToHex(sample[0], sample[1], sample[2]);
            }
            stops.push(this.DrawStop(ctx, config, posX, i === selectedInd));
        }
        return stops;
    }

    /**
     * draws gradient stop
     * @param {CanvasRenderingContext2D} ctx
     * @param {Object} config
     * @param {Integer} posX
     * @param {Boolean} isSelected
     */
    DrawStop(ctx, config, posX, isSelected) {
        ctx.beginPath();
        ctx.moveTo(posX, config.gradientBoxYRange.x + 7);
        ctx.lineTo(posX - 5, config.gradientBoxYRange.x);
        ctx.lineTo(posX - 5, config.gradientBoxYRange.x - 12);
        ctx.lineTo(posX + 5, config.gradientBoxYRange.x - 12);
        ctx.lineTo(posX + 5, config.gradientBoxYRange.x);
        ctx.closePath();

        ctx.lineWidth = (isSelected ? 4 : 2);
        if (!isSelected)
            ctx.strokeStyle = "rgb(114,114,114)";
        else
            ctx.strokeStyle = "rgb(0,23,255)";
        ctx.stroke();
        ctx.fillStyle = "rgb(255, 255, 255)";
        ctx.fill();

        return {
            x: posX - 5,
            y: config.gradientBoxYRange.x - 12,
            width: 10,
            height: 12,
            posX: posX,
        };
    }
}
