import { Object3D } from "./Object3D";
import { Camera } from "./cameras/Camera";
import { PerspectiveCamera } from "./cameras/PerspectiveCamera";
import { WebglRenderer } from "../2d/renderers/WebglRenderer";
import { D3Renderer } from "./D3Renderer";
import { Light } from "./lights/Light";
import { PointLight } from "./lights/PointLight";
import { DirectionalLight } from "./lights/DirectionalLight";
import { Vector3 } from "./math/Vector3";
import { Point, Matrix, Rectangle } from "../2d/math";
import { Vector2 } from "./math/Vector2";
import { MouseEvent } from "../2d/events";
import { Raycaster } from "./Raycaster";
import { Filter } from "../2d/filter";
import { hex2rgb } from "../2d/utils";
import { DirectionalLightShadow } from "./lights/DirectionalLightShadow";

export enum ShadowType {
    basic = "SHADOWMAP_TYPE_BASIC",
    pcf = "SHADOWMAP_TYPE_PCF",
}
/**
 * 作为3D的顶级容器，不能被object3D添加，自身不能添加其他2D元素
 * 带透明度的融合有问题，尽量自行调整渲染顺序
 */
export class Scene3D extends Object3D {
    private _viewId: number = -1;
    /**
     * gl的视窗数据记录，是左下角开始，以防混淆不用Rectangle
     */
    private _viewport: { x: number, y: number, width: number, height: number };
    private _viewX: number = 0;
    get viewX() {
        return this._viewX;
    }
    set viewX(value: number) {
        value = value || 0;
        if (this._viewX === value) return;
        this._viewX = value;
        this._viewId = -1;
    }
    private _viewY: number = 0;
    get viewY() {
        return this._viewY;
    }
    set viewY(value: number) {
        value = value || 0;
        if (this._viewY === value) return;
        this._viewY = value;
        this._viewId = -1;
    }

    private _viewWidth: number = 750;
    get viewWidth() {
        return this._viewWidth;
    }
    set viewWidth(value: number) {
        value = value || 0;
        if (this._viewWidth === value) return;
        this._viewWidth = value;
        this._viewId = -1;
    }
    private _viewHeight: number = 1624;
    get viewHeight() {
        return this._viewHeight;
    }
    set viewHeight(value: number) {
        value = value || 0;
        if (this._viewHeight === value) return;
        this._viewHeight = value;
        this._viewId = -1;
    }
    setViewRect(x: number, y: number, width: number, height: number) {
        this.viewX = x;
        this.viewY = y;
        this.viewWidth = width;
        this.viewHeight = height;
        this._viewId = -1;
    }
    private _fog: { color: Float32Array, near: number, far: number /*density: number*/ }
    setFog(color: number = 0xffffff, near: number = 0, far: number = 100 /*density: number = 0.00025*/) {
        var arr = hex2rgb(color)
        if (this._fog) {
            this._fog.color[0] = arr[0];
            this._fog.color[1] = arr[1];
            this._fog.color[2] = arr[2];
            // this._fog.density = density
            this._fog.near = near;
            this._fog.far = far;
        } else {
            this._fog = { color: new Float32Array(arr), near, far/*density*/ }
        }
    }
    removeFog() {
        this._fog = null;
    }

    /**
     * 父级是2d元素，用于修改视窗位置
     */
    parent: any

    visible: boolean = true
    /**
     * 相机
     */
    camera: Camera;

    private raycaster: Raycaster = new Raycaster();
    /**
     * 阴影类型
     */
    public shadowType: ShadowType = ShadowType.basic;
    /**
     * 滤镜数组
     */
    private _filters: Filter[];
    get filters(): Filter[] { return this._filters && this._filters.slice(); }
    set filters(value: Filter[]) { this._filters = value && value.slice(); }
    /**
     * 可用的滤镜数据，_filters筛选过的
     */
    protected _enabledFilters: Filter[];
    constructor() {
        super()
        this._instanceType = "Scene3D";
        this.scene = this;
        //@ts-ignore为了添加进容器时候的操作，做个兼容
        this.transform = { _parentId: 0 }
        this.camera = new PerspectiveCamera();
    }
    //只处理自己的xy坐标
    updateTransform() {
        if (!this.visible) return
        //子级统统自己的更新方法
        this.updateWorldMatrix();
    }

