import { Cache } from "../../../../core/cache/Cache.js";
import { TextureAttachment } from "./TextureAttachment.js";
import { TextureAttachmentsByMaterialType } from "./TextureAttachmensByMaterialType.js";
import { computeTextureHash } from "./computeTextureHash.js";
import { computeTextureEquality } from "./computeTextureEquality.js";
import { computeMaterialHash } from "./computeMaterialHash.js";
import { computeMaterialEquality } from "./computeMaterialEquality.js";


export class StaticMaterialCache {
    constructor() {

        /**
         *
         * @type {Cache<Material, Material>}
         */
        this.materialCache = new Cache({
            maxWeight: 1000,
            keyHashFunction: computeMaterialHash,
            keyEqualityFunction: computeMaterialEquality
        });

        /**
         *
         * @type {Cache<Texture, Texture>}
         */
        this.textureCache = new Cache({
            maxWeight: 1000,
            keyHashFunction: computeTextureHash,
            keyEqualityFunction: computeTextureEquality
        });
    }

    /**
     *
     * @param {Material} material
     * @returns {Material}
     */
    acquire(material) {

        const existingMaterial = this.materialCache.get(material);

        if (existingMaterial !== null) {
            //console.log("Re-used Material:", existingMaterial);
            return existingMaterial;
        }

        const textureCache = this.textureCache;

        //patch textures
        const materialType = material.type;

        /**
         *
         * @type {TextureAttachment[]}
         */
        const attachments = TextureAttachmentsByMaterialType[materialType];


        if (attachments !== undefined) {
            const attachment_count = attachments.length;

            for (let i = 0; i < attachment_count; i++) {
                const attachment = attachments[i];

                const texture = attachment.read(material);

                if (texture === null || texture === undefined) {
                    continue;
                }

                //check cache
                const existingTexture = textureCache.get(texture);

                if (existingTexture !== null) {

                    //console.log('Re-using texture', existingTexture);

                    attachment.write(material, existingTexture);

                } else {

                    //texture was not in cache, cache it
                    textureCache.put(texture, texture);

                }

            }
        }

        //doesn't exist, add
        this.materialCache.put(material, material);

        // console.log("Unique Material:", material);

        return material;

    }

    /**
     *
     * @param {function(string)} error_consumer
     * @param {*} [thisArg]
     * @returns {boolean}
     */
    validate(error_consumer, thisArg) {
        const materials_valid = this.materialCache.validate((message, key, value) => {
            error_consumer.call(thisArg, `Material error: ${message}. Material ${key}`);
        });
        const textures_valid = this.textureCache.validate((message, key, value) => {
            error_consumer.call(thisArg, `Texture error: ${message}. Texture ${key}`);
        });

        return materials_valid
            && textures_valid;
    }
}

/**
 * Global singleton
 * @readonly
 * @type {StaticMaterialCache}
 */
StaticMaterialCache.Global = new StaticMaterialCache();
