
import { AmbientLight, BoxBufferGeometry, BufferGeometry, Color, ConeBufferGeometry, CubeRefractionMapping, CubeTextureLoader, FrontSide, Geometry, LinearEncoding, Mesh, MeshPhysicalMaterial, Object3D, SphereBufferGeometry, sRGBEncoding, Texture, TextureLoader, TorusBufferGeometry, TorusKnotBufferGeometry, Vector3 } from 'three';
import { Singleton } from './Singleton';
import { Viewer } from './Viewer';
import { PMREMGenerator3 } from './PMREMGenerator2';
import type { PhysicalMaterialRecord, TextureTableRecord } from 'webcad_ue4_api';
import { MaterialEditorCameraControl } from './MaterialMouseControl';
import { GetConfig } from '../lib/libOutputConfig';

async function textureRenderUpdate(textureRecord:TextureTableRecord){
    const texture = textureRecord['texture'] as Texture;

    texture.wrapS = textureRecord.WrapS;
    texture.wrapT = textureRecord.WrapT;
    texture.anisotropy = 16;
    texture.rotation = textureRecord.rotation;
    texture.repeat.set(textureRecord.repeatX, textureRecord.repeatY);
    texture.offset.set(textureRecord.moveX, textureRecord.moveY);
    texture.needsUpdate = true;

//     for (let f of this.waits)
//         f();
//     this.waits.length = 0;
// 
//     this.AsyncUpdated();
}
/**
 * 材质编辑器
 */
export class MaterialEditor extends Singleton
{
    private _pointerLocked = false;

    Geometrys: Map<string, Geometry | BufferGeometry>;

    CurGeometryName = "球";
    Canvas: HTMLElement;
    ShowObject: Object3D;
    ShowMesh: Mesh;
    Viewer: Viewer;

    private _MeshMaterial: MeshPhysicalMaterial = new MeshPhysicalMaterial({});
    //构造
    private constructor()
    {
        super();
        this.initGeometrys();

        this.LoadDefaultExr();
        this.LoadMetalEnv();
    }

    initGeometrys()
    {
        this.Geometrys = new Map<string, Geometry | BufferGeometry>(
            [
                ["球", new SphereBufferGeometry(1000, 32, 32)],
                ["圆环", new TorusBufferGeometry(0.8 * 1e3, 0.4 * 1e3, 32, 64)],
                ["立方体", new BoxBufferGeometry(1e3, 1e3, 1e3, 1, 1, 1)],
                ["环面纽结", new TorusKnotBufferGeometry(0.7 * 1e3, 0.3 * 1e3, 128, 64)],
                ["圆锥体", new ConeBufferGeometry(1 * 1e3, 2 * 1e3, 32)],
                // ["球(多面)", new SphereBufferGeometry(1 * 1e3, 64, 64)],
                // ["立方体(多面)", new BoxBufferGeometry(1 * 1e3, 1 * 1e3, 1 * 1e3, 128, 128, 128)]
            ]
        );
    }
    initViewer()
    {
        if (!this.Viewer)
        {
            this.Viewer = new Viewer(this.Canvas);
            // this.Viewer.PreViewer.Cursor.CursorObject.visible = false;
            // this.Viewer.UsePass = false;
            this.initScene();
            new MaterialEditorCameraControl(this.Viewer, this.ShowObject.position);
            this.Viewer.ZoomAll();

            // 初始化相机位置到观察物体的正后方
            // CameraUpdate中的Rotate参数类型并非标准的欧拉角。。。
            this.Viewer.RotateAround(new Vector3(0, -90 * 8, 0), this.ShowObject.position);
            this.Viewer.Pan(new Vector3(0, 0, -1500));
            this.Viewer.UpdateRender();
            this.Viewer.Fov = 90;
        }
    }
    SetViewer(canvas: HTMLElement)
    {
        this.Canvas = canvas;
        this.initViewer();
        this.CurGeometryName = "球";
    }
    initScene()
    {
        let scene = this.Viewer.Scene;
        this.ShowObject = new Object3D();
        let geo = this.Geometrys.get(this.CurGeometryName);
        this.ShowMesh = new Mesh(geo, this._MeshMaterial);
        this.ShowMesh.scale.set(1000, 1000, 1000);

        this.ShowObject.add(this.ShowMesh);
        scene.add(this.ShowObject);

        //环境光
        let ambient = new AmbientLight();
        ambient.intensity = 0.7;
        scene.add(ambient);
    }

    metaTexture: Texture;
    metaPromise: Promise<Texture>;
    async LoadMetalEnv(): Promise<Texture>
    {
        if (this.metaTexture) return this.metaTexture;
        if (this.metaPromise) return this.metaPromise;

        return new Promise(async (res, rej) =>
        {
            let urls = [...GetConfig().envTextureSrc];
            new CubeTextureLoader()
                .load(urls, (t) =>
                {
                    t.encoding = sRGBEncoding;
                    t.mapping = CubeRefractionMapping;
                    t.encoding = LinearEncoding;

                    let pmremGenerator = new PMREMGenerator3(this.Viewer.Renderer);
                    let ldrCubeRenderTarget = pmremGenerator.fromCubemap(t);

                    this.metaTexture = ldrCubeRenderTarget.texture;
                    res(this.metaTexture);

                    this.Viewer.Scene.background = this.metaTexture;
                    this.Viewer.UpdateRender();
                });
        });
    }

    exrPromise: Promise<Texture>;
    exrTexture: Texture;
    async LoadDefaultExr(): Promise<Texture>
    {
        if (this.exrTexture) return this.exrTexture;
        if (this.exrPromise) return this.exrPromise;

        this.exrPromise = new Promise<Texture>((res, rej) =>
        {
            let urls = [...GetConfig().grayEnvTextureSrc];
            new CubeTextureLoader()
                .load(urls, (t) =>
                {
                    t.encoding = sRGBEncoding;
                    t.mapping = CubeRefractionMapping;
                    t.encoding = LinearEncoding;

                    let pmremGenerator = new PMREMGenerator3(this.Viewer.Renderer);
                    let target = pmremGenerator.fromCubemap(t);
                    this.exrTexture = target.texture;
                    res(this.exrTexture);

                    this.Viewer.UpdateRender();
                });
        });
        return this.exrPromise;
    }

    private Material: PhysicalMaterialRecord;
    setMaterial(mat: PhysicalMaterialRecord)
    {
        this.Material = mat;
        this._MeshMaterial.copy(mat.Material);

        let mtl = this._MeshMaterial;
        if (mtl.metalness > 0.8)
            this.LoadMetalEnv().then(env =>
            {
                mtl.envMap = env;
                mtl.needsUpdate = true;
            });
        else
            this.LoadDefaultExr().then(exr =>
            {
                mtl.envMap = exr;
                mtl.needsUpdate = true;
            });

        this.Update();
    }

    dispose()
    {
    }

    async Update()
    {
        let mat = this.ShowMesh.material as MeshPhysicalMaterial;
        mat.needsUpdate = true;

        this._MeshMaterial.copy(this.Material.Material);

        let mtl = this._MeshMaterial;
        if (mtl.metalness > 0.8)
            this.LoadMetalEnv().then(env =>
            {
                mtl.envMap = env;
                mtl.needsUpdate = true;
            });
        else
            this.LoadDefaultExr().then(exr =>
            {
                mtl.envMap = exr;
                mtl.needsUpdate = true;
            });

        this.Viewer.UpdateRender();
    }
}
