import { GLSL3, ShaderMaterial, UniformsUtils, Vector2 } from "three";
import { fp_build_fragment_shader } from "./fp_build_fragment_shader.js";
import { fp_build_vertex_shader } from "./fp_build_vertex_shader.js";


export class ForwardPlusThreeMaterial extends ShaderMaterial {
    constructor({ uniforms, fragmentShader, vertexShader }) {

        const _uniforms = UniformsUtils.merge([
            uniforms,
            {
                fp_t_light_tiles: {
                    type: 't',
                    value: null
                },
                fp_t_light_lookup: {
                    type: 't',
                    value: null
                },
                fp_t_light_data: {
                    type: 't',
                    value: null
                },
                fp_t_decal_atlas: {
                    type: 't',
                    value: null
                },
                fp_f_camera_near: {
                    type: 'f',
                    value: 0
                },
                fp_f_camera_far: {
                    type: 'f',
                    value: 0
                },
                fp_resolution: {
                    type: 'v2',
                    value: new Vector2()
                }
            }]);

        super({
            uniforms: _uniforms,
            lights: true,
            vertexShader: fp_build_vertex_shader(vertexShader),
            fragmentShader: fp_build_fragment_shader(fragmentShader),
            glslVersion: GLSL3
        });


        this.__fragment_shader = fragmentShader;
        this.__vertex_shader = vertexShader;

        // this.onBeforeCompile = console.warn;
    }

    /**
     *
     * @param {MeshStandardMaterial} material
     */
    copy_basic(material) {
        this.alphaTest = material.alphaTest;

        this.blendDst = material.blendDst;
        this.blendDstAlpha = material.blendDstAlpha;
        this.blendEquation = material.blendEquation;
        this.blendEquationAlpha = material.blendEquationAlpha;
        this.blending = material.blending;
        this.blendSrc = material.blendSrc;
        this.blendSrcAlpha = material.blendSrcAlpha;

        this.clippingPlanes = material.clippingPlanes;
        this.clipShadows = material.clipShadows;
        this.colorWrite = material.colorWrite;

        this.depthFunc = material.depthFunc;
        this.depthTest = material.depthTest;
        this.depthWrite = material.depthWrite;

        this.fog = material.fog;

        this.stencilWrite = material.stencilWrite;
        this.stencilFunc = material.stencilFunc;
        this.stencilRef = material.stencilRef;
        this.stencilWriteMask = material.stencilWriteMask;
        this.stencilFuncMask = material.stencilFuncMask;
        this.stencilFail = material.stencilFail;
        this.stencilZFail = material.stencilZFail;
        this.stencilZPass = material.stencilZPass;
        this.name = `FP#${material.name}`;

        this.opacity = material.opacity;

        this.polygonOffset = material.polygonOffset;
        this.polygonOffsetFactor = material.polygonOffsetFactor;
        this.polygonOffsetUnits = material.polygonOffsetUnits;

        this.precision = material.precision;

        this.premultipliedAlpha = material.premultipliedAlpha;
        this.dithering = material.dithering;
        this.flatShading = material.flatShading;
        this.side = material.side;
        this.shadowSide = material.shadowSide;
        this.toneMapped = material.toneMapped;
        this.transparent = material.transparent;

        this.vertexColors = material.vertexColors;
        this.visible = material.visible;

        // TODO consider cloning user data
        this.userData = material.userData;

        // copy defines
        for (const definesKey in material.defines) {
            this.defines[definesKey] = material.defines[definesKey];
        }


        if (material.color !== undefined) {
            this.uniforms.diffuse.value.copy(material.color);
        }

        //
        this.lights = true;

        this.skinning = material.skinning;


        // request material update
        this.needsUpdate = true;
    }

    /**
     *
     * @param {LightManager} light_manager
     */
    attach_lights(light_manager) {
        const uniforms = this.uniforms;

        uniforms['fp_t_light_tiles'].value = light_manager.getTextureClusters();
        uniforms['fp_t_light_lookup'].value = light_manager.getTextureLookup();
        uniforms['fp_t_light_data'].value = light_manager.getTextureData();
        uniforms['fp_t_decal_atlas'].value = light_manager.getTextureDecalAtlas();
    }
}
