/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "scene3d/mitem.h"
		#include "binding_helpers.h"

		using namespace GS;
		using namespace GS::S3D;
		using namespace GS::Script;


//------------------------------------------------------------------------------
#define	__SQ_TEST_ITEM_PHYSIC if (!item->physic_item) return sq_throwerror(vm, "No physic interface.");

SQInteger			ItemSetSelfMask(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(item, MItem, typetag_Item)
	__SQ_GETINT(mask)
	__SQ_GETEND
	__SQ_TEST_ITEM_PHYSIC
	item->physic_item->SetSelfMask(mask);
	__SQ_RETURN
}
SQInteger			ItemCollisionActivate(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(item, MItem, typetag_Item)
	__SQ_GETBOOL(active)
	__SQ_GETEND
	__SQ_TEST_ITEM_PHYSIC
	item->physic_item->SetActive(asbool(active));
	__SQ_RETURN
}
SQInteger			ItemSetCollisionMask(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(item, MItem, typetag_Item)
	__SQ_GETINT(mask)
	__SQ_GETEND
	__SQ_TEST_ITEM_PHYSIC
	item->physic_item->SetCollisionMask(mask);
	__SQ_RETURN
}
SQInteger			ItemGetShapeFromIndex(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(item, MItem, typetag_Item)
	__SQ_GETINT(idx)
	__SQ_GETEND
	__SQ_TEST_ITEM_PHYSIC
	if	(idx < (SQInteger)item->physic_item_desc.shape_list.GetCount())
		__SQ_RETURNSAFEPTR(item->physic_item_desc.shape_list[idx], typetag_ColShape)
	__SQ_RETURNNULL
}
SQInteger			ItemAddCollisionShape(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(item, MItem, typetag_Item)
	__SQ_TEST_ITEM_PHYSIC
	PhysicShape *shape = new PhysicShape;
	if	(!shape)
		return sq_throwerror(vm, "Failed to allocate collision shape.");
	item->physic_item_desc.shape_list.Add(shape);
	__SQ_RETURNSAFEPTR(shape, typetag_ColShape)
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
SQInteger			ShapeSetMesh(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_GETSTRING(path)
	bool r = path ? shape->Set(PhysicShape::TypeMesh, path) : false;
	__SQ_GETEND
	__SQ_RETURNBOOL(r)
}
SQInteger			ShapeSetConvex(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_GETSTRING(path)
	bool r = path ? shape->Set(PhysicShape::TypeConvex, path) : false;
	__SQ_GETEND
	__SQ_RETURNBOOL(r)
}
SQInteger			ShapeSetSphere(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_GETFLOAT(radius)
	__SQ_GETEND
	__SQ_RETURNBOOL(shape->Set(PhysicShape::TypeSphere, Vector4(radius, 0, 0)))
}
SQInteger			ShapeSetBox(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_GETVECTOR(scale)
	__SQ_GETEND
	__SQ_RETURNBOOL(shape->Set(PhysicShape::TypeBox, scale))
}
SQInteger			ShapeSetCapsule(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_GETFLOAT(radius)
	__SQ_GETFLOAT(length)
	__SQ_GETEND
	__SQ_RETURNBOOL(shape->Set(PhysicShape::TypeCapsule, Vector4(radius, length, 0.0)))
}
SQInteger			ShapeSetCylinder(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_GETFLOAT(radius)
	__SQ_GETFLOAT(length)
	__SQ_GETEND
	__SQ_RETURNBOOL(shape->Set(PhysicShape::TypeCylinder, Vector4(radius, length, 0.0)))
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
SQInteger			ShapeGetItem(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_RETURNSAFEPTR(NULL, typetag_Item);
}
SQInteger			ShapeGetPosition(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_RETURNVECTOR(shape->position)
}
SQInteger			ShapeSetPosition(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_GETVECTOR(p)
	__SQ_GETEND
	shape->position = p;
	__SQ_RETURN
}
SQInteger			ShapeGetRotation(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_RETURNVECTOR(shape->rotation)
}
SQInteger			ShapeSetRotation(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_GETVECTOR(e)
	__SQ_GETEND
	shape->rotation = e;
	__SQ_RETURN
}
SQInteger			ShapeSetRestitution(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_GETFLOAT(r)
	__SQ_GETEND
	shape->restitution = r;
	__SQ_RETURN
}
SQInteger			ShapeSetFriction(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_GETFLOAT(df)
	__SQ_GETFLOAT(sf)
	__SQ_GETEND
	shape->dynamic_friction = df;
	shape->static_friction = sf;
	__SQ_RETURN
}
SQInteger			ShapeGetMass(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_RETURNFLOAT(shape->mass)
}
SQInteger			ShapeSetMass(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(shape, PhysicShape, typetag_ColShape)
	__SQ_GETFLOAT(mass)
	__SQ_GETEND
	shape->mass = mass;
	__SQ_RETURN
}
//------------------------------------------------------------------------------

//	//poly poly intersection
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Gather up one-dimensional extents of the projection of the polygon
// onto this axis.
void gatherPolygonProjectionExtents(GS::Array<GS::Vector4> poly, GS::Vector4 v, float &outMin, float &outMax)
{
  // Initialize extents to a single point, the first vertex
  outMin = outMax = v.Dot(poly[0]);

  // Now scan all the rest, growing extents to include them
  for (uint i = 1; i < poly.GetCount(); ++i) {
    float d = v.Dot(poly[i]);
    if (d < outMin)
      outMin = d;
    else if (d > outMax)
      outMax = d;
  }
}
// Helper routine: test if two convex polygons overlap, using only the edges of
// the first polygon (polygon "a") to build the list of candidate separating axes.
bool findSeparatingAxis(GS::Array<Vector4> poly_a, GS::Array<Vector4> poly_b)
{
	// Iterate over all the edges
	uint prev = poly_a.GetCount() - 1;
	for (uint cur = 0; cur < poly_a.GetCount(); ++cur)
	{
		// Get edge vector.  (Assume operator- is overloaded)
		GS::Vector4 edge = poly_a[cur] - poly_a[prev];
		edge.y = 0;
		edge.Normalize();

		// Rotate vector 90 degrees (doesn't matter which way) to get
		// candidate separating axis.
		GS::Vector4 v(edge.z, 0, -edge.x);

		// Gather extents of both polygons projected onto this axis
		float result_poly_a_min, result_poly_a_max;
		gatherPolygonProjectionExtents(poly_a, v, result_poly_a_min, result_poly_a_max);
		float result_poly_b_min, result_poly_b_max;
		gatherPolygonProjectionExtents(poly_b, v, result_poly_b_min, result_poly_b_max);

		// Is this a separating axis?
		if (result_poly_a_max < result_poly_b_min) return true;
		if (result_poly_b_max < result_poly_a_min) return true;

			// Next edge, please
		prev = cur;
	}

	// Failed to find a separating axis
	return false;
}

// Here is our high level entry point.  It tests whether two polygons intersect.  The
// polygons must be convex, and they must not be degenerate.
bool convexPolygonOverlap(GS::Array<GS::Vector4> poly_a, GS::Array<Vector4> poly_b) 	//poly[a, b, c, d]
{
	// First, use all of A's edges to get candidate separating axes
	if (findSeparatingAxis(poly_a, poly_b))
		return false;

	// Now swap roles, and use B's edges
	if (findSeparatingAxis(poly_b, poly_a))
		return false;

	// No separating axis found.  They must overlap
	return true;
}

SQInteger			convexPolygonOverlapBind(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)

	GS::Array<GS::Vector4> poly_a(4);
	sq_pushnull(vm);//null iterator
	for (int i = 0; i < 4; ++i)
	{
		sq_next(vm, __SQ_STACKPOS - 1);
		GetVector(vm, -1, poly_a[i]);
		sq_pop(vm, 2);
	}
	sq_pop(vm, 1); //pops the null iterator

	__SQ_GETUPDATESTACK
	GS::Array<GS::Vector4> poly_b(4);
	sq_pushnull(vm);//null iterator
	for (int i = 0; i < 4; ++i)
	{
		sq_next(vm, __SQ_STACKPOS - 1);
		GetVector(vm, -1, poly_b[i]);
		sq_pop(vm, 2);
	}
	sq_pop(vm, 1); //pops the null iterator

	__SQ_GETEND

		__SQ_RETURNBOOL(convexPolygonOverlap(poly_a, poly_b));
}

//------------------------------------------------------------
bool	PointInPoly2D(GS::Vector4 point, GS::Array<GS::Vector4> poly)		// poly = [Vector(), Vector(), Vector(), Vector()]
//------------------------------------------------------------
{
  bool oddNodes = false;
  float x2 = poly[3].x;
  float z2 = poly[3].z;
  float x1, z1;

  // vertex a
  x1 = poly[0].x;
  z1 = poly[0].z;
  if (((z1 < point.z) && (z2 >= point.z)) || (z1 >= point.z) && (z2 < point.z)) {
    if ((point.z - z1) / (z2 - z1) * (x2 - x1) < (point.x - x1))
      oddNodes = !oddNodes;
  }

  x2 = x1;
  z2 = z1;

  // vertex b
  x1 = poly[1].x;
  z1 = poly[1].z;
  if (((z1 < point.z) && (z2 >= point.z)) || (z1 >= point.z) && (z2 < point.z)) {
    if ((point.z - z1) / (z2 - z1) * (x2 - x1) < (point.x - x1))
      oddNodes = !oddNodes;
  }

  x2 = x1;
  z2 = z1;

  // vertex c
  x1 = poly[2].x;
  z1 = poly[2].z;
  if (((z1 < point.z) && (z2 >= point.z)) || (z1 >= point.z) && (z2 < point.z)) {
    if ((point.z - z1) / (z2 - z1) * (x2 - x1) < (point.x - x1))
      oddNodes = !oddNodes;
  }

  x2 = x1;
  z2 = z1;

  // vertex d
  x1 = poly[3].x;
  z1 = poly[3].z;
  if (((z1 < point.z) && (z2 >= point.z)) || (z1 >= point.z) && (z2 < point.z)) {
    if ((point.z - z1) / (z2 - z1) * (x2 - x1) < (point.x - x1))
      oddNodes = !oddNodes;
  }

  return oddNodes;
}

SQInteger			PointInPoly2DBind(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)

		__SQ_GETVECTOR(p);

	GS::Array<GS::Vector4> poly_a(4);
	sq_pushnull(vm);//null iterator
	for (int i = 0; i < 4; ++i)
	{
		sq_next(vm, __SQ_STACKPOS - 1);
		GetVector(vm, -1, poly_a[i]);
		sq_pop(vm, 2);
	}
	sq_pop(vm, 1); //pops the null iterator

	__SQ_GETUPDATESTACK
	__SQ_GETEND
	
	__SQ_RETURNBOOL(PointInPoly2D(p, poly_a));
}


