import { Scene3D } from "./Scene3D";
import { Matrix4 } from "./math/Matrix4";
import { Vector3 } from "./math/Vector3";
import { Quaternion } from "./math/Quaternion";
import { Euler } from "./math/Euler";
import { EventDispatcher, Event } from "../2d/events";
import { RAD_TO_DEG, DEG_TO_RAD } from "../2d/const"
import { Raycaster, IntersectData } from "./Raycaster";

const tempVector3 = new Vector3();

export class Object3D extends EventDispatcher {
    static DefaultUp: Vector3 = new Vector3(0, 1, 0)
    /**
     * 名字
     */
    public name: string = null;
    /**
     * 是否可见
     */
    public visible: boolean = true;

    public mouseEnable: boolean = true;

    public mouseChildren: boolean = true;
    /**
     * 场景
     */
    public scene: Scene3D;
    /**
     * 子级
     */
    public children: Object3D[] = [];
    /**
     * 父级
     */
    public parent: Object3D = null;
    /**
     * 世界矩阵
     */
    _worldMatrix: Matrix4 = new Matrix4();
    /**
     * 本地矩阵
     */
    _localMatrix: Matrix4 = new Matrix4();

    public up = Object3D.DefaultUp.clone();

    position: Vector3 = new Vector3();
    scale: Vector3 = new Vector3(1, 1, 1);

    rotation: Euler;
    quaternion: Quaternion

    constructor() {
        super();
        this._instanceType = "Object3D";

        var rotation = new Euler();
        var quaternion = new Quaternion();

        function onRotationChange() {
            quaternion.setFromEuler(rotation, false);
        }
        function onQuaternionChange() {
            rotation.setFromQuaternion(quaternion, undefined, false);
        }

        rotation.onChange(onRotationChange);
        quaternion.onChange(onQuaternionChange);

        this.rotation = rotation;
        this.quaternion = quaternion;
    }
    addChild<T extends Object3D>(object: T): T {
        // //是否已添加，不考虑先后渲染了
        // if (this === object.parent) return object;
        // //从原先父级移除自己
        // if (object.parent) object.parent.removeChild(object);
        // object.parent = this;
        // this.children.push(object);
        // //冒泡
        // if (this.scene) object._onDispatchBubbledEvent("onAddedToScene");
        // return object

        this.addChildAt(object, this.children.length);
        return object;
    }
    addChildAt<T extends Object3D>(child: T, index: number): T {
        if (!child) return;
        let s = this;
        let sameParent = (s == child.parent);
        let len: number;
        if (child.parent) {
            if (!sameParent) {
                child.parent.removeChild(child);
            } else {
                len = s.children.length;
                for (let i = 0; i < len; i++) {
                    if (s.children[i] == child) {
                        s.children.splice(i, 1);
                        break;
                    }
                }
            }
        }
        child.parent = s;
        len = s.children.length;
        if (index >= len) {
            s.children[s.children.length] = child;
            index = len;
        } else if (index == 0 || index < 0) {
            s.children.unshift(child);
            index = 0;
        } else {
            s.children.splice(index, 0, child);
        }
        if (s.scene && !sameParent) {
            child._onDispatchBubbledEvent("onAddedToScene");
        }
        return child;
    }
    addChildren<T extends Object3D>(...children: T[]): T[] {
        children.forEach((child: T) => { this.addChild(child); })
        return children;
    }
    removeChild<T extends Object3D>(object: T): T {
        var index = this.children.indexOf(object);
        if (index !== - 1) {
            object.parent = null;
            object._onDispatchBubbledEvent('onRemovedFromScene');
            this.children.splice(index, 1);
        }
        return object
    }
    /**
     * FYGE. Event得加上属性REMOVED_FROM_SCENE，ADDED_TO_SCENE
     * @param type 
     */
    private _onDispatchBubbledEvent(type: string): void {
        let s: Object3D = this;
        if (type == "onRemovedFromScene" && !s.scene) return;
        if (type == "onRemovedFromScene") {
            s.dispatchEvent(type);
            //以防REMOVED_FROM_STAGE事件里用到了scene
            s.scene = null;
        } else if (type == "onAddedToScene") {
            s.scene = s.parent.scene;
            s.dispatchEvent(type);
        }
        let len = s.children.length;
        //子级的处理
        for (let i = 0; i < len; i++) {
            s.children[i]._onDispatchBubbledEvent(type);
        }
    }


