import { obtainTerrain } from "../../../src/engine/ecs/terrain/util/obtainTerrain.js";
import { sampler2d_to_data_url } from "../../../src/engine/graphics/texture/sampler/sampler2d_to_data_url.js";
import { ActionUpdateTexture } from "../../actions/concrete/ActionUpdateTexture.js";
import { ArrayCopyAction } from "../../actions/concrete/ArrayCopyAction.js";
import { ModifyPatchTextureArray2DAction } from "../../actions/concrete/ModifyPatchTextureArray2DAction.js";
import { TerrainPaintTool } from "./TerrainPaintTool.js";

export class TerrainTexturePaintTool extends TerrainPaintTool {
    constructor() {
        super();

        this.name = "texture-paint-terrain";

        /**
         *
         * @type {number}
         */
        this.settings.splatIndex = 0;

        /**
         *
         * @type {Float32Array}
         * @private
         */
        this.__splatMapScratch = new Float32Array(0);

        this.__iconLayer = null;
    }


    updateIcon(editor) {

        /**
         *
         * @type {Terrain}
         */
        const terrain = obtainTerrain(editor.engine.entityManager.dataset);

        if (terrain === null) {
            // no terrain
            return;
        }

        /**
         *
         * @type {TerrainLayer}
         */
        const layer = terrain.layers.get(this.settings.splatIndex);

        if (layer === this.__iconLayer) {
            return;
        }

        this.__iconLayer = layer;

        this.buildIcon(terrain);
    }

    /**
     *
     * @param {Terrain} terrain
     */
    buildIcon(terrain) {

        /**
         *
         * @type {TerrainLayer}
         */
        const layer = terrain.layers.get(this.settings.splatIndex);

        if (layer === undefined) {
            return;
        }

        let url = layer.textureDiffuseURL;

        if (url === null) {
            // no URL, try to sample texture directly
            url = sampler2d_to_data_url(layer.diffuse);
        }

        this.icon.set(url);
    }

    initialize() {
        super.initialize();
    }

    finalize() {
        super.finalize();
    }

    async paint(timeDelta) {

        const power = this.settings.brushStrength * timeDelta;

        /**
         *
         * @type {Terrain}
         */
        const terrain = this.terrain;

        const brushPosition = this.__brushPosition;
        const brushSize = this.settings.brushSize;

        const terrainSize = terrain.size;

        const terrainHeight = terrainSize.y;
        const terrainWidth = terrainSize.x;

        const uv_x = brushPosition.x / terrainWidth;
        const uv_y = brushPosition.y / terrainHeight;

        const uv_w = brushSize / terrainWidth;
        const uv_h = brushSize / terrainHeight;

        const uv_x0 = uv_x - uv_w / 2;
        const uv_x1 = uv_x + uv_w / 2;

        const uv_y0 = uv_y - uv_h / 2;
        const uv_y1 = uv_y + uv_h / 2;

        const marker = this.settings.marker;

        const direction = this.modifiers.shift ? -1 : 1;

        const speed = power * direction;

        /**
         *
         * @type {Float32Array}
         */
        const weightData = this.__splatMapScratch;

        /**
         *
         * @type {SplatMapping}
         */
        const splat = terrain.splat;

        const splatWidth = splat.size.x;
        const splatHeight = splat.size.y;


        const h_x0 = uv_x0 * splatWidth;
        const h_x1 = uv_x1 * splatWidth;

        const h_y0 = uv_y0 * splatHeight;
        const h_y1 = uv_y1 * splatHeight;

        const x0 = Math.ceil(h_x0);
        const x1 = Math.floor(h_x1);

        const y0 = Math.ceil(h_y0);
        const y1 = Math.floor(h_y1);

        const splatIndex = this.settings.splatIndex;

        //create action

        // const m_action = new ModifyPatchSampler2DAction(splat_layer_sampler, [x0, y0, x1, y1]);
        const m_action = new ModifyPatchTextureArray2DAction(weightData, [splat.size.x, splat.size.y, splat.depth], [x0, y0, x1, y1]);

        m_action.applyWeightStampToLayer(
            uv_x * splat.size.x, uv_y * splat.size.y,
            marker, splatIndex, speed,
            0, 255
        );

        await this.editor.actions.doMany([
            m_action,
            new ArrayCopyAction(weightData, splat.weightData),
            new ActionUpdateTexture(splat.weightTexture)
        ]);
    }

    start() {
        super.start();

        /**
         *
         * @type {Terrain}
         */
        const terrain = this.terrain;

        const splat = terrain.splat;

        const weightData = splat.weightData;

        /**
         *
         * @type {Float32Array}
         */
        this.__splatMapScratch = new Float32Array(weightData);

        this.editor.actions.mark('paint terrain texture');
    }
}
