import { Raycaster, Scene, Vector3, WebGLRenderer, type Vector, type WebGLRendererParameters } from "three";
import { cZeroVec, GetBox, GetBoxArr } from "./GeUtils";
import { PlaneExt } from "./PlaneExt";
import { CameraType, CameraUpdate } from "webcad_ue4_api";

export class Viewer {
    m_LookTarget: any;
    m_Camera: CameraUpdate = new CameraUpdate();
    protected NeedUpdate: boolean = true;
    Renderer: WebGLRenderer;//渲染器  //暂时只用这个类型
    m_DomEl: HTMLElement;    //画布容器

    _Height: number = 0;
    _Width: number = 0;

    _Scene: Scene = new Scene();

    get Scene() {
        return this._Scene;
    }

    get Fov() {
        return this.m_Camera.Fov;
    }

    set Fov(val: number) {
        this.m_Camera.Fov = val;
        this.NeedUpdate = true;
    }

    UpdateRender()
    {
        this.NeedUpdate = true;
    }
    
    /**
     *
     * @param {HTMLElement} canvasContainer 可以传入一个div或者一个画布
     * @memberof Viewer
     */
    constructor(canvasContainer: HTMLElement) {

        this.m_DomEl = canvasContainer;
        this.initRender(canvasContainer);
        this.OnSize();
        this.StartRender();
        window.addEventListener("resize", () => {
            this.OnSize();
        });
        this.InitCamera();
    }

    //初始化render
    initRender(canvasContainer: HTMLElement) {
        let params: WebGLRendererParameters = {
            antialias: true,//antialias:true/false是否开启反锯齿
            precision: "highp",//precision:highp/mediump/lowp着色精度选择
            alpha: true//alpha:true/false是否可以设置背景色透明
        };
        this.Renderer = new WebGLRenderer(params);
        //加到画布
        canvasContainer.appendChild(this.Renderer.domElement);

        this.Renderer.autoClear = true;

        //如果设置，那么它希望所有的纹理和颜色都是预乘的伽玛。默认值为false。
        // this.Renderer.gammaInput = true;
        // this.Renderer.gammaOutput = true;
        // this.Renderer.shadowMap.enabled = true;
        // this.Renderer.toneMapping = ReinhardToneMapping;
        //设置设备像素比。 这通常用于HiDPI设备，以防止模糊输出画布。
        this.Renderer.setPixelRatio(window.devicePixelRatio);
        this.Renderer.physicallyCorrectLights = true;
        //this.Renderer.toneMappingExposure = Math.pow(1, 5.0); // to allow for very bright scenes.

        //设置它的背景色为黑色
        this.Renderer.setClearColor(0xffffff, 1);


        this.OnSize();
    }

    InitCamera() {
        this.m_Camera.CameraType = CameraType.PerspectiveCamera;
        this.m_Camera.Near = 0.001;
        this.m_Camera.Camera.far = 1000000000;
    }
    OnSize = (width?: number, height?: number) => {
        this._Width = width ? width : this.m_DomEl.clientWidth;
        this._Height = height ? height : this.m_DomEl.clientHeight;

        //校验.成为2的倍数 避免外轮廓错误.
        if (this._Width % 2 == 1)
            this._Width -= 1;
        if (this._Height % 2 == 1)
            this._Height -= 1;

        this.Renderer.setSize(this._Width, this._Height);
        this.m_Camera.SetSize(this._Width, this._Height);
    };

    StartRender = () => {
        requestAnimationFrame(this.StartRender);
        if (this._Scene != null && this.NeedUpdate) {
            this.Render();
            this.m_Camera.Update();
            this.NeedUpdate = false;
        }
    };
    Render() {
        this.Renderer.render(this._Scene, this.m_Camera.Camera);
    }

    ScreenToWorld(pt: Vector3, planVec?: Vector3) {
        //变换和求交点
        let plan = new PlaneExt(planVec || new Vector3(0, 0, 1));
        let raycaster = new Raycaster();
        // 射线从相机射线向屏幕点位置
        raycaster.setFromCamera(
            {
                x: (pt.x / this._Width) * 2 - 1,
                y: - (pt.y / this._Height) * 2 + 1
            }
            , this.m_Camera.Camera
        );
        plan.intersectRay(raycaster.ray, pt, true);
    }
    WorldToScreen(pt: Vector3) {
        let widthHalf = this._Width * 0.5;
        let heightHalf = this._Height * 0.5;

        pt.project(this.m_Camera.Camera);

        pt.x = (pt.x * widthHalf) + widthHalf;
        pt.y = - (pt.y * heightHalf) + heightHalf;
    }

    /**
     * 更新视角观测目标(物体中心)
     *
     * @memberof Viewer
     */
    UpdateLockTarget() {
        let renderList = this.Renderer.renderLists.get(this._Scene, this.m_Camera.Camera);
        let box = GetBoxArr(renderList.opaque.map(o => o.object));
        if (box)
            this.m_LookTarget = box.getCenter(new Vector3());
        else
            this.m_LookTarget = cZeroVec;
    }
    Rotate(mouseMove: Vector3) {
        this.m_Camera.Rotate(mouseMove, this.m_LookTarget);
        this.NeedUpdate = true;
    }
    RotateAround(movement: Vector3, center: Vector3) {
        this.m_Camera.Rotate(movement, center);
        this.NeedUpdate = true;
    }
    Pan(mouseMove: Vector3) {
        this.m_Camera.Pan(mouseMove);
        this.NeedUpdate = true;
    }
    Zoom(scale: number, center?: Vector3) {
        this.m_Camera.Zoom(scale, center);
        this.NeedUpdate = true;
    }
    ZoomAll()
    {
        this.m_Camera.ZoomExtentsBox3(GetBox(this._Scene, true));
        this.NeedUpdate = true;
    }

    ViewToTop() {
        this.m_Camera.LookAt(new Vector3(0, 0, -1));
        this.NeedUpdate = true;
    }
    ViewToFront() {
        this.m_Camera.LookAt(new Vector3(0, 1, 0));
        this.NeedUpdate = true;
    }
    ViewToSwiso() {
        this.m_Camera.LookAt(new Vector3(1, 1, -1));
        this.NeedUpdate = true;
    }
}
