import { DoubleSide, Vector2 } from "three";
import { Cache } from "../../../../core/cache/Cache.js";
import { invokeObjectEquals } from "../../../../core/model/object/invokeObjectEquals.js";
import { invokeObjectHash } from "../../../../core/model/object/invokeObjectHash.js";
import { GameAssetType } from "../../../asset/GameAssetType.js";
import { EnginePlugin } from "../../../plugin/EnginePlugin.js";
import { MaterialManager } from "../../material/manager/MaterialManager.js";
import { RibbonMaterialX } from "./RibbonMaterialX.js";

export class RibbonXPlugin extends EnginePlugin {
    initialize(engine) {
        super.initialize(engine);

        this.id = 'ribbon-x-plugin';

        /**
         * @private
         * @readonly
         * @type {Cache<RibbonXMaterialSpec, RibbonMaterialX>}
         */
        this.materialCache = new Cache({
            maxWeight: 128,

            keyHashFunction: invokeObjectHash,
            keyEqualityFunction: invokeObjectEquals
        });

        this.__shared_uniforms = {
            resolution: {
                value: new Vector2(1, 1)
            }
        };

        /**
         *
         * @type {MaterialManager}
         * @private
         */
        this.__materials = new MaterialManager();
    }

    __handle_viewport_size_change() {
        const s = this.engine.graphics.viewport.size;

        this.__shared_uniforms.resolution.value.set(s.x, s.y);
    }

    async startup() {
        this.__handle_viewport_size_change();

        this.engine.graphics.viewport.size.onChanged.add(this.__handle_viewport_size_change, this);

        return super.startup();
    }

    async shutdown() {

        this.engine.graphics.viewport.size.onChanged.remove(this.__handle_viewport_size_change, this);

        return super.shutdown();
    }

    /**
     *
     * @param {RibbonXMaterialSpec} spec
     * @return {Reference<Material>}
     */
    obtain_material_reference(spec) {
        const mm = this.obtain_material_x(spec);

        return this.__materials.obtain(mm);
    }

    /**
     *
     * @param {RibbonXMaterialSpec} spec
     * @returns {RibbonMaterialX}
     */
    obtain_material_x(spec) {
        return this.materialCache.getOrCompute(spec, this.__build_material, this);
    }

    /**
     *
     * @param {RibbonXMaterialSpec} spec
     * @returns {RibbonMaterialX}
     * @private
     */
    __build_material(spec) {

        const material = new RibbonMaterialX({
            transparent: true,
            depthWrite: false,
            wireframe: false,
            side: DoubleSide,
            defines: {
                USE_TEXTURE: false
            }
        });

        material.uniforms.resolution = this.__shared_uniforms.resolution;

        if (typeof spec.diffuse === "string") {
            const am = this.engine.assetManager;

            am.get({
                path: spec.diffuse,
                type: GameAssetType.Texture,
                callback: (asset) => {
                    /**
                     *
                     * @type {Texture}
                     */
                    const texture = asset.create();

                    texture.generateMipmaps = true;

                    material.uniforms.uDiffuse.value = texture;
                }, failure: console.warn
            });

            material.defines.USE_TEXTURE = true;
        }


        return material;
    }
}