    renderWebGL(renderer: WebglRenderer) {
        if (!this.visible) return

        //切换渲染插件
        var plugin: D3Renderer = renderer.plugins["d3"];
        //传入camera数据，着色器各自传
        this.camera.updateWorldMatrix();
        plugin.camera = this.camera;
        //处理所有灯，只找同一级的？还是递归找灯
        plugin.lightsConfig = this.getLightConfig();
        //雾化
        plugin.fog = this._fog || null;
        //阴影类型
        plugin.shadowType = this.shadowType;

        //设置插件，会调用start，如果是两个场景，会有问题，因为是同一个插件不会flush上一个，到时注意修改
        renderer.batchManager.setObjectRenderer(plugin);


        this.setViewport(renderer);

        plugin.viewport = this._viewport;

        //处理子级东西
        if (this.filters && this.filters.length) {
            //估计没必要
            // renderer.batchManager.flush();
            const filters = this.filters;
            if (filters) {
                if (!this._enabledFilters) this._enabledFilters = [];
                //重置
                this._enabledFilters.length = 0;
                //筛选可用的
                for (let i = 0; i < filters.length; i++) {
                    if (filters[i].enabled) this._enabledFilters.push(filters[i]);
                }
                //@ts-ignore
                if (this._enabledFilters.length) renderer.filterManager.pushFilter(this, this._enabledFilters);
            }
            this.render(renderer);
            //刷掉批处理
            renderer.batchManager.flush();
            //移除滤镜
            if (filters && this._enabledFilters && this._enabledFilters.length) renderer.filterManager.popFilter();
        } else {
            this.render(renderer);
        }

    }
    setViewport(renderer: WebglRenderer) {
        if (this._viewId !== this.parent.transform._worldID) {
            this._viewId = this.parent.transform._worldID;
            var pt = this.parent.transform.worldMatrix;
            var x = (this.viewX * pt.a) + (this.viewY * pt.c) + pt.tx;
            var y = (this.viewX * pt.b) + (this.viewY * pt.d) + pt.ty;
            //计算实际的viewWidth和viewHeight，简单点直接是父级的缩放，还是要全局的缩放，如果只在舞台
            var scaleX = Math.sqrt((pt.a * pt.a) + (pt.b * pt.b));
            var scaleY = Math.sqrt((pt.c * pt.c) + (pt.d * pt.d));
            //记录
            this._viewport = {
                x,
                y: renderer.height - y - this.viewHeight * scaleY,
                width: this.viewWidth * scaleX,
                height: this.viewHeight * scaleY
            }
        }
        this._viewport && renderer.gl.viewport(
            this._viewport.x,
            this._viewport.y,
            this._viewport.width,
            this._viewport.height
        );
    }

