import { Utils } from '../utils/Utils';
import { Broadphase } from '../collision/Broadphase';


/**
 * 按x或y轴排序后检测,性能好
 * Sweep and prune broadphase along one axis.
 *
 * @class SAPBroadphase
 * @constructor
 * @extends Broadphase
 */
export class SAPBroadphase extends Broadphase {
    /**
     * List of bodies currently in the broadphase.
     * @property axisList
     * @type {Array}
     */
    axisList: any[];
    /**
     * The axis to sort along. 0 means x-axis and 1 y-axis. If your bodies are more spread out over the X axis, set axisIndex to 0, and you will gain some performance.
     * @property axisIndex
     * @type {Number}
     */
    axisIndex: number;

    _addBodyHandler: (e: any) => void;
    _removeBodyHandler: (e: any) => void;

    constructor() {
        super(Broadphase.SAP);


        this.axisList = [];


        this.axisIndex = 0;

        var that = this;
        this._addBodyHandler = function (e) {
            that.axisList.push(e.body);
        };

        this._removeBodyHandler = function (e) {
            // Remove from list
            var idx = that.axisList.indexOf(e.body);
            if (idx !== -1) {
                that.axisList.splice(idx, 1);
            }
        };
    }


    /**
     * Change the world
     * @method setWorld
     * @param {World} world
     */
    setWorld(world) {
        // Clear the old axis array
        this.axisList.length = 0;

        // Add all bodies from the new world
        Utils.appendArray(this.axisList, world.bodies);

        // Remove old handlers, if any
        world
            .off("addBody", this._addBodyHandler)
            .off("removeBody", this._removeBodyHandler);

        // Add handlers to update the list of bodies.
        world.on("addBody", this._addBodyHandler).on("removeBody", this._removeBodyHandler);

        this.world = world;
    };



    sortList() {
        var bodies = this.axisList,
            axisIndex = this.axisIndex;

        // Sort the lists
        sortAxisList(bodies, axisIndex);
    };

    /**
     * Get the colliding pairs
     * @method getCollisionPairs
     * @param  {World} world
     * @return {Array}
     */
    getCollisionPairs(/*world*/) {
        var bodies = this.axisList,
            result = this.result,
            axisIndex = this.axisIndex;

        result.length = 0;

        // Update all AABBs if needed
        var l = bodies.length;
        while (l--) {
            var b = bodies[l];
            if (b.aabbNeedsUpdate) {
                b.updateAABB();
            }
        }

        // Sort the lists
        this.sortList();

        // Look through the X list
        for (var i = 0, N = bodies.length | 0; i !== N; i++) {
            var bi = bodies[i];

            for (var j = i + 1; j < N; j++) {
                var bj = bodies[j];

                // Bounds overlap?
                var overlaps = (bj.aabb.lowerBound[axisIndex] <= bi.aabb.upperBound[axisIndex]);
                if (!overlaps) {
                    break;
                }

                if (Broadphase.canCollide(bi, bj) && this.boundingVolumeCheck(bi, bj)) {
                    result.push(bi, bj);
                }
            }
        }

        return result;
    };

    /**
     * Returns all the bodies within an AABB.
     * @method aabbQuery
     * @param  {World} world
     * @param  {AABB} aabb
     * @param {array} result An array to store resulting bodies in.
     * @return {array}
     * @todo since the list is sorted, optimization can be done
     */
    aabbQuery(world, aabb, result) {
        result = result || [];

        this.sortList();

        var axisList = this.axisList;
        for (var i = 0; i < axisList.length; i++) {
            var b = axisList[i];

            if (b.aabbNeedsUpdate) {
                b.updateAABB();
            }

            if (b.aabb.overlaps(aabb)) {
                result.push(b);
            }
        }

        return result;
    };
}

function sortAxisList(a, axisIndex) {
    axisIndex = axisIndex | 0;
    for (var i = 1, l = a.length; i < l; i++) {
        var v = a[i];
        for (var j = i - 1; j >= 0; j--) {
            if (a[j].aabb.lowerBound[axisIndex] <= v.aabb.lowerBound[axisIndex]) {
                break;
            }
            a[j + 1] = a[j];
        }
        a[j + 1] = v;
    }
    return a;
}