/*
 * JBox2D - A Java Port of Erin Catto's Box2D
 * 
 * JBox2D homepage: http://jbox2d.sourceforge.net/ 
 * Box2D homepage: http://www.box2d.org
 * 
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 * 
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 * 
 * 1. The origin of this software must not be misrepresented; you must not
 * claim that you wrote the original software. If you use this software
 * in a product, an acknowledgment in the product documentation would be
 * appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 * misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */

package org.jbox2d.collision;

//Version: b2BroadPhase.h/.cpp rev 108->139

//import java.util.Arrays;
//import java.util.Collections;

import org.jbox2d.common.MathUtils;
import org.jbox2d.common.Settings;
import org.jbox2d.common.Vec2;



// Notes:
// - we use bound arrays instead of linked lists for cache coherence.
// - we use quantized integral values for fast compares.
// - we use short indices rather than pointers to save memory.
// - we use a stabbing count for fast overlap queries (less than order N).
// - we also use a time stamp on each proxy to speed up the registration of
// overlap query results.
// - where possible, we compare bound indices instead of values to reduce
// cache misses (TODO_ERIN).
// - no broadphase is perfect and neither is this one: it is not great for
// huge worlds (use a multi-SAP instead), it is not great for large objects.

class BoundValues {
    public int[] lowerValues;
    public int[] upperValues;
    
    public BoundValues() {
        lowerValues = new int[2];
        upperValues = new int[2];
    }
}

/**
 * This broad phase uses the Sweep and Prune algorithm as described in:
 * Collision Detection in Interactive 3D Environments by Gino van den Bergen
 * Also, some ideas, such as using integral values for fast compares comes from
 * Bullet (http:/www.bulletphysics.com).
 */
public class BroadPhase {
    public static final int INVALID = Integer.MAX_VALUE;

    public static final int NULL_EDGE = Integer.MAX_VALUE;

    public PairManager m_pairManager;

    public Proxy m_proxyPool[];

    int m_freeProxy;

    BufferedPair pairBuffer[];

    int m_pairBufferCount;

    public Bound m_bounds[][];

    //PairCallback pairCallback;

    int m_queryResults[];

    int m_queryResultCount;

    public AABB m_worldAABB;

    public Vec2 m_quantizationFactor;

    public int m_proxyCount;

    int m_timeStamp;

    private static final boolean debugPrint = false;

    public static final boolean s_validate = false;

    // Dumps m_bounds array to console for debugging
    private void dump() {
        for (int i = 0; i < 10; i++) {
            System.out.print("bounds[ %d ] = %d, %d \n" + i + " " +
                    m_bounds[0][i].value + " " + m_bounds[1][i].value);
        }
    }

    public BroadPhase(AABB worldAABB, PairCallback callback) {
        if (debugPrint) {
            System.out.println("BroadPhase()");
        }
        
        // array initialization
        m_proxyPool = new Proxy[Settings.maxProxies];
        pairBuffer = new BufferedPair[Settings.maxPairs];
        m_bounds = new Bound[2][2 * Settings.maxProxies];
        m_queryResults = new int[Settings.maxProxies];

        for (int i = 0; i < 2 * Settings.maxProxies; i++) {
            m_bounds[0][i] = new Bound();
            m_bounds[1][i] = new Bound();
        }

        for (int i = 0; i < Settings.maxProxies; i++) {
            pairBuffer[i] = new BufferedPair();
        }

        m_pairManager = new PairManager();
        m_pairManager.initialize(this, callback);
        
        //assert worldAABB.isValid();

        m_worldAABB = new AABB(worldAABB);
        m_proxyCount = 0;

        Vec2 d = worldAABB.upperBound.sub(worldAABB.lowerBound);
        m_quantizationFactor = new Vec2(Integer.MAX_VALUE / d.x,
                Integer.MAX_VALUE / d.y);

        for (int i = 0; i < Settings.maxProxies - 1; ++i) {
            m_proxyPool[i] = new Proxy();
            m_proxyPool[i].setNext(i + 1);
            m_proxyPool[i].timeStamp = 0;
            m_proxyPool[i].overlapCount = INVALID;
            m_proxyPool[i].userData = null;
        }

        m_proxyPool[Settings.maxProxies - 1] = new Proxy();
        m_proxyPool[Settings.maxProxies - 1].setNext(PairManager.NULL_PROXY);
        m_proxyPool[Settings.maxProxies - 1].timeStamp = 0;
        m_proxyPool[Settings.maxProxies - 1].overlapCount = INVALID;
        m_proxyPool[Settings.maxProxies - 1].userData = null;
        m_freeProxy = 0;

        m_timeStamp = 1;
        m_queryResultCount = 0;
    }
    
