/*
* 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.Contacts{


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


//typedef b2Contact* b2ContactCreateFcn(b2Shape* shape1, b2Shape* shape2, b2BlockAllocator* allocator);
//typedef void b2ContactDestroyFcn(b2Contact* contact, b2BlockAllocator* allocator);



public class b2Contact
{
	public virtual function GetManifolds():Array{return null};
	
	/// Get the number of manifolds. This is 0 or 1 between convex shapes.
	/// This may be greater than 1 for convex-vs-concave shapes. Each
	/// manifold holds up to two contact points with a shared contact normal.
	public function GetManifoldCount():int
	{
		return m_manifoldCount;
	}
	
	/// Is this contact solid?
	/// @return true if this contact should generate a response.
	public function IsSolid():Boolean{
		return (m_flags & e_nonSolidFlag) == 0;
	}
	
	/// Get the next contact in the world's contact list.
	public function GetNext():b2Contact{
		return m_next;
	}
	
	/// Get the first shape in this contact.
	public function GetShape1():b2Shape{
		return m_shape1;
	}
	
	/// Get the second shape in this contact.
	public function GetShape2():b2Shape{
		return m_shape2;
	}

	//--------------- Internals Below -------------------
	
	// m_flags
	// enum
	static public var e_nonSolidFlag:uint	= 0x0001;
	static public var e_slowFlag:uint		= 0x0002;
	static public var e_islandFlag:uint		= 0x0004;
	static public var e_toiFlag:uint		= 0x0008;

	static public function AddType(createFcn:Function, destroyFcn:Function, type1:int, type2:int) : void
	{
		//b2Settings.b2Assert(b2Shape.e_unknownShape < type1 && type1 < b2Shape.e_shapeTypeCount);
		//b2Settings.b2Assert(b2Shape.e_unknownShape < type2 && type2 < b2Shape.e_shapeTypeCount);
		
		s_registers[type1][type2].createFcn = createFcn;
		s_registers[type1][type2].destroyFcn = destroyFcn;
		s_registers[type1][type2].primary = true;
		
		if (type1 != type2)
		{
			s_registers[type2][type1].createFcn = createFcn;
			s_registers[type2][type1].destroyFcn = destroyFcn;
			s_registers[type2][type1].primary = false;
		}
	}
	static public function InitializeRegisters() : void{
		s_registers = new Array(b2Shape.e_shapeTypeCount);
		for (var i:int = 0; i < b2Shape.e_shapeTypeCount; i++){
			s_registers[i] = new Array(b2Shape.e_shapeTypeCount);
			for (var j:int = 0; j < b2Shape.e_shapeTypeCount; j++){
				s_registers[i][j] = new b2ContactRegister();
			}
		}
		
		AddType(b2CircleContact.Create, b2CircleContact.Destroy, b2Shape.e_circleShape, b2Shape.e_circleShape);
		AddType(b2PolyAndCircleContact.Create, b2PolyAndCircleContact.Destroy, b2Shape.e_polygonShape, b2Shape.e_circleShape);
		AddType(b2PolygonContact.Create, b2PolygonContact.Destroy, b2Shape.e_polygonShape, b2Shape.e_polygonShape);
		
	}
	static public function Create(shape1:b2Shape, shape2:b2Shape, allocator:*):b2Contact{
		if (s_initialized == false)
		{
			InitializeRegisters();
			s_initialized = true;
		}
		
		var type1:int = shape1.m_type;
		var type2:int = shape2.m_type;
		
		//b2Settings.b2Assert(b2Shape.e_unknownShape < type1 && type1 < b2Shape.e_shapeTypeCount);
		//b2Settings.b2Assert(b2Shape.e_unknownShape < type2 && type2 < b2Shape.e_shapeTypeCount);
		
		var reg:b2ContactRegister = s_registers[type1][type2];
		var createFcn:Function = reg.createFcn;
		if (createFcn != null)
		{
			if (reg.primary)
			{
				return createFcn(shape1, shape2, allocator);
			}
			else
			{
				var c:b2Contact = createFcn(shape2, shape1, allocator);
				for (var i:int = 0; i < c.m_manifoldCount; ++i)
				{
					var m:b2Manifold = c.GetManifolds()[ i ];
					m.normal = m.normal.Negative();
				}
				return c;
			}
		}
		else
		{
			return null;
		}
	}
	static public function Destroy(contact:b2Contact, allocator:*) : void{
		//b2Settings.b2Assert(s_initialized == true);
		
		if (contact.m_manifoldCount > 0)
		{
			contact.m_shape1.m_body.WakeUp();
			contact.m_shape2.m_body.WakeUp();
		}
		
		var type1:int = contact.m_shape1.m_type;
		var type2:int = contact.m_shape2.m_type;
		
		//b2Settings.b2Assert(b2Shape.e_unknownShape < type1 && type1 < b2Shape.e_shapeTypeCount);
		//b2Settings.b2Assert(b2Shape.e_unknownShape < type2 && type2 < b2Shape.e_shapeTypeCount);
		
		var reg:b2ContactRegister = s_registers[type1][type2];
		var destroyFcn:Function = reg.destroyFcn;
		destroyFcn(contact, allocator);
	}

	public function b2Contact(s1:b2Shape=null, s2:b2Shape=null)
	{
		m_flags = 0;
		
		if (!s1 || !s2){
			m_shape1 = null;
			m_shape2 = null;
			return;
		}
		
		if (s1.IsSensor() || s2.IsSensor())
		{
			m_flags |= e_nonSolidFlag;
		}
		
		m_shape1 = s1;
		m_shape2 = s2;
		
		m_manifoldCount = 0;
		
		m_friction = Math.sqrt(m_shape1.m_friction * m_shape2.m_friction);
		m_restitution = b2Math.b2Max(m_shape1.m_restitution, m_shape2.m_restitution);
		
		m_prev = null;
		m_next = null;
		
		m_node1.contact = null;
		m_node1.prev = null;
		m_node1.next = null;
		m_node1.other = null;
		
		m_node2.contact = null;
		m_node2.prev = null;
		m_node2.next = null;
		m_node2.other = null;
	}
	
	public function Update(listener:b2ContactListener) : void
	{
		var oldCount:int = m_manifoldCount;
		
		Evaluate(listener);
		
		var newCount:int = m_manifoldCount;
		
		var body1:b2Body = m_shape1.m_body;
		var body2:b2Body = m_shape2.m_body;
		
		if (newCount == 0 && oldCount > 0)
		{
			body1.WakeUp();
			body2.WakeUp();
		}
		
		// Slow contacts don't generate TOI events.
		if (body1.IsStatic() || body1.IsBullet() || body2.IsStatic() || body2.IsBullet())
		{
			m_flags &= ~e_slowFlag;
		}
		else
		{
			m_flags |= e_slowFlag;
		}
	}

	//virtual ~b2Contact() {}

	public virtual function Evaluate(listener:b2ContactListener) : void{};
	static public var s_registers:Array; //[][]
	static public var s_initialized:Boolean = false;

	public var m_flags:uint;

	// World pool and list pointers.
	public var m_prev:b2Contact;
	public var m_next:b2Contact;

	// Nodes for connecting bodies.
	public var m_node1:b2ContactEdge = new b2ContactEdge();
	public var m_node2:b2ContactEdge = new b2ContactEdge();

	public var m_shape1:b2Shape;
	public var m_shape2:b2Shape;

	public var m_manifoldCount:int;

	// Combined friction
	public var m_friction:Number;
	public var m_restitution:Number;
	
	public var m_toi:Number;

};


}
