/*
 * 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;

import org.jbox2d.common.*;

//Updated to rev 139 of b2CollideCircle.cpp

/**
 * Circle/circle and circle/polygon overlap solver -
 * for internal use only.
 */
public class CollideCircle {

    public static void collideCircles(Manifold manifold, 
    		CircleShape circle1, XForm xf1,
            CircleShape circle2, XForm xf2) {
        manifold.pointCount = 0;

    	Vec2 p1 = XForm.mul(xf1, circle1.getLocalPosition());
    	Vec2 p2 = XForm.mul(xf2, circle2.getLocalPosition());

    	Vec2 d = p2.sub(p1);
        
    	float distSqr = Vec2.dot(d, d);

    	float r1 = circle1.getRadius();
    	float r2 = circle2.getRadius();
    	float radiusSum = r1+r2;
        if (distSqr > radiusSum * radiusSum) {
            return;
        }

        float separation;
        if (distSqr < Settings.EPSILON) {
            separation = -radiusSum;
            manifold.normal.set(0.0f, 1.0f);
        }
        else {
            float dist = (float) Math.sqrt(distSqr);
            separation = dist - radiusSum;
            float a = 1.0f / dist;
            manifold.normal.x = a * d.x;
            manifold.normal.y = a * d.y;
        }

        manifold.pointCount = 1;
        //manifold.points[0].id.key = 0;
        manifold.points[0].id.zero(); //use this instead of zeroing through key
        manifold.points[0].separation = separation;

    	p1.addLocal(manifold.normal.mul(r1));
    	p2.subLocal(manifold.normal.mul(r2));

    	Vec2 p = new Vec2(0.5f * (p1.x + p2.x), 0.5f * (p1.y + p2.y));

    	manifold.points[0].localPoint1 = XForm.mulT(xf1, p);
    	manifold.points[0].localPoint2 = XForm.mulT(xf2, p);

    }

    public static void collidePolygonAndCircle(Manifold manifold, 
    		PolygonShape polygon, XForm xf1,
            CircleShape circle, XForm xf2) {
    	
        manifold.pointCount = 0;

        // Compute circle position in the frame of the polygon.
        Vec2 c = XForm.mul(xf2, circle.getLocalPosition());
    	Vec2 cLocal = XForm.mulT(xf1, c);

        // Find edge with maximum separation.
        int normalIndex = 0;
        float separation = -Float.MAX_VALUE;
        float radius = circle.getRadius();
        int vertexCount = polygon.getVertexCount();
        Vec2[] vertices = polygon.getVertices();
        Vec2[] normals = polygon.getNormals();
        for (int i = 0; i < vertexCount; ++i) {

            float s = Vec2.dot(normals[i], cLocal.sub(vertices[i]));
            if (s > circle.m_radius) {
                // Early out.
                return;
            }

            if (s > separation) {
                normalIndex = i;
                separation = s;
            }
        }
        // If the center is inside the polygon ...
        if (separation < Settings.EPSILON) {
            manifold.pointCount = 1;
            manifold.normal = Mat22.mul(xf1.R, normals[normalIndex]);
            manifold.points[0].id.features.incidentEdge = normalIndex;
            manifold.points[0].id.features.incidentVertex = Collision.NULL_FEATURE;
            manifold.points[0].id.features.referenceEdge = 0;
            manifold.points[0].id.features.flip = 0;
    		Vec2 position = c.sub(manifold.normal.mul(radius));
    		manifold.points[0].localPoint1 = XForm.mulT(xf1, position);
    		manifold.points[0].localPoint2 = XForm.mulT(xf2, position);
            manifold.points[0].separation = separation - radius;
            return;
        }

        // Project the circle center onto the edge segment.
        int vertIndex1 = normalIndex;
        int vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0;
        Vec2 e = vertices[vertIndex2].sub(vertices[vertIndex1]);
        float length = e.normalize();
        //assert(length > Settings.EPSILON);

        // Project the center onto the edge.
        float u = Vec2.dot(cLocal.sub(vertices[vertIndex1]), e);

        Vec2 p = new Vec2();
        if (u <= 0.0f) {
            p.set(vertices[vertIndex1]);
            manifold.points[0].id.features.incidentEdge = Collision.NULL_FEATURE;
            manifold.points[0].id.features.incidentVertex = vertIndex1;
        }
        else if (u >= length) {
            p.set(vertices[vertIndex2]);
            manifold.points[0].id.features.incidentEdge = Collision.NULL_FEATURE;
            manifold.points[0].id.features.incidentVertex = vertIndex2;
        }
        else {
            p.set(vertices[vertIndex1]);
            p.x += u * e.x;
            p.y += u * e.y;
            manifold.points[0].id.features.incidentEdge = normalIndex;
            manifold.points[0].id.features.incidentVertex = 0;
        }

        Vec2 d = cLocal.sub(p);
        float dist = d.normalize();
        if (dist > radius) {
            return;
        }

        manifold.pointCount = 1;
        
    	manifold.normal = Mat22.mul(xf1.R, d);
    	Vec2 position = c.sub(manifold.normal.mul(radius));
    	manifold.points[0].localPoint1 = XForm.mulT(xf1, position);
    	manifold.points[0].localPoint2 = XForm.mulT(xf2, position);
        manifold.points[0].separation = dist - radius;
        manifold.points[0].id.features.referenceEdge = 0;
        manifold.points[0].id.features.flip = 0;
    }
}