 // This one is only used for validation.
    protected boolean testOverlap(Proxy p1, Proxy p2) {
        for (int axis = 0; axis < 2; ++axis)
        {
            Bound[] bounds = m_bounds[axis];

            //assert(p1.lowerBounds[axis] < 2 * m_proxyCount);
            //assert(p1.upperBounds[axis] < 2 * m_proxyCount);
            //assert(p2.lowerBounds[axis] < 2 * m_proxyCount);
            //assert(p2.upperBounds[axis] < 2 * m_proxyCount);

            if (bounds[p1.lowerBounds[axis]].value > bounds[p2.upperBounds[axis]].value)
                return false;

            if (bounds[p1.upperBounds[axis]].value < bounds[p2.lowerBounds[axis]].value)
                return false;
        }

        return true;
    }

    private boolean testOverlap(BoundValues b, Proxy p) {
        for (int axis = 0; axis < 2; ++axis)
        {
            Bound[] bounds = m_bounds[axis];

            //assert(p.lowerBounds[axis] < 2 * m_proxyCount);
            //assert(p.upperBounds[axis] < 2 * m_proxyCount);

            if (b.lowerValues[axis] > bounds[p.upperBounds[axis]].value)
                return false;

            if (b.upperValues[axis] < bounds[p.lowerBounds[axis]].value)
                return false;
        }

        return true;
    }

//    boolean shouldCollide(int id1, int id2) {
//        assert (id1 < Settings.maxProxies);
//        assert (id2 < Settings.maxProxies);
//        Proxy p1 = m_proxyPool[id1];
//        Proxy p2 = m_proxyPool[id2];
//
//        if (p1.groupIndex == p2.groupIndex && p1.groupIndex != 0) {
//            return p1.groupIndex > 0;
//        }
//
//        return (p1.maskBits & p2.categoryBits) != 0
//                && (p1.categoryBits & p2.maskBits) != 0;
//    }

    public Proxy getProxy(int proxyId) {
        if (proxyId == PairManager.NULL_PROXY
                || (m_proxyPool[proxyId].isValid() == false)) {
            return null;
        }
        else {
            return m_proxyPool[proxyId];
        }
    }