//----------------------------------------------------------
void				RegisterCollisionBinding(HSQUIRRELVM vm)
//----------------------------------------------------------
{
	/*#
	Func: convexPolygonOverlapBind
	Proto: void:array polyA, array polyB
	Desc: return if the 2 poly overlap
	#*/
	sq_register(vm, convexPolygonOverlapBind, "convexPolygonOverlapBind", _SC(".aa"));
	/*#
	Func: convexPolygonOverlapBind
	Proto: void:Vector p, array polyA
	Desc: return if the 2 poly overlap
	#*/
	sq_register(vm, PointInPoly2DBind, "PointInPoly2DBind", _SC(".xa"));
/*#
	Topic: Collision
	Type: ColShape
#*/
/*#
	Section: CollisionShapeType
	Desc: Collision Shape Type functions
#*/
	/*#
		Func: ShapeSetMesh
		Proto: bool:ColShape shape,string path
		Desc: Set the shape as a collision mesh.
	#*/
	sq_register(vm, ShapeSetMesh, "ShapeSetMesh", _SC(".xs"));
	/*#
		Func: ShapeSetConvex
		Proto: bool:ColShape shape,string path
		Desc: Set the shape as a convex collision.
	#*/
	sq_register(vm, ShapeSetConvex, "ShapeSetConvex", _SC(".xs"));
	/*#
		Func: ShapeSetSphere
		Proto: bool:ColShape shape,float radius
		Desc: Set the shape as a sphere.
	#*/
	sq_register(vm, ShapeSetSphere, "ShapeSetSphere", _SC(".xn"));
	/*#
		Func: ShapeSetBox
		Proto: bool:ColShape shape,Vector dimensions
		Desc: Set the shape as a box collision shape.
	#*/
	sq_register(vm, ShapeSetBox, "ShapeSetBox", _SC(".xx"));
	/*#
		Func: ShapeSetCapsule
		Proto: bool:ColShape shape,float radius,float length
		Desc: Set the shape as a Z-oriented capsule collision shape.
	#*/
	sq_register(vm, ShapeSetCapsule, "ShapeSetCapsule", _SC(".xnn"));
	/*#
		Func: ShapeSetCylinder
		Proto: bool:ColShape shape,float radius,float length
		Desc: Set the shape as a Z-oriented cylinder collision shape.
	#*/
	sq_register(vm, ShapeSetCylinder, "ShapeSetCylinder", _SC(".xnn"));

/*#
	Section: CollisionShape
	Desc: Collision Shape functions
#*/
	/*#
		Func: ShapeSetPosition
		Proto: void:ColShape shape,Vector position
		Desc: Set the collision shape position in item space.
	#*/
	sq_register(vm, ShapeSetPosition, "ShapeSetPosition", _SC(".xx"));
	/*#
		Func: ShapeGetPosition
		Proto: Vector:ColShape shape
		Desc: Get the collision shape position in item space.
	#*/
	sq_register(vm, ShapeGetPosition, "ShapeGetPosition", _SC(".x"));
	/*#
		Func: ShapeSetRotation
		Proto: void:ColShape shape,Vector position
		Desc: Set the collision shape position from a Euler triplet in item space.
	#*/
	sq_register(vm, ShapeSetRotation, "ShapeSetRotation", _SC(".xx"));
	/*#
		Func: ShapeGetRotation
		Proto: Vector:ColShape shape
		Desc: Get the collision shape rotation as an Euler triplet in item space.
	#*/
	sq_register(vm, ShapeGetRotation, "ShapeGetRotation", _SC(".x"));

	/*#
		Func: ShapeSetMass
		Proto: void:ColShape shape,float mass
		Desc: Set the collision shape mass. Do not forget to update the item collision setup to update changes.
	#*/
	sq_register(vm, ShapeSetMass, "ShapeSetMass", _SC(".xn"));
	/*#
		Func: ShapeGetMass
		Proto: float:ColShape shape
		Desc: Get the collision shape mass.
	#*/
	sq_register(vm, ShapeGetMass, "ShapeGetMass", _SC(".x"));
	/*#
		Func: ShapeSetFriction
		Proto: void:ColShape shape,float dynamic_friction,float static_friction
		Desc: Set collision shape dynamic and static friction.
	#*/
	sq_register(vm, ShapeSetFriction, "ShapeSetFriction", _SC(".xnn"));
	/*#
		Func: ShapeSetRestitution
		Proto: void:ColShape shape,float restitution
		Desc: Set collision shape restitution (1 for a perfect elastic collision).
	#*/
	sq_register(vm, ShapeSetRestitution, "ShapeSetRestitution", _SC(".xn"));
	/*#
		Func: ShapeGetItem
		Proto: Item:ColShape shape
		Desc: Get the item this collision shape belongs to.
	#*/
	sq_register(vm, ShapeGetItem, "ShapeGetItem", _SC(".x"));

/*#
	Section: CollisionItem
	Desc: Item functions
#*/
	/*#
		Func: ItemAddCollisionShape
		Proto: ColShape:Item
		Desc: Add a new collision shape to item.
	#*/
	sq_register(vm, ItemAddCollisionShape, "ItemAddCollisionShape", _SC(".x"));
	/*#
		Func: ItemCollisionActivate
		Proto: void:Item,bool active
		Desc: Activate or deactivate item collision.
	#*/
	sq_register(vm, ItemCollisionActivate, "ItemCollisionActivate", _SC(".xb"));
	/*#
		Func: ItemSetSelfMask
		Proto: void:Item,int self_mask
		Desc: Set item self mask, this mask is a bit field.
	#*/
	sq_register(vm, ItemSetSelfMask, "ItemSetSelfMask", _SC(".xi"));
	/*#
		Func: ItemSetCollisionMask
		Proto: void:Item,int collision_mask
		Desc: Set item collision mask, this mask is a bit field.
	#*/
	sq_register(vm, ItemSetCollisionMask, "ItemSetCollisionMask", _SC(".xi"));
	/*#
		Func: ItemGetShapeFromIndex
		Proto: ColShape:item,int index
		Desc: Get item collision shape from index.
	#*/
	sq_register(vm, ItemGetShapeFromIndex, "ItemGetShapeFromIndex", _SC(".xi"));

	// Push defines.
	sq_pushroottable(vm);

	/*#
		Enum: ShapeMask
		Desc: Shape mask to filter out certain type of shape from intersection tests.
		Values: CollisionTraceMesh,CollisionTraceSphere,CollisionTraceCuboid,CollisionTraceAll
	#*/
	sq_pushstring(vm, "CollisionTraceMesh", -1); sq_pushinteger(vm, -1); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "CollisionTraceSphere", -1); sq_pushinteger(vm, -1); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "CollisionTraceCuboid", -1); sq_pushinteger(vm, -1); sq_newslot(vm, -3, true);
	sq_pushstring(vm, "CollisionTraceAll", -1); sq_pushinteger(vm, ~0); sq_newslot(vm, -3, true);

	sq_pop(vm, 1);
}
