import { DRef } from '@zephyr3d/base';

/**
 * Texture property mixin
 * @param name - Texture name
 * @returns Texture mixin
 *
 * @public
 */ function mixinTextureProps(name) {
    return function(BaseCls, vertex = false) {
        var _computedKey, _computedKey1, _computedKey2;
        const capName = `${name[0].toUpperCase()}${name.slice(1)}`;
        const id = `mixinTexture${capName}`;
        let feature = 0;
        let featureTexIndex = 0;
        let featureTexMatrix = 0;
        if (BaseCls[id]) {
            return BaseCls;
        }
        _computedKey = `sample${capName}Texture`, _computedKey1 = `get${capName}TextureUniform`, _computedKey2 = `get${capName}TexCoord`;
        const cls = class extends BaseCls {
            constructor(){
                super();
                const texture = new DRef();
                let sampler = null;
                let texCoord = 0;
                let matrix = null;
                Object.defineProperty(this, `${name}Texture`, {
                    get: function() {
                        return texture.get();
                    },
                    set: function(newValue) {
                        if (texture.get() !== newValue) {
                            texture.set(newValue);
                            this.useFeature(feature, !!newValue);
                            if (newValue) {
                                this.useFeature(featureTexIndex, texCoord);
                                this.useFeature(featureTexMatrix, !!matrix);
                                this.uniformChanged();
                            }
                        }
                    },
                    enumerable: true,
                    configurable: true
                });
                Object.defineProperty(this, `${name}TextureSampler`, {
                    get: function() {
                        return sampler;
                    },
                    set: function(newValue) {
                        if (sampler !== newValue) {
                            sampler = newValue;
                            this.uniformChanged();
                        }
                    },
                    enumerable: true,
                    configurable: true
                });
                Object.defineProperty(this, `${name}TexCoordMatrix`, {
                    get: function() {
                        return matrix;
                    },
                    set: function(newValue) {
                        matrix = newValue;
                        this.useFeature(featureTexMatrix, !!matrix);
                        this.uniformChanged();
                    },
                    enumerable: true,
                    configurable: true
                });
                Object.defineProperty(this, `${name}TexCoordIndex`, {
                    get: function() {
                        return texCoord;
                    },
                    set: function(newValue) {
                        if (texCoord !== newValue) {
                            texCoord = newValue;
                            this.useFeature(featureTexIndex, texCoord);
                            this.uniformChanged();
                        }
                    },
                    enumerable: true,
                    configurable: true
                });
            }
            [_computedKey](scope, texCoord) {
                // @ts-ignore
                const tex = this[`get${capName}TextureUniform`](scope);
                // @ts-ignore
                const coord = texCoord ?? this[`get${capName}TexCoord`](scope);
                return scope.$builder.textureSample(tex, coord);
            }
            [_computedKey1](scope) {
                return scope.$builder.shaderKind === 'fragment' ? scope[`z${capName}Tex`] : null;
            }
            [_computedKey2](scope) {
                const texCoord = this[`${name}TexCoordIndex`];
                if (texCoord < 0) {
                    return null;
                }
                const pb = scope.$builder;
                if (pb.shaderKind === 'vertex' !== !!vertex) {
                    throw new Error(`mixinTextureProps.get${capName}TexCoord(): must be called in ${vertex ? 'vertex' : 'fragment'} stage`);
                }
                return scope.$builder.shaderKind === 'fragment' ? scope.$inputs[`z${capName}TexCoord`] : this.featureUsed(featureTexMatrix) ? pb.mul(scope[`z${capName}TextureMatrix`], pb.vec4(scope.$inputs[`texCoord${texCoord}`], 0, 1)).xy : scope.$inputs[`texCoord${texCoord}`];
            }
            copyFrom(other) {
                super.copyFrom(other);
                const that = this;
                that[`${name}Texture`] = other[`${name}Texture`];
                that[`${name}TextureSampler`] = other[`${name}TextureSampler`];
                that[`${name}TexCoordMatrix`] = other[`${name}TexCoordMatrix`];
                that[`${name}TexCoordIndex`] = other[`${name}TexCoordIndex`];
            }
            vertexShader(scope) {
                super.vertexShader(scope);
                if (vertex || this.needFragmentColor()) {
                    const pb = scope.$builder;
                    const that = this;
                    if (this.featureUsed(feature)) {
                        const texCoordIndex = that[`${name}TexCoordIndex`];
                        if (texCoordIndex >= 0) {
                            const semantic = `texCoord${that[`${name}TexCoordIndex`]}`;
                            if (!scope.$getVertexAttrib(semantic)) {
                                scope.$inputs[semantic] = pb.vec2().attrib(semantic);
                            }
                            if (this.featureUsed(featureTexMatrix)) {
                                scope[`z${capName}TextureMatrix`] = pb.mat4().uniform(2);
                                if (!vertex) {
                                    scope.$outputs[`z${capName}TexCoord`] = pb.mul(scope[`z${capName}TextureMatrix`], pb.vec4(scope.$inputs[semantic], 0, 1)).xy;
                                }
                            } else if (!vertex) {
                                scope.$outputs[`z${capName}TexCoord`] = scope.$inputs[semantic];
                            }
                        }
                    }
                }
            }
            fragmentShader(scope) {
                super.fragmentShader(scope);
                if (this.needFragmentColor()) {
                    const pb = scope.$builder;
                    if (this.featureUsed(feature)) {
                        scope[`z${capName}Tex`] = pb.tex2D().uniform(2);
                    }
                }
            }
            applyUniformValues(bindGroup, ctx, pass) {
                super.applyUniformValues(bindGroup, ctx, pass);
                if (this.needFragmentColor(ctx)) {
                    if (this.featureUsed(feature)) {
                        const that = this;
                        bindGroup.setTexture(`z${capName}Tex`, that[`${name}Texture`], that[`${name}TextureSampler`]);
                        if (this.featureUsed(featureTexMatrix)) {
                            bindGroup.setValue(`z${capName}TextureMatrix`, that[`${name}TexCoordMatrix`]);
                        }
                    }
                }
            }
            onDispose() {
                super.onDispose();
                this[`${name}Texture`] = null;
            }
        };
        feature = cls.defineFeature();
        featureTexIndex = cls.defineFeature();
        featureTexMatrix = cls.defineFeature();
        // @ts-ignore
        cls[id] = true;
        return cls;
    };
}

export { mixinTextureProps };
//# sourceMappingURL=texture.js.map