    // Create and destroy proxies. These call Flush first.
    int createProxy(AABB aabb, //int groupIndex, int categoryBits, int maskBits,
            Object userData) {
        if (debugPrint) {
            System.out.println("CreateProxy()");
        }
        
        //assert(m_proxyCount < Settings.maxProxies);
        //assert(m_freeProxy != PairManager.NULL_PROXY);
        
        int proxyId = m_freeProxy;
        Proxy proxy = m_proxyPool[proxyId];
        m_freeProxy = proxy.getNext();

        proxy.overlapCount = 0;
        proxy.userData = userData;
//        proxy.groupIndex = groupIndex;
//        proxy.categoryBits = categoryBits;
//        proxy.maskBits = maskBits;
        
        //assert m_proxyCount < Settings.maxProxies;

        int boundCount = 2 * m_proxyCount;

        int lowerValues[] = new int[2];
        int upperValues[] = new int[2];
        computeBounds(lowerValues, upperValues, aabb);

        for (int axis = 0; axis < 2; ++axis) {
            Bound[] bounds = m_bounds[axis];
            int[] indexes = new int[2];

            query(indexes, lowerValues[axis], upperValues[axis], bounds,
                    boundCount, axis);
            int lowerIndex = indexes[0];
            int upperIndex = indexes[1];

            // System.out.println(edgeCount + ", "+lowerValues[axis] + ",
            // "+upperValues[axis]);
            // memmove(bounds[upperIndex + 2], bounds[upperIndex],
            // (edgeCount - upperIndex) * sizeof(b2Bound));

            System.arraycopy(m_bounds[axis], upperIndex, m_bounds[axis],
                    upperIndex + 2, boundCount - upperIndex);
            for (int i = 0; i < boundCount - upperIndex; i++) {
                m_bounds[axis][upperIndex + 2 + i] = new Bound(
                        m_bounds[axis][upperIndex + 2 + i]);
            }

            // memmove(bounds[lowerIndex + 1], bounds[lowerIndex],
            // (upperIndex - lowerIndex) * sizeof(b2Bound));
            // System.out.println(lowerIndex+" "+upperIndex);
            System.arraycopy(m_bounds[axis], lowerIndex, m_bounds[axis],
                    lowerIndex + 1, upperIndex - lowerIndex);
            for (int i = 0; i < upperIndex - lowerIndex; i++) {
                m_bounds[axis][lowerIndex + 1 + i] = new Bound(
                        m_bounds[axis][lowerIndex + 1 + i]);
            }

            // The upper index has increased because of the lower bound
            // insertion.
            ++upperIndex;

            // Copy in the new bounds.

            // if (bounds[lowerIndex] == null)
            //assert (bounds[lowerIndex] != null) : "Null pointer (lower)";
            // if (bounds[upperIndex] == null)
            //assert (bounds[upperIndex] != null) : "Null pointer (upper)";

            bounds[lowerIndex].value = lowerValues[axis];
            bounds[lowerIndex].proxyId = proxyId;
            bounds[upperIndex].value = upperValues[axis];
            bounds[upperIndex].proxyId = proxyId;

            bounds[lowerIndex].stabbingCount = lowerIndex == 0 ? 0
                    : bounds[lowerIndex - 1].stabbingCount;
            bounds[upperIndex].stabbingCount = bounds[upperIndex - 1].stabbingCount;

            // System.out.printf("lv: %d , lid: %d, uv: %d, uid: %d
            // \n",lowerValues[axis],proxyId,upperValues[axis],proxyId);

            // Adjust the stabbing count between the new bounds.
            for (int index = lowerIndex; index < upperIndex; ++index) {
                ++bounds[index].stabbingCount;
            }

            // Adjust the all the affected bound indices.
            for (int index = lowerIndex; index < boundCount + 2; ++index) {
                Proxy proxyn = m_proxyPool[bounds[index].proxyId];
                if (bounds[index].isLower()) {
                    proxyn.lowerBounds[axis] = index;
                }
                else {
                    proxyn.upperBounds[axis] = index;
                }
            }
        }

        ++m_proxyCount;

        //assert m_queryResultCount < Settings.maxProxies;
        // Create pairs if the AABB is in range.
        for (int i = 0; i < m_queryResultCount; ++i) {
            //assert(m_queryResults[i] < Settings.maxProxies);
            //assert(m_proxyPool[m_queryResults[i]].isValid());

            m_pairManager.addBufferedPair(proxyId, m_queryResults[i]);
        }

        m_pairManager.commit();

        if (s_validate)
        {
            validate();
        }

        // Prepare for next query.
        m_queryResultCount = 0;
        incrementTimeStamp();

        return proxyId;
    }

