import { Light } from "../scene/index.js";
import { DepthPass } from "./DepthPass.js";
import {} from "./ForwardPass.js";
import { Material } from "./Material.js";
import { MaterialInstance } from "./MaterialInstance.js";
import {} from "./RenderDevice.js";
import {} from "./RenderPass.js";
import { Sampler } from "./Sampler.js";
import { ShadowMap } from "./ShadowMap.js";
import {} from "./ShaderLibrary.js";
import { TextureBinding } from "./TextureBinding.js";
export class ShadowPass {
    options;
    shadowMap;
    lastResult = null;
    renderTarget = null;
    constructor(options) {
        this.options = options;
        this.shadowMap = options.shadowMap ?? new ShadowMap();
    }
    execute(context) {
        const skipReason = this.getSkipReason();
        if (skipReason) {
            this.lastResult = {
                rendered: false,
                reason: skipReason,
                casterCount: 0,
                skippedTransparentCasters: 0,
                targetBacked: false,
                diagnostics: context.device.getDiagnostics()
            };
            return this.lastResult;
        }
        const opaqueCasters = this.options.casters.filter((caster) => !isTransparentCaster(caster));
        const skippedTransparentCasters = this.options.casters.length - opaqueCasters.length;
        if (opaqueCasters.length === 0) {
            this.lastResult = {
                rendered: false,
                reason: "no-opaque-casters",
                casterCount: 0,
                skippedTransparentCasters,
                targetBacked: false,
                diagnostics: context.device.getDiagnostics()
            };
            return this.lastResult;
        }
        const ownsFrame = context.device.captureState().get("frameActive") === false;
        if (ownsFrame) {
            context.device.beginFrame(context.width, context.height);
        }
        try {
            return this.executeWithinFrame(context, opaqueCasters, skippedTransparentCasters);
        }
        finally {
            if (ownsFrame) {
                context.device.endFrame();
            }
        }
    }
    executeWithinFrame(context, opaqueCasters, skippedTransparentCasters) {
        const target = this.ensureRenderTarget(context);
        context.device.setRenderTarget(target);
        initializeShadowTarget(context, target);
        const depthOptions = this.options.shaderLibrary
            ? { casters: opaqueCasters, shaderLibrary: this.options.shaderLibrary, viewProjectionMatrix: this.options.viewProjectionMatrix }
            : { casters: opaqueCasters, viewProjectionMatrix: this.options.viewProjectionMatrix };
        const pass = new DepthPass(depthOptions);
        pass.execute(context);
        context.device.setRenderTarget(null);
        this.lastResult = {
            rendered: true,
            reason: "rendered",
            casterCount: opaqueCasters.length,
            skippedTransparentCasters,
            targetBacked: true,
            shadowTextureLabel: shadowTextureForTarget(target).label,
            shadowTextureKind: target.depthTexture ? "depth-texture" : "encoded-color-depth",
            diagnostics: context.device.getDiagnostics()
        };
        return this.lastResult;
    }
    getLastResult() {
        return this.lastResult;
    }
    getRenderTarget() {
        return this.renderTarget;
    }
    getForwardShadowMap(options) {
        if (!this.renderTarget || !this.lastResult?.rendered) {
            return null;
        }
        return {
            ...options,
            bias: options.bias ?? this.shadowMap.bias,
            filterKernel: options.filterKernel ?? this.shadowMap.filterKernel,
            texture: new TextureBinding({
                name: "u_shadowMapTexture",
                texture: shadowTextureForTarget(this.renderTarget),
                sampler: new Sampler({ minFilter: "nearest", magFilter: "nearest", addressU: "clamp-to-edge", addressV: "clamp-to-edge" }),
                required: true
            })
        };
    }
    dispose() {
        this.renderTarget?.dispose();
        this.renderTarget = null;
        this.shadowMap.dispose();
    }
    ensureRenderTarget(context) {
        if (this.renderTarget && !this.renderTarget.disposed && this.renderTarget.width === this.shadowMap.size && this.renderTarget.height === this.shadowMap.size) {
            return this.renderTarget;
        }
        this.renderTarget?.dispose();
        this.renderTarget = context.device.createRenderTarget({
            width: this.shadowMap.size,
            height: this.shadowMap.size,
            label: `${this.shadowMap.texture.label}-depth-color`,
            format: "rgba8",
            depth: context.device.info.capabilities?.includes("depth-textures") ? "texture" : true
        });
        return this.renderTarget;
    }
    getSkipReason() {
        const light = this.options.light;
        if (!light) {
            return "no-light";
        }
        if (!light.visible) {
            return "light-disabled";
        }
        if (!light.castsShadow) {
            return "not-shadow-casting";
        }
        if (this.options.casters.length === 0) {
            return "no-casters";
        }
        return null;
    }
}
function shadowTextureForTarget(target) {
    return target.depthTexture ?? target.colorTexture;
}
function initializeShadowTarget(context, target) {
    context.device.setRenderTarget(target);
    if (context.device.clearRenderTarget) {
        context.device.clearRenderTarget([1, 1, 1, 1]);
    }
    else {
        context.device.clear([1, 1, 1, 1]);
    }
}
function isTransparentCaster(caster) {
    if (!caster.material) {
        return false;
    }
    const material = getBaseMaterial(caster.material);
    return material.renderState.blend;
}
function getBaseMaterial(material) {
    return material instanceof MaterialInstance ? material.baseMaterial : material;
}
//# sourceMappingURL=ShadowPass.js.map