    getBounds() {
        var pt = this.parent.transform.worldMatrix;
        var y = (this.viewX * pt.b) + (this.viewY * pt.d) + pt.ty;
        return new Rectangle(this._viewport.x, y, this._viewport.width, this._viewport.height)
    }
    getLightConfig(
        con: Object3D = this,
        arr: LightsConfig = { pointLights: [], directionalLights: [], ambientLightColor: [0, 0, 0] }
    ) {
        var viewMatrix = this.camera.worldMatrixInverse;
        for (var i = 0; i < con.children.length; i++) {
            var c = con.children[i] as Light;
            if (c instanceof Light) {
                var color: number[] = c.colorVec3.clone().multiplyScalar(c.intensity).toArray();
                if (c.instanceType == "AmbientLight") {
                    arr.ambientLightColor[0] += color[0];
                    arr.ambientLightColor[1] += color[1];
                    arr.ambientLightColor[2] += color[2];
                }
                else if (c.instanceType == "PointLight") {
                    var position: Vector3 = new Vector3().setFromMatrixPosition(c._worldMatrix);
                    position.applyMatrix4(viewMatrix);//这步是否需要，待测试
                    arr.pointLights.push({
                        color: color,
                        position: position.toArray(),
                        distance: c["distance"],
                        decay: c["distance"] === 0 ? 0.0 : c["decay"],
                    })
                }
                else if (c.instanceType == "DirectionalLight") {
                    var direction: Vector3 = new Vector3().setFromMatrixPosition(c._worldMatrix);
                    direction.sub(new Vector3().setFromMatrixPosition(c["target"]._worldMatrix));
                    direction.transformDirection(viewMatrix);
                    var shadowMatrixDepth: number[];
                    if (c.castShadow) {//这里直接计算了灯光的相机吧
                        if (!c.shadow.map) {
                            //暂时不用这个map。因为现在就一个
                        }
                        var shadowCamera = c.shadow.camera,
                            shadowMatrix = c.shadow.matrix,
                            lightP = c._worldMatrix.elements.slice(12, 15),
                            lightTargetP = c["target"]._worldMatrix.elements.slice(12, 15);
                        shadowCamera.position.set(lightP[0], lightP[1], lightP[2]);
                        shadowCamera.lookAt(lightTargetP[0], lightTargetP[1], lightTargetP[2]);
                        shadowCamera.updateWorldMatrix();
                        //利用shadowMatrix算一次
                        shadowMatrixDepth = shadowMatrix.setIdentity()
                            .multiply(shadowCamera.projectionMatrix)
                            .multiply(shadowCamera.worldMatrixInverse).toArray();
                        //重算几次
                        shadowMatrix.set(//这里为了减少着色器里的计算
                            0.5, 0.0, 0.0, 0.5,
                            0.0, 0.5, 0.0, 0.5,
                            0.0, 0.0, 0.5, 0.5,
                            0.0, 0.0, 0.0, 1.0
                        );
                        shadowMatrix.multiply(shadowCamera.projectionMatrix);
                        shadowMatrix.multiply(shadowCamera.worldMatrixInverse);
                    }
                    arr.directionalLights.push({
                        color: color,
                        direction: direction.normalize().toArray(),
                        castShadow: c.castShadow,
                        shadowMatrixDepth,
                        shadowMatrix: c.shadow.matrix.toArray(),
                        bias: c.shadow.bias,
                        radius: c.shadow.radius,
                        mapSize: [c.shadow.mapSize.x, c.shadow.mapSize.y]
                    })
                }
            }
            if (c.children && c.children.length) this.getLightConfig(c, arr);
        }
        return arr
    }
    /**
     * 点击事件，只算场景的先，待mesh等写完所有2d包围盒再说，将几何转成2d的点后，根据索引的所有三角面计算是否选中
     * @param globalPoint 
     * @param isMouseEvent 
     */
    hitTestPoint(globalPoint: Point, isMouseEvent: boolean = false) {
        if (!this.parent || !this.mouseEnable) return null;
        //转成全局
        var pt = this.parent.transform.worldMatrix;
        var x = (this.viewX * pt.a) + (this.viewY * pt.c) + pt.tx;
        var y = (this.viewX * pt.b) + (this.viewY * pt.d) + pt.ty;
        //计算实际的viewWidth和viewHeight，简单点直接是父级的缩放，还是要全局的缩放，如果只在舞台
        var scaleX = Math.sqrt((pt.a * pt.a) + (pt.b * pt.b));
        var scaleY = Math.sqrt((pt.c * pt.c) + (pt.d * pt.d));
        var w = this.viewWidth * scaleX;
        var h = this.viewHeight * scaleY;

        if (globalPoint.x >= x && globalPoint.x <= (x + w) && globalPoint.y >= y && globalPoint.y <= (y + h)) {
        } else {
            return null;
        }
        //检查场景中的
        if (this.mouseChildren) {
            //canvas上的坐标，转成视窗内的坐标，暂时不管旋转的，也不允许scene3D视窗旋转
            x = ((globalPoint.x - x) / w) * 2 - 1;
            y = - ((globalPoint.y - y) / h) * 2 + 1;
            this.raycaster.setFromCamera(new Vector3(x, y, 0.5), this.camera);
            //只需要距离最近的那个就行
            var arr = this.raycaster.intersectObject(this)
            if (arr.length) return arr[0].object
        }
        return this;
    }