    public void destroyProxy(int proxyId) {
        //assert(0 < m_proxyCount && m_proxyCount <= Settings.maxProxies);
        Proxy proxy = m_proxyPool[proxyId];
        //assert(proxy.isValid());

        int boundCount = 2 * m_proxyCount;

        for (int axis = 0; axis < 2; ++axis) {
            Bound[] bounds = m_bounds[axis];

            int lowerIndex = proxy.lowerBounds[axis];
            int upperIndex = proxy.upperBounds[axis];
            int lowerValue = bounds[lowerIndex].value;
            int upperValue = bounds[upperIndex].value;

            // memmove(bounds + lowerIndex, bounds + lowerIndex + 1,
            // (upperIndex - lowerIndex - 1) * sizeof(b2Bound));
            // memmove(bounds[lowerIndex + 1], bounds[lowerIndex],
            // (upperIndex - lowerIndex) * sizeof(b2Bound));
            System.arraycopy(m_bounds[axis], lowerIndex + 1, m_bounds[axis],
                    lowerIndex, upperIndex - lowerIndex - 1);
            for (int i = 0; i < upperIndex - lowerIndex - 1; i++) {
                m_bounds[axis][lowerIndex + i] = new Bound(
                        m_bounds[axis][lowerIndex + i]);
            }
            // memmove(bounds + upperIndex-1, bounds + upperIndex + 1,
            // (edgeCount - upperIndex - 1) * sizeof(b2Bound));
            System.arraycopy(m_bounds[axis], upperIndex + 1, m_bounds[axis],
                    upperIndex - 1, boundCount - upperIndex - 1);
            for (int i = 0; i < boundCount - upperIndex - 1; i++) {
                m_bounds[axis][upperIndex - 1 + i] = new Bound(
                        m_bounds[axis][upperIndex - 1 + i]);
            }

            // Fix bound indices.
            for (int index = lowerIndex; index < boundCount - 2; ++index) {
                Proxy proxyn = m_proxyPool[bounds[index].proxyId];
                if (bounds[index].isLower()) {
                    proxyn.lowerBounds[axis] = index;
                }
                else {
                    proxyn.upperBounds[axis] = index;
                }
            }

            // Fix stabbing count.
            for (int index = lowerIndex; index < upperIndex - 1; ++index) {
                --bounds[index].stabbingCount;
            }

            // Query for pairs to be removed. lowerIndex and upperIndex are not
            // needed.
            int[] ignored = new int[2];
            query(ignored, lowerValue, upperValue, bounds, boundCount - 2, axis);
        }

        //assert (m_queryResultCount < Settings.maxProxies);

        for (int i = 0; i < m_queryResultCount; ++i) {
            //assert(m_proxyPool[m_queryResults[i]].isValid());
            m_pairManager.removeBufferedPair(proxyId, m_queryResults[i]);
        }
        
        m_pairManager.commit();

        // Prepare for next query.
        m_queryResultCount = 0;
        incrementTimeStamp();

        // Return the proxy to the pool.
        proxy.userData = null;
        proxy.overlapCount = BroadPhase.INVALID;
        proxy.lowerBounds[0] = BroadPhase.INVALID;
        proxy.lowerBounds[1] = BroadPhase.INVALID;
        proxy.upperBounds[0] = BroadPhase.INVALID;
        proxy.upperBounds[1] = BroadPhase.INVALID;


        // Return the proxy to the pool.
        proxy.setNext(m_freeProxy);
        m_freeProxy = proxyId;
        --m_proxyCount;

         if (s_validate) {
             validate();
         }
    }

