import * as pc from "playcanvas";
let hasOverrideRenderer = false;
const tempClusterArray = [];
/** @enum {number} */
const ToneMapping = {
    LINEAR: pc.TONEMAP_LINEAR, // TONEMAP_LINEAR
    FILMIC: pc.TONEMAP_FILMIC, // TONEMAP_FILMIC
    HEJL: pc.TONEMAP_HEJL, // TONEMAP_HEJL
    ACES: pc.TONEMAP_ACES, // TONEMAP_ACES
    ACES2: pc.TONEMAP_ACES2, // TONEMAP_ACES2
    NEUTRAL: pc.TONEMAP_NEUTRAL, // TONEMAP_NEUTRAL
};
/** @enum {string} */
const SsaoType = {
    NONE: 'none', // SSAOTYPE_NONE
    LIGHTING: 'lighting', // SSAOTYPE_LIGHTING
    COMBINE: 'combine', // SSAOTYPE_COMBINE
};
/** @enum {string} */
const RenderFormat = {
    RGBA8: 'rgba8',
    RG11B10: 'rg11b10',
    RGBA16: 'rgba16f',
    RGBA32: 'rgba32f',
};
// Convert string enum values back to PlayCanvas PIXELFORMAT constants
const formatMap = {
    rgba8: pc.PIXELFORMAT_RGBA8,
    rg11b10: pc.PIXELFORMAT_111110F,
    rgba16f: pc.PIXELFORMAT_RGBA16F,
    rgba32f: pc.PIXELFORMAT_RGBA32F,
};
/** @enum {string} */
const DebugType = {
    NONE: 'none',
    SCENE: 'scene',
    SSAO: 'ssao',
    BLOOM: 'bloom',
    VIGNETTE: 'vignette',
    DOFCOC: 'dofcoc',
    DOFBLUR: 'dofblur',
};
/** @interface */
class Rendering {
    /**
     * @attribute
     * @type {RenderFormat}
     */
    renderFormat = RenderFormat.RG11B10;
    /**
     * @attribute
     * @type {RenderFormat}
     */
    renderFormatFallback0 = RenderFormat.RGBA16;
    /**
     * @attribute
     * @type {RenderFormat}
     */
    renderFormatFallback1 = RenderFormat.RGBA32;
    /**
     * @attribute
     */
    stencil = false;
    /**
     * @attribute
     * @range [0.1, 1]
     * @precision 2
     * @step 0.01
     */
    renderTargetScale = 1.0;
    /**
     * @attribute
     * @range [1, 4]
     * @precision 0
     * @step 1
     */
    samples = 1;
    /**
     * @attribute
     */
    sceneColorMap = false;
    /**
     * @attribute
     */
    sceneDepthMap = false;
    /**
     * @attribute
     * @type {ToneMapping}
     */
    toneMapping = ToneMapping.LINEAR;
    /**
     * @attribute
     * @range [0, 1]
     * @precision 3
     * @step 0.001
     */
    sharpness = 0.0;
    /**
     * @attribute
     * @type {DebugType}
     */
    debug = DebugType.NONE;
}
/** @interface */
class Ssao {
    /**
     * @attribute
     * @type {SsaoType}
     */
    type = SsaoType.NONE;
    /**
     * @attribute
     * @visibleif {type !== 'none'}
     */
    blurEnabled = true;
    /**
     * @attribute
     * @range [0, 1]
     * @visibleif {type !== 'none'}
     * @precision 3
     * @step 0.001
     */
    intensity = 0.5;
    /**
     * @attribute
     * @range [0, 100]
     * @visibleif {type !== 'none'}
     * @precision 3
     * @step 0.001
     */
    radius = 30;
    /**
     * @attribute
     * @range [1, 64]
     * @visibleif {type !== 'none'}
     * @precision 0
     * @step 1
     */
    samples = 12;
    /**
     * @attribute
     * @range [0.1, 10]
     * @visibleif {type !== 'none'}
     * @precision 3
     * @step 0.001
     */
    power = 6;
    /**
     * @attribute
     * @range [1, 90]
     * @visibleif {type !== 'none'}
     * @precision 1
     * @step 1
     */
    minAngle = 10;
    /**
     * @attribute
     * @range [0.5, 1]
     * @visibleif {type !== 'none'}
     * @precision 3
     * @step 0.001
     */
    scale = 1;
}
/** @interface */
class Bloom {
    /**
     * @attribute
     */
    enabled = false;
    /**
     * @attribute
     * @visibleif {enabled}
     * @range [0, 1]
     * @precision 3
     * @step 0.001
     */
    intensity = 0.01;
    /**
     * @attribute
     * @visibleif {enabled}
     * @range [1, 16]
     * @precision 0
     * @step 0
     */
    blurLevel = 16;
}
/** @interface */
class Grading {
    /**
     * @attribute
     */
    enabled = false;
    /**
     * @attribute
     * @visibleif {enabled}
     * @range [0, 3]
     * @precision 3
     * @step 0.001
     */
    brightness = 1;
    /**
     * @attribute
     * @visibleif {enabled}
     * @range [0.5, 1.5]
     * @precision 3
     * @step 0.001
     */
    contrast = 1;
    /**
     * @attribute
     * @visibleif {enabled}
     * @range [0, 2]
     * @precision 3
     * @step 0.001
     */
    saturation = 1;
    /**
     * @attribute
     * @visibleif {enabled}
     */
    tint = new pc.Color(1, 1, 1, 1);
}
/** @interface */
class ColorLUT {
    /**
     * @attribute
     * @type {pc.Asset}
     * @resource texture
     */
    texture = null;
    /**
     * @attribute
     * @visibleif texture
     * @range [0, 1]
     * @precision 3
     * @step 0.001
     */
    intensity = 1;
}
/** @interface */
class Vignette {
    /**
     * @attribute
     */
    enabled = false;
    /**
     * @attribute
     * @visibleif {enabled}
     * @range [0, 1]
     * @precision 3
     * @step 0.001
     */
    intensity = 0.5;
    /**
     * @attribute
     * @visibleif {enabled}
     * @range [0, 3]
     * @precision 3
     * @step 0.001
     */
    inner = 0.5;
    /**
     * @attribute
     * @visibleif {enabled}
     * @range [0, 3]
     * @precision 3
     * @step 0.001
     */
    outer = 1;
    /**
     * @attribute
     * @visibleif {enabled}
     * @range [0.01, 10]
     * @precision 3
     * @step 0.001
     */
    curvature = 0.5;
}
/** @interface */
class Fringing {
    /**
     * @attribute
     */
    enabled = false;
    /**
     * @attribute
     * @visibleif {enabled}
     * @range [0, 100]
     * @precision 1
     * @step 0.1
     */
    intensity = 50;
}
/** @interface */
class Taa {
    /**
     * @attribute
     */
    enabled = false;
    /**
     * @attribute
     * @visibleif {enabled}
     * @range [0, 1]
     * @precision 2
     * @step 0.1
     */
    jitter = 1;
}
/** @interface */
class Dof {
    /**
     * @attribute
     */
    enabled = false;
    /**
     * @attribute
     * @visibleif {enabled}
     */
    highQuality = true;
    /**
     * @attribute
     * @visibleif {enabled}
     */
    nearBlur = false;
    /**
     * @attribute
     * @visibleif {enabled}
     * @precision 2
     * @step 1
     */
    focusDistance = 100;
    /**
     * @attribute
     * @visibleif {enabled}
     * @precision 2
     * @step 1
     */
    focusRange = 10;
    /**
     * @attribute
     * @visibleif {enabled}
     * @precision 2
     * @step 0.1
     */
    blurRadius = 3;
    /**
     * @attribute
     * @visibleif {enabled}
     * @range [1, 10]
     * @precision 0
     * @step 1
     */
    blurRings = 4;
    /**
     * @attribute
     * @visibleif {enabled}
     * @range [1, 10]
     * @precision 0
     * @step 1
     */
    blurRingPoints = 5;
}
export class ViversePostProcessing extends pc.Script {
    static scriptName = 'viversePostProcessing';
    engineCameraFrame;
    /**
     * @attribute
     * @type {Rendering}
     */
    rendering = new Rendering();
    /**
     * @attribute
     * @type {Ssao}
     */
    ssao = new Ssao();
    /**
     * @attribute
     * @type {Bloom}
     */
    bloom = new Bloom();
    /**
     * @attribute
     * @type {Grading}
     */
    grading = new Grading();
    /**
     * @attribute
     * @type {ColorLUT}
     */
    colorLUT = new ColorLUT();
    /**
     * @attribute
     * @type {Vignette}
     */
    vignette = new Vignette();
    /**
     * @attribute
     * @type {Taa}
     */
    taa = new Taa();
    /**
     * @attribute
     * @type {Fringing}
     */
    fringing = new Fringing();
    /**
     * @attribute
     * @type {Dof}
     */
    dof = new Dof();
    initialize() {
        this.engineCameraFrame = new pc.CameraFrame(this.app, this.entity.camera);
        if (!hasOverrideRenderer) {
            // Fix: Intercept WorldClustersAllocator to filter renderActions with invalid layers
            // Guards against undefined/null layers and ensures proper light cluster assignment
            const allocator = this.app.renderer.worldClustersAllocator;
            // assign light clusters to render actions that need it
            allocator.assign = function (renderPasses) {
                // reuse previously allocated clusters
                tempClusterArray.push(...this._allocated);
                this._allocated.length = 0;
                this._clusters.clear();
                // update render actions in passes that use them
                const passCount = renderPasses.length;
                for (let p = 0; p < passCount; p++) {
                    const renderPass = renderPasses[p];
                    const renderActions = renderPass.renderActions;
                    if (renderActions) {
                        // process all render actions
                        const count = renderActions.length;
                        for (let i = 0; i < count; i++) {
                            const ra = renderActions[i];
                            ra.lightClusters = null;
                            // if the layer has lights used by clusters, and meshes
                            const layer = ra.layer;
                            if (layer && layer.hasClusteredLights && layer.meshInstances.length) {
                                // use existing clusters if the lights on the layer are the same
                                const hash = layer.getLightIdHash();
                                const existingRenderAction = this._clusters.get(hash);
                                let clusters = existingRenderAction?.lightClusters;
                                // no match, needs new clusters
                                if (!clusters) {
                                    // use already allocated cluster from last frame, or create a new one
                                    clusters = tempClusterArray.pop() ?? new pc.WorldClusters(this.device);
                                    this._allocated.push(clusters);
                                    this._clusters.set(hash, ra);
                                }
                                ra.lightClusters = clusters;
                            }
                            // no clustered lights, use the cluster with no lights
                            if (!ra.lightClusters) {
                                ra.lightClusters = this.empty;
                            }
                        }
                    }
                }
                // delete leftovers
                tempClusterArray.forEach((item) => item.destroy());
                tempClusterArray.length = 0;
            };
            hasOverrideRenderer = true;
        }
        this.on('enable', () => {
            this.engineCameraFrame.enabled = true;
        });
        this.on('disable', () => {
            this.engineCameraFrame.enabled = false;
        });
        this.on('destroy', () => {
            this.engineCameraFrame.destroy();
        });
        this.on('state', (enabled) => {
            this.engineCameraFrame.enabled = enabled;
        });
    }
    postUpdate(_dt) {
        const cf = this.engineCameraFrame;
        if (!cf) {
            this.engineCameraFrame = new pc.CameraFrame(this.app, this.entity.camera);
            console.warn('[CameraFrameSetting] CameraFrame not found');
            return;
        }
        const { rendering, bloom, grading, vignette, fringing, taa, ssao, dof, colorLUT } = this;
        const dstRendering = cf.rendering;
        dstRendering.renderFormats.length = 0;
        dstRendering.renderFormats.push(formatMap[rendering.renderFormat] ?? pc.PIXELFORMAT_RGBA8);
        dstRendering.renderFormats.push(formatMap[rendering.renderFormatFallback0] ?? pc.PIXELFORMAT_RGBA16F);
        dstRendering.renderFormats.push(formatMap[rendering.renderFormatFallback1] ?? pc.PIXELFORMAT_RGBA32F);
        dstRendering.stencil = rendering.stencil;
        dstRendering.renderTargetScale = rendering.renderTargetScale;
        dstRendering.samples = rendering.samples;
        dstRendering.sceneColorMap = rendering.sceneColorMap;
        dstRendering.sceneDepthMap = rendering.sceneDepthMap;
        dstRendering.toneMapping = rendering.toneMapping;
        dstRendering.sharpness = rendering.sharpness;
        // ssao
        const dstSsao = cf.ssao;
        dstSsao.type = ssao.type;
        if (ssao.type !== SsaoType.NONE) {
            dstSsao.intensity = ssao.intensity;
            dstSsao.radius = ssao.radius;
            dstSsao.samples = ssao.samples;
            dstSsao.power = ssao.power;
            dstSsao.minAngle = ssao.minAngle;
            dstSsao.scale = ssao.scale;
        }
        // bloom
        const dstBloom = cf.bloom;
        dstBloom.intensity = bloom.enabled ? bloom.intensity : 0;
        if (bloom.enabled) {
            dstBloom.blurLevel = bloom.blurLevel;
        }
        // grading
        const dstGrading = cf.grading;
        dstGrading.enabled = grading.enabled;
        if (grading.enabled) {
            dstGrading.brightness = grading.brightness;
            dstGrading.contrast = grading.contrast;
            dstGrading.saturation = grading.saturation;
            dstGrading.tint.copy(grading.tint);
        }
        // colorLUT
        const dstColorLUT = cf.colorLUT;
        if (colorLUT.texture?.resource) {
            dstColorLUT.texture = colorLUT.texture.resource;
            dstColorLUT.intensity = colorLUT.intensity;
        }
        else {
            dstColorLUT.texture = null;
        }
        // vignette
        const dstVignette = cf.vignette;
        dstVignette.intensity = vignette.enabled ? vignette.intensity : 0;
        if (vignette.enabled) {
            dstVignette.inner = vignette.inner;
            dstVignette.outer = vignette.outer;
            dstVignette.curvature = vignette.curvature;
        }
        // taa
        const dstTaa = cf.taa;
        dstTaa.enabled = taa.enabled;
        if (taa.enabled) {
            dstTaa.jitter = taa.jitter;
        }
        // fringing
        const dstFringing = cf.fringing;
        dstFringing.intensity = fringing.enabled ? fringing.intensity : 0;
        // dof
        const dstDof = cf.dof;
        dstDof.enabled = dof.enabled;
        if (dof.enabled) {
            dstDof.highQuality = dof.highQuality;
            dstDof.nearBlur = dof.nearBlur;
            dstDof.focusDistance = dof.focusDistance;
            dstDof.focusRange = dof.focusRange;
            dstDof.blurRadius = dof.blurRadius;
            dstDof.blurRings = dof.blurRings;
            dstDof.blurRingPoints = dof.blurRingPoints;
        }
        // debugging
        cf.debug =
            rendering.debug === DebugType.NONE ? null : rendering.debug;
        cf.update();
    }
}
