import { defineProps } from '../types.js';
import { Vector2, Vector3, Vector4, Matrix4x4 } from '@zephyr3d/base';
import { getEngine, getDevice } from '../../../app/api.js';

function getTextureLabel(name) {
    return name.replace(/Texture$/, '').replace(/([a-z0-9])([A-Z])/g, '$1 $2').toLowerCase();
}
/** @internal */ const meshInstanceClsMap = new Map();
/** @internal */ function getMeshMaterialInstanceUniformsClass(cls) {
    let info = meshInstanceClsMap.get(cls);
    if (!info) {
        class C {
            material;
            materialId;
            constructor(material){
                this.material = material;
                this.materialId = getEngine().resourceManager.getAssetId(material.coreMaterial) ?? '';
            }
        }
        const S = {
            ctor: C,
            name: `${cls.name}InstanceUniforms`,
            async createFunc (_ctx, init) {
                const material = await getEngine().resourceManager.fetchMaterial(init);
                if (!material) {
                    console.error(`Material not found when creating material instance uniforms: ${String(init)}`);
                    return {
                        obj: null,
                        loadProps: false
                    };
                }
                return {
                    obj: new C(material.createInstance())
                };
            },
            getInitParams (obj) {
                return obj.materialId;
            },
            getProps () {
                return cls.INSTANCE_UNIFORMS.filter((u)=>!!u.name).map((u)=>({
                        name: u.name,
                        description: `Per-instance override value for ${u.name}`,
                        type: u.type,
                        get (value) {
                            const prop = u.prop;
                            const val = this.material[prop];
                            if (u.type === 'float') {
                                value.num[0] = val;
                            } else if (u.type === 'vec2') {
                                value.num[0] = val.x;
                                value.num[1] = val.y;
                            } else if (u.type === 'vec3' || u.type === 'rgb') {
                                value.num[0] = val.x;
                                value.num[1] = val.y;
                                value.num[2] = val.z;
                            } else if (u.type === 'vec4' || u.type === 'rgba') {
                                value.num[0] = val.x;
                                value.num[1] = val.y;
                                value.num[2] = val.z;
                                value.num[3] = val.w;
                            }
                        },
                        set (value) {
                            const prop = u.prop;
                            if (u.type === 'float') {
                                // @ts-ignore
                                this.material[prop] = value.num[0];
                            } else if (u.type === 'vec2') {
                                // @ts-ignore
                                this.material[prop] = new Vector2(value.num[0], value.num[1]);
                            } else if (u.type === 'vec3' || u.type === 'rgb') {
                                // @ts-ignore
                                this.material[prop] = new Vector3(value.num[0], value.num[1], value.num[2]);
                            } else if (u.type === 'vec4' || u.type === 'rgba') {
                                // @ts-ignore
                                this.material[prop] = new Vector4(value.num[0], value.num[1], value.num[2], value.num[3]);
                            }
                        }
                    }));
            }
        };
        info = {
            C,
            S
        };
        meshInstanceClsMap.set(cls, info);
    }
    return info.S;
}
function getTextureProps(manager, name, type, sRGB, phase, isValid) {
    const textureLabel = getTextureLabel(name);
    return defineProps([
        {
            name: name[0].toUpperCase() + name.slice(1),
            description: `Texture asset ID or file path for the ${textureLabel} texture`,
            type: 'object',
            default: '',
            phase: phase,
            options: {
                mimeTypes: [
                    'image/jpeg',
                    'image/png',
                    'image/tga',
                    'image/vnd.radiance',
                    'image/x-dds',
                    'image/webp'
                ] 
            },
            isNullable () {
                return true;
            },
            get (value) {
                value.str[0] = manager.getAssetId(this[name]) ?? '';
            },
            async set (value) {
                if (!value || !value.str[0]) {
                    this[name] = null;
                } else {
                    const assetId = value.str[0];
                    let tex;
                    try {
                        tex = await manager.fetchTexture(assetId, {
                            linearColorSpace: !sRGB
                        });
                    } catch (err) {
                        console.error(`Load asset failed: ${value.str[0]}: ${err}`);
                        tex = null;
                    }
                    const isValidTextureType = tex?.isTexture2D() ;
                    if (isValidTextureType) {
                        this[name] = tex;
                    } else {
                        console.error('Invalid texture type');
                    }
                }
            },
            isValid () {
                if (this.$isInstance) {
                    return false;
                }
                if (isValid) {
                    return isValid.call(this);
                }
                return true;
            }
        },
        {
            name: name[0].toUpperCase() + name.slice(1, name.length - 7) + 'TexCoordScale',
            description: `UV scale applied to the ${textureLabel} texture`,
            type: 'vec2',
            phase: phase + 1,
            default: [
                1,
                1
            ],
            options: {
                animatable: true
            },
            get (value) {
                const matrix = this[name.slice(0, name.length - 7) + 'TexCoordMatrix'];
                if (!matrix) {
                    value.num[0] = 1;
                    value.num[1] = 1;
                } else {
                    const scale = new Vector3();
                    matrix.decompose(scale, null, null);
                    value.num[0] = scale.x;
                    value.num[1] = scale.y;
                }
            },
            set (value) {
                if (value.num[0] === 1 && value.num[1] === 1) {
                    // @ts-ignore
                    this[name.slice(0, name.length - 7) + 'TexCoordMatrix'] = null;
                } else {
                    // @ts-ignore
                    this[name.slice(0, name.length - 7) + 'TexCoordMatrix'] = Matrix4x4.scaling(new Vector3(value.num[0], value.num[1], 1));
                }
            },
            isValid () {
                if (this.$isInstance) {
                    return false;
                }
                if (isValid) {
                    return !!this[name] && isValid.call(this);
                } else {
                    return !!this[name];
                }
            }
        },
        {
            name: name[0].toUpperCase() + name.slice(1, name.length - 7) + 'TexCoordAddressU',
            description: `U-axis wrap mode for the ${textureLabel} texture, possible values are clamp, repeat and mirrored_repeat`,
            type: 'string',
            options: {
                enum: {
                    labels: [
                        'Clamp',
                        'Repeat',
                        'MirroredRepeat'
                    ],
                    values: [
                        'clamp',
                        'repeat',
                        'mirrored_repeat'
                    ]
                }
            },
            phase: phase + 1,
            default: 'clamp',
            get (value) {
                const sampler = this[name.slice(0, name.length - 7) + 'TextureSampler'] ?? this[name].getDefaultSampler(false);
                value.str[0] = sampler.addressModeU;
            },
            set (value) {
                const sampler = this[name.slice(0, name.length - 7) + 'TextureSampler'] ?? this[name].getDefaultSampler(false);
                this[name.slice(0, name.length - 7) + 'TextureSampler'] = getDevice().createSampler({
                    addressU: value.str[0],
                    addressV: sampler.addressModeV,
                    lodMax: sampler.lodMax,
                    lodMin: sampler.lodMin,
                    magFilter: sampler.magFilter,
                    minFilter: sampler.minFilter,
                    mipFilter: sampler.mipFilter,
                    maxAnisotropy: sampler.maxAnisotropy
                });
            },
            isValid () {
                if (this.$isInstance) {
                    return false;
                }
                if (isValid) {
                    return !!this[name] && isValid.call(this);
                } else {
                    return !!this[name];
                }
            }
        },
        {
            name: name[0].toUpperCase() + name.slice(1, name.length - 7) + 'TexCoordAddressV',
            description: `V-axis wrap mode for the ${textureLabel} texture, possible values are clamp, repeat and mirrored_repeat`,
            type: 'string',
            options: {
                enum: {
                    labels: [
                        'Clamp',
                        'Repeat',
                        'MirroredRepeat'
                    ],
                    values: [
                        'clamp',
                        'repeat',
                        'mirrored_repeat'
                    ]
                }
            },
            phase: phase + 1,
            default: 'clamp',
            get (value) {
                const sampler = this[name.slice(0, name.length - 7) + 'TextureSampler'] ?? this[name].getDefaultSampler(false);
                value.str[0] = sampler.addressModeV;
            },
            set (value) {
                const sampler = this[name.slice(0, name.length - 7) + 'TextureSampler'] ?? this[name].getDefaultSampler(false);
                // @ts-ignore
                this[name.slice(0, name.length - 7) + 'TextureSampler'] = getDevice().createSampler({
                    addressU: sampler.addressModeU,
                    addressV: value.str[0],
                    lodMax: sampler.lodMax,
                    lodMin: sampler.lodMin,
                    magFilter: sampler.magFilter,
                    minFilter: sampler.minFilter,
                    mipFilter: sampler.mipFilter,
                    maxAnisotropy: sampler.maxAnisotropy
                });
            },
            isValid () {
                if (this.$isInstance) {
                    return false;
                }
                if (isValid) {
                    return !!this[name] && isValid.call(this);
                } else {
                    return !!this[name];
                }
            }
        },
        {
            name: name[0].toUpperCase() + name.slice(1, name.length - 7) + 'TexCoordIndex',
            description: `UV set index used by the ${textureLabel} texture`,
            type: 'int',
            phase: phase + 1,
            default: 0,
            get (value) {
                value.num[0] = this[name.slice(0, name.length - 7) + 'TexCoordIndex'];
            },
            set (value) {
                this[name.slice(0, name.length - 7) + 'TexCoordIndex'] = value.num[0];
            },
            isValid () {
                if (this.$isInstance) {
                    return false;
                }
                if (isValid) {
                    return !!this[name] && isValid.call(this);
                } else {
                    return !!this[name];
                }
            }
        }
    ]);
}

export { getMeshMaterialInstanceUniformsClass, getTextureProps, meshInstanceClsMap };
//# sourceMappingURL=common.js.map