    clone(recursive: boolean = true) {
        return new Object3D().copy(this, recursive);
    };
    copy(source: Object3D, recursive: boolean = true) {
        this.name = source.name;
        this.visible = source.visible;
        this.up.copy(source.up);

        this.position.copy(source.position);
        this.quaternion.copy(source.quaternion);
        this.scale.copy(source.scale);

        this._localMatrix.copy(source._localMatrix);
        this._worldMatrix.copy(source._worldMatrix);

        this.visible = source.visible;
        if (recursive === true) {
            for (var i = 0; i < source.children.length; i++) {
                var child = source.children[i];
                this.addChild(child.clone());
            }
        }
        return this;
    }

    lookAt(x: number, y: number, z: number) {
        var q1 = new Quaternion();
        var m1 = new Matrix4();
        var target = new Vector3();
        var position = new Vector3();

        target.set(x, y, z);

        var parent = this.parent;

        this.updateWorldMatrix(true, false);

        position.setFromMatrixPosition(this._worldMatrix);

        if (this._instanceType == "Camera" || this._instanceType == "PerspectiveCamera") {
            m1.lookAt(position, target, this.up);
        } else {
            m1.lookAt(target, position, this.up);
        }
        this.quaternion.setFromRotationMatrix(m1);
        if (parent) {
            m1.extractRotation(parent._worldMatrix);
            q1.setFromRotationMatrix(m1);
            this.quaternion.premultiply(q1.inverse());
        }

    };

    applyMatrix(matrix: Matrix4) {
        this._localMatrix.multiplyMatrices(matrix, this._localMatrix);
        this._localMatrix.decompose(this.position, this.quaternion, this.scale);

    }

    applyQuaternion(q: Quaternion) {
        this.quaternion.premultiply(q);
        return this;
    }

    localToGlobal(vector: Vector3) {
        return vector.applyMatrix4(this._worldMatrix);
    };

    globalToLocal(vector: Vector3) {
        if (vector instanceof Vector3) {
            var m1 = new Matrix4();
            return vector.applyMatrix4(m1.setInverseOf(this._worldMatrix));
        }
        return vector
    };
    /**
     * 获取该物体的舞台坐标
     */
    get stagePos(): { x: number, y: number } {
        if (!this.scene) return null;
        const scene = this.scene;
        this.localToGlobal(tempVector3.set(0, 0, 0));
        tempVector3.project(scene.camera);
        var a = scene.viewWidth / 2;
        var b = scene.viewHeight / 2;
        var x = Math.round(tempVector3.x * a + a) + scene.viewX;//标准设备坐标转屏幕坐标
        var y = Math.round(-tempVector3.y * b + b) + scene.viewY;//标准设备坐标转屏幕坐标
        return { x, y };
    }

    updateLocalMatrix() {
        this._localMatrix.compose(this.position, this.quaternion, this.scale);
        // this.dirty=true
    }
    /**
     * 现在每帧都计算，以后考虑加标记，或手动设置标记更新
     * @param updateParents 
     * @param updateChildren 
     */
    updateWorldMatrix(updateParents: boolean = false, updateChildren: boolean = true) {

        var parent = this.parent;
        if (updateParents === true && parent !== null) {
            //先往父级递归计算矩阵
            parent.updateWorldMatrix(true, false);
        }

        //更新本地矩阵先，后续优化加标记
        this.updateLocalMatrix();
        //给出父级的世界矩阵
        if (this.parent === null || !(this.parent instanceof Object3D)) {
            this._worldMatrix.copy(this._localMatrix);
        } else {
            this._worldMatrix.multiplyMatrices(this.parent._worldMatrix, this._localMatrix);
        }
        if (updateChildren === true) {
            var children = this.children;
            //往子级递归
            for (var i = 0, l = children.length; i < l; i++)children[i].updateWorldMatrix(false, true);
        }
    }
    /**
     * 统一更新方法，子类可重写，基类用于派发监听事件
     */
    update() {
        if (!this.visible) return;
        //监听的
        if (this.hasEventListener(Event.ENTER_FRAME)) {
            this.dispatchEvent(Event.ENTER_FRAME);
        }
        //自己处理
        let len = this.children.length;
        for (let i = len - 1; i >= 0; i--) {
            const child = this.children[i];
            if (child.visible) child.update();
        }
    }