    // Call MoveProxy as many times as you like, then when you are done
    // call Flush to finalized the proxy pairs (for your time step).
    void moveProxy(int proxyId, AABB aabb) {
        if (debugPrint) {
            System.out.println("MoveProxy()");
        }

        if (proxyId == PairManager.NULL_PROXY || Settings.maxProxies <= proxyId) {
            return;
        }

        //assert (aabb.isValid()) : "invalid AABB";

        int boundCount = 2 * m_proxyCount;

        Proxy proxy = m_proxyPool[proxyId];
        
        //Get new bound values
        BoundValues newValues = new BoundValues();
        computeBounds(newValues.lowerValues, newValues.upperValues, aabb);
        
        //Get old bound values
        BoundValues oldValues = new BoundValues();
        for (int axis = 0; axis < 2; ++axis) {
            oldValues.lowerValues[axis] = m_bounds[axis][proxy.lowerBounds[axis]].value;
            oldValues.upperValues[axis] = m_bounds[axis][proxy.upperBounds[axis]].value;
        }

        for (int axis = 0; axis < 2; ++axis) {
            Bound[] bounds = m_bounds[axis];

            int lowerIndex = proxy.lowerBounds[axis];
            int upperIndex = proxy.upperBounds[axis];

            int lowerValue = newValues.lowerValues[axis];
            int upperValue = newValues.upperValues[axis];

            int deltaLower = lowerValue - bounds[lowerIndex].value;
            int deltaUpper = upperValue - bounds[upperIndex].value;

            bounds[lowerIndex].value = lowerValue;
            bounds[upperIndex].value = upperValue;

            //
            // Expanding adds overlaps
            //

            // Should we move the lower bound down?
            if (deltaLower < 0) {
                int index = lowerIndex;
                while (index > 0 && lowerValue < bounds[index - 1].value) {
                    Bound bound = bounds[index];
                    Bound prevBound = bounds[index - 1];

                    int prevProxyId = prevBound.proxyId;
                    Proxy prevProxy = m_proxyPool[prevBound.proxyId];

                    ++prevBound.stabbingCount;

                    if (prevBound.isUpper() == true) {
                        if (testOverlap(newValues, prevProxy)) {
                            m_pairManager.addBufferedPair(proxyId, prevProxyId);
                        }

                        ++prevProxy.upperBounds[axis];
                        ++bound.stabbingCount;
                    }
                    else {
                        ++prevProxy.lowerBounds[axis];
                        --bound.stabbingCount;
                    }

                    --proxy.lowerBounds[axis];

                    // b2Swap(*bound, *prevEdge);
                    Bound tmp = new Bound(bound);
                    bound.set(prevBound);
                    prevBound.set(tmp);
                    --index;
                }
            }

            // Should we move the upper bound up?
            if (deltaUpper > 0) {
                int index = upperIndex;
                while (index < boundCount - 1
                        && bounds[index + 1].value <= upperValue) {
                    Bound bound = bounds[index];
                    Bound nextBound = bounds[index + 1];
                    int nextProxyId = nextBound.proxyId;
                    Proxy nextProxy = m_proxyPool[nextProxyId];

                    ++nextBound.stabbingCount;

                    if (nextBound.isLower() == true) {
                        if (testOverlap(newValues, nextProxy)) {
                            m_pairManager.addBufferedPair(proxyId, nextProxyId);
                        }

                        --nextProxy.lowerBounds[axis];
                        ++bound.stabbingCount;
                    }
                    else {
                        --nextProxy.upperBounds[axis];
                        --bound.stabbingCount;
                    }

                    ++proxy.upperBounds[axis];
                    // b2Swap(*bound, *nextEdge);
                    // wasn't actually swapping! bounds[index] and
                    // bounds[index+1] need to be swapped by VALUE
                    Bound tmp = new Bound(bound);
                    bound.set(nextBound);
                    nextBound.set(tmp);
                    ++index;
                }
            }

            //
            // Shrinking removes overlaps
            //

            // Should we move the lower bound up?
            if (deltaLower > 0) {
                int index = lowerIndex;
                while (index < boundCount - 1
                        && bounds[index + 1].value <= lowerValue) {
                    Bound bound = bounds[index];
                    Bound nextBound = bounds[index + 1];

                    int nextProxyId = nextBound.proxyId;
                    Proxy nextProxy = m_proxyPool[nextProxyId];

                    --nextBound.stabbingCount;

                    if (nextBound.isUpper()) {
                        if (testOverlap(oldValues,nextProxy)) {
                            m_pairManager.removeBufferedPair(proxyId, nextProxyId);
                        }

                        --nextProxy.upperBounds[axis];
                        --bound.stabbingCount;
                    }
                    else {
                        --nextProxy.lowerBounds[axis];
                        ++bound.stabbingCount;
                    }

                    ++proxy.lowerBounds[axis];
                    // b2Swap(*bound, *nextEdge);
                    // Bound tmp = bound;
                    // bound = nextEdge;
                    // nextEdge = tmp;
                    Bound tmp = new Bound(bound);
                    bound.set(nextBound);
                    nextBound.set(tmp);
                    ++index;
                }
            }

            // Should we move the upper bound down?
            if (deltaUpper < 0) {
                int index = upperIndex;
                while (index > 0 && upperValue < bounds[index - 1].value) {
                    Bound bound = bounds[index];
                    Bound prevBound = bounds[index - 1];

                    int prevProxyId = prevBound.proxyId;
                    Proxy prevProxy = m_proxyPool[prevProxyId];

                    --prevBound.stabbingCount;

                    if (prevBound.isLower() == true) {
                        if (testOverlap(oldValues, prevProxy)) {
                            m_pairManager.removeBufferedPair(proxyId, prevProxyId);
                        }

                        ++prevProxy.lowerBounds[axis];
                        --bound.stabbingCount;
                    }
                    else {
                        ++prevProxy.upperBounds[axis];
                        ++bound.stabbingCount;
                    }

                    --proxy.upperBounds[axis];
                    // b2Swap(*bound, *prevEdge);
                    // Bound tmp = bound;
                    // bound = prevEdge;
                    // prevEdge = tmp;
                    Bound tmp = new Bound(bound);
                    bound.set(prevBound);
                    prevBound.set(tmp);
                    --index;
                }
            }
        }

        if (s_validate) {
            validate();
        }
    }
    
