import { AbstractMaterialTransformer } from "../../../material/manager/AbstractMaterialTransformer.js";
import { composeCompile } from "../../../material/composeCompile.js";
import { GLSL3, Vector2 } from "three";
import { isLitMaterial } from "./isLitMaterial.js";
import { fp_build_fragment_shader } from "../materials/fp_build_fragment_shader.js";
import { fp_build_vertex_shader } from "../materials/fp_build_vertex_shader.js";
import { fp_build_vertex_lighting_shared } from "../materials/fp_build_vertex_lighting_shared.js";

/**
 * @readonly
 * @type {string}
 */
const PROPERTY_TRANSFORMER_MARKER = '@forward-plus-material-transformer';

export class MaterialTransformer extends AbstractMaterialTransformer {

    /**
     *
     * @param {LightManager} light_manager
     * @param {Vector2} resolution
     */
    constructor({ light_manager, resolution }) {
        super();

        this.__light_manager = light_manager;
        this.__resolution = resolution;

        const uniforms = this.__common_uniforms = {
            fp_t_light_tiles: {
                type: 't',
                value: light_manager.getTextureClusters()
            },
            fp_t_light_lookup: {
                type: 't',
                value: light_manager.getTextureLookup()
            },
            fp_t_light_data: {
                type: 't',
                value: light_manager.getTextureData()
            },
            fp_t_decal_atlas: {
                type: 't',
                value: light_manager.getTextureDecalAtlas()
            },
            fp_t_decal_atlas_resolution: {
                type: 't',
                value: new Vector2(1, 1)
            },
            fp_f_camera_near: {
                type: 'f',
                value: 0
            },
            fp_f_camera_far: {
                type: 'f',
                value: 0
            },
            fp_resolution: {
                type: 'v2',
                value: resolution
            }
        };

        this.__on_before_compile = (shader) => {
            Object.assign(shader.uniforms, uniforms);

            shader.glslVersion = GLSL3;

            if (shader.defines.LIGHTING_VERTEX_ONLY === true) {
                // Lambertial vertex-space lighting
                shader.vertexShader = fp_build_vertex_lighting_shared(shader.vertexShader);
            } else {

                shader.vertexShader = fp_build_vertex_shader(shader.vertexShader);
                shader.fragmentShader = fp_build_fragment_shader(shader.fragmentShader);
            }
        };
    }

    /**
     *
     * @param {CameraView} c
     */
    updateCamera(c) {
        const us = this.__common_uniforms;

        us.fp_f_camera_far.value = c.camera.far;
        us.fp_f_camera_near.value = c.camera.near;

        // TODO move to "onbefore render"
        us.fp_t_decal_atlas_resolution.value.set(
            us.fp_t_decal_atlas.value.image.width,
            us.fp_t_decal_atlas.value.image.height,
        );
    }

    transform(source) {
        if (source.hasOwnProperty(PROPERTY_TRANSFORMER_MARKER)) {
            // already transformed

            if (source[PROPERTY_TRANSFORMER_MARKER] !== this) {
                throw new Error('The material is already transformed, but is associated with a different transformer instance');
            } else {
                return source;
            }

        }

        let result = source;

        if (isLitMaterial(source)) {

            result = source.clone();
            // inherit uniforms directly

            if (source.isShaderMaterial) {
                // TODO use Proxy
                result.uniforms = Object.assign({}, source.uniforms);
                result.defines = Object.assign({}, source.defines);
            }

            result.onBeforeCompile = composeCompile(source.onBeforeCompile, this.__on_before_compile);


            result[PROPERTY_TRANSFORMER_MARKER] = this;

        }

        return result;
    }
}
