import { Camera, Effect, PostProcess } from '../loader/babylonjs-import';
import { ssaoapplierShader } from '../loader/CustomBabylonMaterials/shaders/SSAOApplierShader';
import { TwinfinityPostProcess } from './TwinfinityPostProcess';

const SHADER_NAME_PREFIX = 'ssao_compose';
const SCENE_SAMPLER = 'sceneSampler';
const AO_SAMPLER = 'aoSampler';

/**
 * SSAOApplier is the class that modulates the screen buffer with the calculated SSAO (which means that even directly lit areas are modulated, which is not correct but looks kind of nice anyways)
 * The variable called blittedscreen is a postprocess that just writes the current scene to a texture buffer
 */
export class SSAOApplier extends TwinfinityPostProcess {
    private _blitedScreen: PostProcess;
    private _renderScale: number;

    constructor(blitedScreen: PostProcess, renderScale: number) {
        super();
        this._blitedScreen = blitedScreen;
        this._renderScale = renderScale;
    }

    protected initialize(camera: Camera): PostProcess {
        const engine = camera.getEngine();
        const defines = new Array<string>();

        if (this._postProcess === undefined) {
            Effect.ShadersStore[SHADER_NAME_PREFIX + 'FragmentShader'] = ssaoapplierShader.fragment();

            const uniforms: string[] = [];

            const newPostProcess = new PostProcess(
                'SSAO_composer',
                SHADER_NAME_PREFIX,
                uniforms,
                [SCENE_SAMPLER, AO_SAMPLER],
                this._renderScale,
                null,
                undefined,
                engine,
                false,
                defines.join('\n')
            );

            const viewer = camera.getScene().twinfinity.viewer;
            if (viewer) {
                newPostProcess.onApplyObservable.add((effect) => {
                    effect.setTextureFromPostProcess(SCENE_SAMPLER, this._blitedScreen);
                    effect.setTextureFromPostProcess(AO_SAMPLER, newPostProcess); // Babylon is trippy in the way that this call will set the texture to the current screen as it was BEFORE newPostProcess is applied
                });
            } else {
                throw new Error('No viewer created?');
            }

            return newPostProcess;
        } else return this._postProcess;
    }
}