    public void commit() {
        m_pairManager.commit();
    }



    /**
     * Query an AABB for overlapping proxies, returns the user data and
     * the count, up to the supplied maximum count.
     */
    public Object[] query(AABB aabb, int maxCount) {
        if (debugPrint) {
            System.out.println("Query(2 args)");
        }

        int lowerValues[] = new int[2];
        int upperValues[] = new int[2];
        computeBounds(lowerValues, upperValues, aabb);

        int indexes[] = new int[2]; // lowerIndex, upperIndex;

        query(indexes, lowerValues[0], upperValues[0], m_bounds[0],
                2 * m_proxyCount, 0);
        query(indexes, lowerValues[1], upperValues[1], m_bounds[1],
                2 * m_proxyCount, 1);

        //assert m_queryResultCount < Settings.maxProxies;

        Object[] results = new Object[maxCount];

        int count = 0;
        for (int i = 0; i < m_queryResultCount && count < maxCount; ++i, ++count) {
            //assert m_queryResults[i] < Settings.maxProxies;
            Proxy proxy = m_proxyPool[m_queryResults[i]];
            proxy.isValid();
            results[i] = proxy.userData;
        }
        
        Object[] copy = new Object[count];
        System.arraycopy(results,0,copy,0,count);

        // Prepare for next query.
        m_queryResultCount = 0;
        incrementTimeStamp();

        return copy;//results;
    }

    public void validate() {
        if (debugPrint) {
            System.out.println("Validate()");
        }

        for (int axis = 0; axis < 2; ++axis) {
            Bound[] bounds = m_bounds[axis];

            int boundCount = 2 * m_proxyCount;
            int stabbingCount = 0;

            for (int i = 0; i < boundCount; ++i) {
                Bound bound = bounds[i];
                //assert(i == 0 || bounds[i-1].value <= bound.value);
                //assert(bound.proxyId != PairManager.NULL_PROXY);
                //assert(m_proxyPool[bound.proxyId].isValid());


                if (bound.isLower() == true) {
                    //assert (m_proxyPool[bound.proxyId].lowerBounds[axis] == i) : (m_proxyPool[bound.proxyId].lowerBounds[axis]
                    //        + " not " + i);
                    ++stabbingCount;
                }
                else {
                    //assert (m_proxyPool[bound.proxyId].upperBounds[axis] == i);
                    --stabbingCount;
                }

                //assert (bound.stabbingCount == stabbingCount);
            }
        }

    }