    private orbitControlConfig: IOrbitControlConfig;
    setOrbCtrCfg(options: IOrbitControlConfig) {
        const cfg = this.orbitControlConfig;
        if (!cfg || !options) return;
        for (var key in options) {
            if (cfg[key] !== undefined) cfg[key] = options[key];
        }
    }
    //相机控件
    setOrbitControl(options?: IOrbitControlConfig) {
        if (this.orbitControlConfig) return;
        this.orbitControlConfig = {
            enabled: true,
            enableDamping: true,
            dampingFactor: 0.25,
            userZoom: true,
            userZoomSpeed: 1.0,
            userRotate: true,
            userRotateSpeed: 1.0,
            autoRotate: false,
            autoRotateSpeed: 2.0,
            //最小旋转角
            minPolarAngle: 0,
            //最大旋转角
            maxPolarAngle: Math.PI,
            minDistance: 0,
            maxDistance: Infinity,
            enablePitch: true
        }
        //设置属性
        this.setOrbCtrCfg(options);
        const cfg = this.orbitControlConfig;
        // pitch，yaw，roll;为俯仰角，偏航角，翻滚角

        // panOffset.multiplyScalar( 1 - scope.dampingFactor );

        //相机目标点
        var center = new Vector3();

        // this.userPan = true;
        // this.userPanSpeed = 2.0;



        var EPS = 0.000001;
        var PIXELS_PER_ROUND = 1800;

        var rotateStart = new Vector2();
        var rotateEnd = new Vector2();
        var rotateDelta = new Vector2();

        var phiDelta = 0;
        var thetaDelta = 0;
        var scale = 1;

        function getAutoRotationAngle() {
            return 2 * Math.PI / 60 / 60 * cfg.autoRotateSpeed;
        }

        function getZoomScale() {
            return Math.pow(0.95, cfg.userZoomSpeed);
        }

        function rotateLeft(angle: number = getAutoRotationAngle()) {
            thetaDelta -= angle;
        };


        function rotateUp(angle: number = getAutoRotationAngle()) {
            phiDelta -= angle;
        };

        function zoomIn(zoomScale: number = getZoomScale()) {
            scale /= zoomScale;
        };
        function zoomOut(zoomScale: number = getZoomScale()) {
            scale *= zoomScale;
        };

        let update = () => {
            if (
                Math.abs(thetaDelta) < 0.0001 &&
                Math.abs(phiDelta) < 0.0001 &&
                Math.abs(scale - 1) < 0.0001 &&
                !cfg.autoRotate
            ) {
                return
            }

            var position = this.camera.position;
            var offset = position.clone().sub(center);
            var theta = Math.atan2(offset.x, offset.z);
            var phi = Math.atan2(Math.sqrt(offset.x * offset.x + offset.z * offset.z), offset.y);

            if (cfg.autoRotate) rotateLeft();

            theta += thetaDelta;
            phi += phiDelta;

            phi = Math.max(cfg.minPolarAngle, Math.min(cfg.maxPolarAngle, phi));

            phi = Math.max(EPS, Math.min(Math.PI - EPS, phi));

            var radius = offset.length() * scale;

            // restrict radius to be between desired limits
            radius = Math.max(cfg.minDistance, Math.min(cfg.maxDistance, radius));

            offset.x = radius * Math.sin(phi) * Math.sin(theta);
            offset.y = radius * Math.cos(phi);
            offset.z = radius * Math.sin(phi) * Math.cos(theta);

            //相机位置
            position.copy(center).add(offset);

            //相机目标点
            this.camera.lookAt(center.x, center.y, center.z);

            //恢复
            if (cfg.enableDamping) {
                thetaDelta *= (1 - cfg.dampingFactor);
                phiDelta *= (1 - cfg.dampingFactor);
            } else {
                thetaDelta = 0;
                phiDelta = 0;
            }

            scale = 1;
            // if (lastPosition.distanceTo(this.object.position) > 0) {
            //     this.dispatchEvent(changeEvent);
            //     lastPosition.copy(this.object.position);
            // }
        };

        //记录两个手指唯一标识
        let fingles: { x: number, y: number, id: number }[] = [];
        let deltaDis: number = 0;
        //事件，暂时只有旋转，其他判断先不要了
        this.addEventListener(MouseEvent.MOUSE_DOWN, (e: MouseEvent) => {
            if (!cfg.enabled) return
            fingles.push({ x: e.clientX, y: e.clientY, id: e.identifier })
            if (fingles.length >= 2) {
                let f1 = fingles[fingles.length - 1];
                let f2 = fingles[fingles.length - 2];
                deltaDis = Math.sqrt(Math.pow(f1.x - f2.x, 2) + Math.pow(f1.y - f2.y, 2));
            }
            // e.stopPropagation();
            rotateStart.set(e.clientX, e.clientY);
        }, this);

        this.addEventListener(MouseEvent.MOUSE_MOVE, (e: MouseEvent) => {
            if (!cfg.enabled) return
            if (!fingles.length) return;
            if (fingles.length == 1) {
                if (!cfg.userRotate) return;
                if (!rotateStart.x && !rotateStart.y) return
                rotateEnd.set(e.clientX, e.clientY);
                rotateDelta.subVectors(rotateEnd, rotateStart);
                rotateLeft(2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * cfg.userRotateSpeed);
                if (cfg.enablePitch) rotateUp(2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * cfg.userRotateSpeed);
                rotateStart.copy(rotateEnd);
                //更新
                // update();
            }
            //多指，取最后两个，如果两个都移动，怎么保证就一个刷新
            else {
                if (cfg.userZoom === false) return;
                var oneF = fingles[fingles.length - 1];
                var twoF = fingles[fingles.length - 2];
                if (e.identifier == oneF.id) {
                    oneF.x = e.clientX
                    oneF.y = e.clientY
                } else if (e.identifier == twoF.id) {
                    twoF.x = e.clientX
                    twoF.y = e.clientY
                } else {
                    return
                }
                var cDelta = Math.sqrt(Math.pow(oneF.x - twoF.x, 2) + Math.pow(oneF.y - twoF.y, 2));
                if (cDelta - deltaDis > 0) {
                    zoomOut(Math.pow(0.95, cfg.userZoomSpeed / 3));//手势的执行太快了
                } else {
                    zoomIn(Math.pow(0.95, cfg.userZoomSpeed / 3));
                }
                //更新
                // update();
                deltaDis = cDelta;
            }

        }, this)
        let up = (e: MouseEvent) => {//淘宝有bug，全部移除先
            fingles.length = 0;
            // for (var i = 0; i < fingles.length; i++) {
            //     if (fingles[i].id == e.identifier) {
            //         fingles.splice(i, 1);
            //         rotateStart.set(0, 0);
            //         return;
            //     }
            // }

        }
        this.addEventListener(MouseEvent.MOUSE_UP, up, this)
        this.addEventListener(MouseEvent.MOUSE_OUT, up, this)
        //加个自动旋转的
        this.addEventListener(MouseEvent.ENTER_FRAME, () => {
            /*if (autoRotate)*/ update()
        }, this)

        //监听下滚轮
        if (document) document.addEventListener('mousewheel', (event) => {
            if (!cfg.enabled || !cfg.userZoom) return;
            var delta = 0;
            // event.preventDefault();
            if (event["wheelDelta"]) { // WebKit / Opera / Explorer 9
                delta = event["wheelDelta"];
            } else if (event["detail"]) { // Firefox
                delta = - event["detail"];
            }
            if (delta > 0) {
                zoomOut();
            } else {
                zoomIn();
            }
            // update();
        }, false);
    }
}

