/*
* Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
*
* 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 classes.Box2D.Dynamics{


import classes.Box2D.Collision.*;
import classes.Box2D.Collision.Shapes.*;
import classes.Box2D.Dynamics.Contacts.*;
import classes.Box2D.Dynamics.*;
import classes.Box2D.Common.Math.*;
import classes.Box2D.Common.*;


// Delegate of b2World.
public class b2ContactManager extends b2PairCallback
{
	public function b2ContactManager() {
		m_world = null;
		m_destroyImmediate = false;
	};

	// This is a callback from the broadphase when two AABB proxies begin
	// to overlap. We create a b2Contact to manage the narrow phase.
	public override function PairAdded(proxyUserData1:*, proxyUserData2:*):*{
		var shape1:b2Shape = proxyUserData1 as b2Shape;
		var shape2:b2Shape = proxyUserData2 as b2Shape;
		
		var body1:b2Body = shape1.m_body;
		var body2:b2Body = shape2.m_body;
		
		if (body1.IsStatic() && body2.IsStatic())
		{
			return m_nullContact;
		}
		
		if (shape1.m_body == shape2.m_body)
		{
			return m_nullContact;
		}
		
		if (body2.IsConnected(body1))
		{
			return m_nullContact;
		}
		
		if (m_world.m_contactFilter != null && m_world.m_contactFilter.ShouldCollide(shape1, shape2) == false)
		{
			return m_nullContact;
		}
		
		// Call the factory.
		var c:b2Contact = b2Contact.Create(shape1, shape2, m_world.m_blockAllocator);
		
		if (c == null)
		{
			return m_nullContact;
		}
		
		// Contact creation may swap shapes.
		shape1 = c.m_shape1;
		shape2 = c.m_shape2;
		body1 = shape1.m_body;
		body2 = shape2.m_body;
		
		// Insert into the world.
		c.m_prev = null;
		c.m_next = m_world.m_contactList;
		if (m_world.m_contactList != null)
		{
			m_world.m_contactList.m_prev = c;
		}
		m_world.m_contactList = c;
		
		
		// Connect to island graph.
		
		// Connect to body 1
		c.m_node1.contact = c;
		c.m_node1.other = body2;
		
		c.m_node1.prev = null;
		c.m_node1.next = body1.m_contactList;
		if (body1.m_contactList != null)
		{
			body1.m_contactList.prev = c.m_node1;
		}
		body1.m_contactList = c.m_node1;
		
		// Connect to body 2
		c.m_node2.contact = c;
		c.m_node2.other = body1;
		
		c.m_node2.prev = null;
		c.m_node2.next = body2.m_contactList;
		if (body2.m_contactList != null)
		{
			body2.m_contactList.prev = c.m_node2;
		}
		body2.m_contactList = c.m_node2;
		
		++m_world.m_contactCount;
		return c;
		
	}

	// This is a callback from the broadphase when two AABB proxies cease
	// to overlap. We retire the b2Contact.
	public override function PairRemoved(proxyUserData1:*, proxyUserData2:*, pairUserData:*): void{
		
		if (pairUserData == null)
		{
			return;
		}
		
		var c:b2Contact = pairUserData as b2Contact;
		if (c == m_nullContact)
		{
			return;
		}
		
		// An attached body is being destroyed, we must destroy this contact
		// immediately to avoid orphaned shape pointers.
		Destroy(c);
	}
	
	static private const s_evalCP:b2ContactPoint = new b2ContactPoint();
	public function Destroy(c:b2Contact) : void
	{
		
		var shape1:b2Shape = c.m_shape1;
		var shape2:b2Shape = c.m_shape2;
		
		// Inform the user that this contact is ending.
		var manifoldCount:int = c.m_manifoldCount;
		if (manifoldCount > 0 && m_world.m_contactListener)
		{
			var b1:b2Body = shape1.m_body;
			var b2:b2Body = shape2.m_body;

			var manifolds:Array  = c.GetManifolds();
			var cp:b2ContactPoint = s_evalCP;
			cp.shape1 = c.m_shape1;
			cp.shape2 = c.m_shape2;
			cp.friction = c.m_friction;
			cp.restitution = c.m_restitution;
			
			for (var i:int = 0; i < manifoldCount; ++i)
			{
				var manifold:b2Manifold = manifolds[ i ];
				cp.normal.SetV(manifold.normal);
				
				for (var j:int = 0; j < manifold.pointCount; ++j)
				{
					var mp:b2ManifoldPoint = manifold.points[j];
					cp.position = b1.GetWorldPoint(mp.localPoint1);
					var v1:b2Vec2 = b1.GetLinearVelocityFromLocalPoint(mp.localPoint1);
					var v2:b2Vec2 = b2.GetLinearVelocityFromLocalPoint(mp.localPoint2);
					cp.velocity.Set(v2.x - v1.x, v2.y - v1.y);
					cp.separation = mp.separation;
					cp.id.key = mp.id._key;
					m_world.m_contactListener.Remove(cp);
				}
			}
		}
		
		// Remove from the world.
		if (c.m_prev)
		{
			c.m_prev.m_next = c.m_next;
		}
		
		if (c.m_next)
		{
			c.m_next.m_prev = c.m_prev;
		}
		
		if (c == m_world.m_contactList)
		{
			m_world.m_contactList = c.m_next;
		}
		
		var body1:b2Body = shape1.m_body;
		var body2:b2Body = shape2.m_body;
		
		// Remove from body 1
		if (c.m_node1.prev)
		{
			c.m_node1.prev.next = c.m_node1.next;
		}
		
		if (c.m_node1.next)
		{
			c.m_node1.next.prev = c.m_node1.prev;
		}
		
		if (c.m_node1 == body1.m_contactList)
		{
			body1.m_contactList = c.m_node1.next;
		}
		
		// Remove from body 2
		if (c.m_node2.prev)
		{
			c.m_node2.prev.next = c.m_node2.next;
		}
		
		if (c.m_node2.next)
		{
			c.m_node2.next.prev = c.m_node2.prev;
		}
		
		if (c.m_node2 == body2.m_contactList)
		{
			body2.m_contactList = c.m_node2.next;
		}
		
		// Call the factory.
		b2Contact.Destroy(c, m_world.m_blockAllocator);
		--m_world.m_contactCount;
	}
	

	// This is the top level collision call for the time step. Here
	// all the narrow phase collision is processed for the world
	// contact list.
	public function Collide() : void
	{
		// Update awake contacts.
		for (var c:b2Contact = m_world.m_contactList; c; c = c.m_next)
		{
			var body1:b2Body = c.m_shape1.m_body;
			var body2:b2Body = c.m_shape2.m_body;
			if (body1.IsSleeping() && body2.IsSleeping())
			{
				continue;
			}
			
			c.Update(m_world.m_contactListener);
		}
	}

	public var m_world:b2World;

	// This lets us provide broadphase proxy pair user data for
	// contacts that shouldn't exist.
	public var m_nullContact:b2NullContact = new b2NullContact();
	public var m_destroyImmediate:Boolean;
	
};

}