    private void computeBounds(int[] lowerValues, int[] upperValues, AABB aabb) {
        if (debugPrint) {
            System.out.println("ComputeBounds()");
        }
        //assert(aabb.upperBound.x > aabb.lowerBound.x);
        //assert(aabb.upperBound.y > aabb.lowerBound.y);
        
        Vec2 minVertex = MathUtils.clamp(aabb.lowerBound, m_worldAABB.lowerBound,
                m_worldAABB.upperBound);
        Vec2 maxVertex = MathUtils.clamp(aabb.upperBound, m_worldAABB.lowerBound,
                m_worldAABB.upperBound);

        // System.out.printf("minV = %f %f, maxV = %f %f
        // \n",aabb.minVertex.x,aabb.minVertex.y,aabb.maxVertex.x,aabb.maxVertex.y);

        // Bump lower bounds downs and upper bounds up. This ensures correct
        // sorting of
        // lower/upper bounds that would have equal values.
        // TODO_ERIN implement fast float to int conversion.
        lowerValues[0] = (int) (m_quantizationFactor.x * (minVertex.x - m_worldAABB.lowerBound.x))
                & (Integer.MAX_VALUE - 1);
        upperValues[0] = (int) (m_quantizationFactor.x * (maxVertex.x - m_worldAABB.lowerBound.x)) | 1;

        lowerValues[1] = (int) (m_quantizationFactor.y * (minVertex.y - m_worldAABB.lowerBound.y))
                & (Integer.MAX_VALUE - 1);
        upperValues[1] = (int) (m_quantizationFactor.y * (maxVertex.y - m_worldAABB.lowerBound.y)) | 1;
    }

 

    /**
     * @param results
     *            out variable
     */
    private void query(int[] results, int lowerValue, int upperValue,
            Bound[] bounds, int boundCount, int axis) {
        if (debugPrint) {
            System.out.println("Query(6 args)");
        }

        int lowerQuery = binarySearch(bounds, boundCount, lowerValue);
        int upperQuery = binarySearch(bounds, boundCount, upperValue);

        // Easy case: lowerQuery <= lowerIndex(i) < upperQuery
        // Solution: search query range for min bounds.
        for (int i = lowerQuery; i < upperQuery; ++i) {
            if (bounds[i].isLower()) {
                incrementOverlapCount(bounds[i].proxyId);
            }
        }
        // Hard case: lowerIndex(i) < lowerQuery < upperIndex(i)
        // Solution: use the stabbing count to search down the bound array.
        if (lowerQuery > 0) {
            int i = lowerQuery - 1;
            int s = bounds[i].stabbingCount;
            // Find the s overlaps.
            while (s != 0) {
                //assert (i >= 0) : ("i = " + i + "; s = " + s);
                if (bounds[i].isLower()) {
                    Proxy proxy = m_proxyPool[bounds[i].proxyId];
                    if (lowerQuery <= proxy.upperBounds[axis]) {
                        incrementOverlapCount(bounds[i].proxyId);
                        --s;
                    }
                }
                --i;
            }
        }

        results[0] = lowerQuery;
        results[1] = upperQuery;
    }

    private void incrementOverlapCount(int proxyId) {
        if (debugPrint) {
            System.out.println("IncrementOverlapCount()");
        }

        Proxy proxy = m_proxyPool[proxyId];
        if (proxy.timeStamp < m_timeStamp) {
            proxy.timeStamp = m_timeStamp;
            proxy.overlapCount = 1;
        }
        else {
            proxy.overlapCount = 2;
            //assert m_queryResultCount < Settings.maxProxies;
            m_queryResults[m_queryResultCount] = proxyId;
            ++m_queryResultCount;
        }
    }

    private void incrementTimeStamp() {
        if (debugPrint) {
            System.out.println("IncrementTimeStamp()");
        }

        if (m_timeStamp == Integer.MAX_VALUE) {
            for (int i = 0; i < Settings.maxProxies; ++i) {
                m_proxyPool[i].timeStamp = 0;
            }
            m_timeStamp = 1;
        }
        else {
            ++m_timeStamp;
        }
    }

    static int binarySearch(Bound[] bounds, int count, int value) {
        if (debugPrint) {
            System.out.println("BinarySearch()");
        }

        int low = 0;
        int high = count - 1;
        while (low <= high) {
            int mid = (low + high) >> 1;
            if (bounds[mid].value > value) {
                high = mid - 1;
            }
            else if (bounds[mid].value < value) {
                low = mid + 1;
            }
            else {
                return mid;
            }
        }

        return low;

    }

    public boolean inRange(AABB aabb) {
        Vec2 d = Vec2.max(aabb.lowerBound.sub(m_worldAABB.upperBound),
                m_worldAABB.lowerBound.sub(aabb.upperBound));
        return (Math.max(d.x, d.y) < 0.0f);
    }
}