export interface IOrbitControlConfig {

    enabled?: boolean,
    enableDamping?: boolean,
    dampingFactor?: number,
    userZoom?: boolean,
    userZoomSpeed?: number,

    userRotate?: boolean,
    userRotateSpeed?: number,

    autoRotate?: boolean,
    autoRotateSpeed?: number,

    minPolarAngle?: number,
    maxPolarAngle?: number,

    minDistance?: number,
    maxDistance?: number,

    enablePitch?: boolean
}



export interface LightsConfig {
    pointLights: PointLightConfig[],
    directionalLights: DirectionalLightConfig[],
    ambientLightColor: number[],
}

interface PointLightConfig {
    color: number[],//光源颜色
    position: number[],//点光源位置
    distance: number,//最大光源距离
    decay: number,//衰减系数，默认1，最佳2
}

interface DirectionalLightConfig {
    direction: number[],
    color: number[],
    castShadow: boolean,
    /**
     * 生成深度图的矩阵
     */
    shadowMatrixDepth: number[],
    /**
     * 传入正常场景的光照矩阵
     */
    shadowMatrix: number[],
    bias: number,
    radius: number,
    mapSize: [number, number]
}



// window['$'].ajax({
//     type: "get",
//     url: "//embedlog.duiba.com.cn/exposure/standard",
//     dataType: "jsonp",
//     data: {
//         dpm: 72915 + '.' + 110 + '.' + 1 + '.' + 1,
//         dcm: 202 + '.' + 1 + '.' + 0 + '.' + 0,
//         appId: 72915,
//         domain: '//embedlog.duiba.com.cn'
//     },
//     async: true,
//     success: (result) => {

//     },
//     error: (message) => {

//     }
// });