import { Decorator } from "../Decorator/Decorator";
import DisplayCollider from "./DisplayCollider";
import DebugMgr from "../Mgr/DebugMgr";

export default class PhysicalSystem {
    private colliderList: Array<DisplayCollider> = new Array<DisplayCollider>();
    private collisionMap: { [hashCode: number]: number[] } = {};

    private collisions: Array<{ collider1: number, collider2: number }> = [];

    /**
     * @returns 未找到返回-1
     */
    private getCollisionIndex(collider1: number, collider2: number): number {
        for (let i = 0; i < this.collisions.length; i++) {
            if ((this.collisions[i].collider1 == collider1 && this.collisions[i].collider2 == collider2) || (this.collisions[i].collider1 == collider2 && this.collisions[i].collider2 == collider1)) {
                return i;
            }
        }
        //未碰撞
        return -1;
    }

    public addCollider(collider: DisplayCollider) {
        for (let i of this.colliderList) {
            if (i.hashCode === collider.hashCode) {
                console.warn("repeat to add collider");
                return;
            }
        }

        this.colliderList.push(collider);
    }

    public removeCollider(collider: DisplayCollider) {
        let index = this.colliderList.indexOf(collider);
        if (index >= 0) {
            this.colliderList.splice(index, 1);
        } else {
            console.warn("remove the nonexistent collider");
        }
    }

    /**检测碰撞 */
    public detectCollision() {
        DebugMgr.instance.setLog(this.colliderList.length.toString());
        //查找所有碰撞
        for (let i = 0; i <= this.colliderList.length - 1; i++) {
            for (let j = i + 1; j <= this.colliderList.length - 1; j++) {
                this.detectTraverse(this.colliderList[i], this.colliderList[j]);
            }
        }
    }

    private detectTraverse(collider1: DisplayCollider, collider2: DisplayCollider) {
        let collisionIndex = this.getCollisionIndex(collider1.hashCode, collider2.hashCode);
        let rect1 = collider1.rect,
            rect2 = collider2.rect,
            rectW1 = rect1.x + rect1.width,
            rectW2 = rect2.x + rect2.width,
            rectH1 = rect1.height + rect1.y,
            rectH2 = rect2.y + rect2.height;
        if (rect1.x <= rectW2 &&
            rectW1 >= rect2.x &&
            rect1.y <= rectH2 &&
            rectH1 >= rect2.y) { //发生碰撞

            //判断碰撞位置
            let contact1: number = null, contact2: number = null;
            if (rect1.x < rect2.x && rectW1 < rectW2) { //相交，1在左，2在右
                contact1 = 1;
                contact2 = 0;
            } else if (rect2.x < rect1.x && rectW2 < rectW1) { //相交，2在左，1在右
                contact1 = 0;
                contact2 = 1;
            } else if (rect1.x <= rect2.x && rectW1 >= rectW2) { //1包含2
                contact1 = ((rect2.x - rect1.x) + rect2.width / 2) / rect1.width;
                contact2 = 0.5;
            } else if (rect1.x >= rect2.x && rectW1 <= rectW2) { //2包含1
                contact1 = 0.5;
                contact2 = ((rect1.x - rect2.x) + rect1.width / 2) / rect2.width;
            } else {
                console.error("logic error");
            }

            if (collisionIndex > -1) { //之前就发生了碰撞
                collider1.onCollisionStay(collider2, { self: contact1, other: contact2 });
                collider2.onCollisionStay(collider1, { self: contact2, other: contact1 });
            } else { //之前没有碰撞
                //添加到索引表
                this.collisions.push({
                    collider1: collider1.hashCode,
                    collider2: collider2.hashCode
                });
                collider1.onCollisionEnter(collider2, { self: contact1, other: contact2 });
                collider2.onCollisionEnter(collider1, { self: contact2, other: contact1 });
            }
        } else { //未发生碰撞
            //碰撞退出
            if (collisionIndex > -1) { //之前有发生碰撞
                //在碰撞列表中删除这一对碰撞
                this.collisions.splice(collisionIndex, 1);
                collider1.onCollisionExit(collider2);
                collider2.onCollisionExit(collider1);
            }
        }
    }

    private static _instance: PhysicalSystem = null;
    public static get instance(): PhysicalSystem {
        if (!this._instance) {
            this._instance = new PhysicalSystem();
        }

        return this._instance;
    }
}