import ObjectRenderer from "../2d/renderers/webgl/ObjectRenderer";
import { WebglRenderer } from "../2d/renderers/WebglRenderer";
import { Camera } from "./cameras/Camera";
import { LightsConfig } from "./Scene3D";
import { GLShader, GLBuffer } from "../glCore";
import { Mesh3D } from "./Mesh3D";
import { BaseShader } from "./shaders/BaseShader";
import { LightShader } from "./shaders/LightShader";
import { hex2rgb, rgb2hex } from "../2d/utils";
import { Matrix4 } from "./math/Matrix4";
import { BLEND_MODES } from "../2d/const";
import { BatchBuffer } from "../2d/renderers/webgl/BatchBuffer";
import { BaseMaterial, RenderSideType } from "./materials/BaseMaterial";

const ShaderMap: { [key: string]: GLShader } = {

}

const ShaderIds = {
    BaseMaterial: "BaseShader",
    LightMaterial: "LightShader",
}

//需要管理渲染的状态 resetToDefault  resetTo3D
export class D3Renderer extends ObjectRenderer {
    /**
     * 赋值的相机，需要里面的worldMatrixInverse和projectionMatrix
     */
    camera: Camera;
    /**
     * 灯光数据，用来初始化着色器和着色器传值
     */
    lightsConfig: LightsConfig;

    private curLightkey: string;
    constructor(renderer: WebglRenderer) {
        super(renderer);
    }
    onContextChange() {

    }
    start() {
        //设置3d状态机属性
        // this.renderer.state.resetTo3D();
        //自行设置状态机，原先的有太多问题
        //允许深度检测
        this.renderer.state.setDepthTest(1);
        //混色模式重新设置
        this.renderer.state.setBlendMode(BLEND_MODES.NORMAL_NPM);
        //设置逆时针为正面，下面根据材质自行设置
        //this.renderer.state.setFrontFace(0);
        //设置剔除，下面根据材质自行设置
        //this.setCullFace(1);


        //初始化着色器
        //基础材质的
        if (!ShaderMap[ShaderIds.BaseMaterial]) {
            ShaderMap[ShaderIds.BaseMaterial] = new BaseShader(this.renderer.gl);
        }
        //@ts-ignore 普通材质的
        ShaderMap[ShaderIds.BaseMaterial].hasAddedCamera = false;

        //天空盒材质的，暂时只能放整张图，后面如果加了textureCube再改

        //灯光材质的
        var pointLightNum = this.lightsConfig.pointLights.length;
        var dirLightNum = this.lightsConfig.directionalLights.length;
        this.curLightkey = ShaderIds.LightMaterial + pointLightNum + dirLightNum
        if (!ShaderMap[this.curLightkey]) {
            ShaderMap[this.curLightkey] = new LightShader(
                this.renderer.gl,
                this.lightsConfig.pointLights.length,
                this.lightsConfig.directionalLights.length
            );
        }
        //@ts-ignore//当前只有一种光照材质
        ShaderMap[this.curLightkey].hasAddedLights = false;
        //@ts-ignore//当前只有一种光照材质
        ShaderMap[this.curLightkey].hasAddedCamera = false;
    }
    stop() {
        this.flush();
        //原先的视窗需要重新//讲道理应该渲染器自己搞，算了
        this.renderer._activeRenderTarget.activate();
        // this.renderer.rootRenderTarget.activate();

        this.renderer.state.setState(this.renderer.state.defaultState)
    }