    /**
     * 通过名字获取子级
     * @param name 
     * @param isOnlyOne 
     * @param isRecursive 
     */
    public getChildByName(name: string | RegExp, isOnlyOne: boolean = true, isRecursive: boolean = false): any {
        if (!name) return null;
        let s = this;
        let rex: any;
        if (typeof (name) == "string") {
            rex = new RegExp("^" + name + "$");
        } else {
            rex = name;
        }
        let elements: Array<Object3D> = [];
        Object3D._getElementsByName(rex, s, isOnlyOne, isRecursive, elements);
        let len = elements.length;
        if (len == 0) {
            return null;
        } else if (len == 1) {
            return elements[0];
        } else {
            return elements;
        }
    }
    /**
     * 
     * @param rex 
     * @param root 
     * @param isOnlyOne 
     * @param isRecursive 
     * @param resultList 
     */
    private static _getElementsByName(rex: RegExp, root: Object3D, isOnlyOne: boolean, isRecursive: boolean, resultList: Array<Object3D>): void {
        let len = root.children.length;
        if (len > 0) {
            let name: string;
            let child: any;
            for (let i = 0; i < len; i++) {
                child = root.children[i];
                name = child.name;
                if (name && name != "") {
                    if (rex.test(name)) {
                        resultList[resultList.length] = child;
                        if (isOnlyOne) {
                            return;
                        }
                    }
                }
                if (isRecursive) {
                    if (child["children"] != null) {
                        Object3D._getElementsByName(rex, child, isOnlyOne, isRecursive, resultList);
                    }
                }
            }
        }
    }
    /**
     * 渲染绘制
     */
    render(renderer) {
        //自身的渲染
        this._render(renderer);
        //子级的渲染
        for (var i = 0; i < this.children.length; i++) {
            let child = this.children[i];
            if (child.visible) child.render(renderer)
        }
    }
    /**
     * 子级重写，自身的渲染方式
     */
    protected _render(renderer) {
        //子级重写
        // renderer.batchManager.setObjectRenderer(renderer.plugins["three"]);
        // renderer.plugins["three"].render(this);
    }

    /**
     * 挂个空的吧，只对自己有效，子级的递归，Raycaster方法里已写
     * @param raycaster 
     * @param intersects 
     */
    raycast(raycaster: Raycaster, intersects: IntersectData[]) {


    };

    destroy() {
        //子级的destroy
        for (let i = this.children.length - 1; i >= 0; i--)  this.children[i].destroy();
        //移除事件
        this.removeAllEventListener();
        // super.destroy();//不适用继承的，里面的eventTypes置空很麻烦
        //从父级移除自己
        if (this.parent) this.parent.removeChild(this);
        //还有很多自己的属性，再说了
    }

    ///////////属性get set 方法////////////////////

    /**
     * 位置信息
     */
    get x(): number {
        return this.position.x;
    }
    set x(value: number) {
        this.position.x = value;
    }
    get y(): number {
        return this.position.y;
    }
    set y(value: number) {
        this.position.y = value;
    }
    get z(): number {
        return this.position.z;
    }
    set z(value: number) {
        this.position.z = value;
    }
    /**
     * 缩放信息
     */
    get scaleX() {
        return this.scale.x;
    }
    set scaleX(value: number) {
        this.scale.x = value;
    }
    get scaleY() {
        return this.scale.y;
    }
    set scaleY(value: number) {
        this.scale.y = value;
    }
    get scaleZ() {
        return this.scale.z;
    }
    set scaleZ(value: number) {
        this.scale.z = value;
    }

    /**
     * 旋转信息，角度制度
     */
    get rotationX() {
        return this.rotation.x * RAD_TO_DEG;
    }
    /**
     * 角度制
     */
    set rotationX(value: number) {
        this.rotation.x = value * DEG_TO_RAD;
    }
    /**
     * 旋转信息，角度制度
     */
    get rotationY() {
        return this.rotation.y * RAD_TO_DEG;
    }
    /**
     * 角度制
     */
    set rotationY(value: number) {
        this.rotation.y = value * DEG_TO_RAD;
    }
    /**
     * 旋转信息，角度制度
     */
    get rotationZ() {
        return this.rotation.z * RAD_TO_DEG;
    }
    /**
     * 角度制
     */
    set rotationZ(value: number) {
        this.rotation.z = value * DEG_TO_RAD;
    }
}