    private meshes: Mesh3D[] = [];
    //继续需要的数据
    render(obj: Mesh3D) {
        if (!obj.geometry || !obj.material) return;
        var mat = obj.material;
        var geo = obj.geometry;
        //提取渲染对象及材质，分组渲染，怎么考虑同一个geometry只需要处理一次attr，
        //分组按
        // if (!this.meshes.length) {
        //     this.meshes.push(obj);
        // } else {
        for (var i = 0; i < this.meshes.length; i++) {
            if (this.meshes[i].material.instanceType == mat.instanceType) {
                this.meshes.splice(i, 0, obj)
                return
            }
        }
        // }
        this.meshes.push(obj);
    }
    flush() {
        //同一材质的模型一起处理，同一个着色器，webglRenderer里会判断，不会重复绑定
        var curShader: GLShader;
        var pointLightNum = this.lightsConfig.pointLights.length;
        var dirLightNum = this.lightsConfig.directionalLights.length;
        var gl: WebGLRenderingContext = this.renderer.gl;
        for (var i = 0; i < this.meshes.length; i++) {
            let mesh = this.meshes[i];//怎么判断是否要重新绑定
            let mat: BaseMaterial = mesh.material;
            let geo = mesh.geometry;
            curShader = ShaderMap[ShaderIds[mat.instanceType]] || ShaderMap[this.curLightkey];
            //绑定着色器
            this.renderer.bindShader(curShader, false);
            //找纹理，绑定纹理，多个纹理也是一样的方法
            var location: number = this.renderer.textureManager.bindTexture(mat.map, undefined, false);
            //传入uniform
            curShader.uniforms["uMap"] = location;
            //uniform参数修改，好像目前就几个，且固定的，以后得仿three建数组
            curShader.uniforms["uMatColor"] = mat.colorArr;
            curShader.uniforms["uMatAlpha"] = mat.alpha;
            //这个估计最好只传一次，后续所有uniform都传一次，存一下
            //光照参数
            //@ts-ignore
            if (mat._lightAffect && !curShader.hasAddedLights) {
                //@ts-ignore
                curShader.hasAddedLights = true
                //只能给当前绑定的shader传值，否则location不存在
                for (var j = 0; j < pointLightNum; j++) {
                    let key = "pointLights[" + j + "]";
                    curShader.uniforms[key].color = this.lightsConfig.pointLights[j].color;
                    curShader.uniforms[key].position = this.lightsConfig.pointLights[j].position;
                    curShader.uniforms[key].distance = this.lightsConfig.pointLights[j].distance;
                    curShader.uniforms[key].decay = this.lightsConfig.pointLights[j].decay;
                }
                for (var j = 0; j < dirLightNum; j++) {
                    let key = "directionalLights[" + j + "]";
                    curShader.uniforms[key].color = this.lightsConfig.directionalLights[j].color;
                    curShader.uniforms[key].direction = this.lightsConfig.directionalLights[j].direction;
                }
                curShader.uniforms.uAmbientLightColor = this.lightsConfig.ambientLightColor;
            }
            //@ts-ignore
            if (!curShader.hasAddedCamera) {
                //@ts-ignore
                curShader.hasAddedCamera = true;
                curShader.uniforms["uViewMatrix"] = this.camera.worldMatrixInverse.toArray();;
                curShader.uniforms["uProjectionMatrix"] = this.camera.projectionMatrix.toArray();
            }
            //mesh的模型矩阵
            curShader.uniforms["uModelMatrix"] = mesh._worldMatrix.toArray();
            //法线矩阵
            if (curShader.uniforms.uNormalMatrix) {
                var modelViewMatrix = new Matrix4().multiplyMatrices(this.camera.worldMatrixInverse, mesh._worldMatrix)
                var normalMatrix = modelViewMatrix.invert().transpose();
                // var normalMatrix = mesh._worldMatrix.invert().transpose();
                curShader.uniforms["uNormalMatrix"] = normalMatrix.toArray()
                // this.setShaderUniform(curShader, "uNormalMatrix", normalMatrix.toArray());
            }

            //几何属性
            var glVaoBuffer = geo._glVaoBuffer[this.renderer.CONTEXT_UID];
            if (!geo._glVaoBuffer[this.renderer.CONTEXT_UID]) {
                glVaoBuffer = geo._glVaoBuffer[this.renderer.CONTEXT_UID] = {
                    // indexBuffer: GLBuffer.createIndexBuffer(gl, null, gl.STATIC_DRAW),
                    attrBuffer: GLBuffer.createVertexBuffer(gl, null, gl.STATIC_DRAW),
                    vaos: {}
                }
                //有索引
                if (geo._indices) glVaoBuffer.indexBuffer = GLBuffer.createIndexBuffer(gl, null, gl.STATIC_DRAW);

                //计算一次，打包所有的顶点属性
                const float32View = geo._attrBuffer.float32View;
                var count = 0;
                for (var j = 0; j < geo._vertices.length / 3; j++) {
                    //顶点
                    float32View[count++] = geo._vertices[j * 3];
                    float32View[count++] = geo._vertices[j * 3 + 1];
                    float32View[count++] = geo._vertices[j * 3 + 2];
                    //@ts-ignore 颜色，以后搞成一位，现在有bug
                    // geo._attrBuffer.uint32View[count++] = rgb2hex(geo._colors.slice(j * 3, j * 3 + 3)) + (1 * 255 << 24)
                    float32View[count++] = geo._colors[j * 3];
                    float32View[count++] = geo._colors[j * 3 + 1];
                    float32View[count++] = geo._colors[j * 3 + 2];
                    // float32View[count++] = 1;
                    //uv
                    float32View[count++] = geo._uvs[j * 2];
                    float32View[count++] = geo._uvs[j * 2 + 1];
                    //法线
                    float32View[count++] = geo._normals[j * 3];
                    float32View[count++] = geo._normals[j * 3 + 1];
                    float32View[count++] = geo._normals[j * 3 + 2];
                }
            }
            //@ts-ignore
            var vao = glVaoBuffer.vaos[curShader._glShaderKey];
            var attrs = curShader.attributes;
            //计算打包顶点信息
            this.packGeometry()
            if (!vao) {
                //@ts-ignore
                vao = glVaoBuffer.vaos[curShader._glShaderKey] = this.renderer.createVao()
                    //索引
                    // .addIndex(glVaoBuffer.indexBuffer)
                    //顶点
                    .addAttribute(glVaoBuffer.attrBuffer, attrs.aPosition, gl.FLOAT, false, geo._vertByteSize, 0 * 4)
                    //颜色
                    .addAttribute(glVaoBuffer.attrBuffer, attrs.aColor, gl.FLOAT, true, geo._vertByteSize, 3 * 4)
                    //uv
                    .addAttribute(glVaoBuffer.attrBuffer, attrs.aTextureCoord, gl.FLOAT, true, geo._vertByteSize, 6 * 4)
                //法线,看情况加
                if (attrs.aNormal) vao.addAttribute(glVaoBuffer.attrBuffer, attrs.aNormal, gl.FLOAT, false, geo._vertByteSize, 8 * 4);
                //索引看情况加
                if (geo._indices) vao.addIndex(glVaoBuffer.indexBuffer);
                // 首次需要绑定buffer到vao
                this.renderer.bindVao(vao);
                //需要分别绑定
                glVaoBuffer.attrBuffer.upload(geo._attrBuffer.vertices, 0, false)
                //有索引才改
                if (geo._indices) glVaoBuffer.indexBuffer.upload(geo._indices, 0, false)
            } else {
                this.renderer.bindVao(vao);
                //不修改，估计都不要变
                // glVaoBuffer.attrBuffer.upload(glVaoBuffer._attrBuffer.vertices, 0, true)
                // glVaoBuffer.indexBuffer.upload(glVaoBuffer._indexBuffer, 0, true)
            }
            //根据材质切换渲染面
            if (mat.side == RenderSideType.DoubleSide) {
                this.renderer.state.setCullFace(0)//不开启剔除
            } else {
                this.renderer.state.setCullFace(1)
                //设置剔除面
                this.renderer.state.setFrontFace(mat.side)
            }
            //绘制
            vao.draw(mat.wireframe ? gl.LINES : gl.TRIANGLES);
        }
        this.meshes.length = 0
    }

    private packGeometry() {

    }

}

interface renderObjInt {



}

WebglRenderer.registerPlugin('d3', D3Renderer);