/*!
In each contact
<ul>
<li> m_handle1 points to trimesh.
<li> m_handle2 points to NULL.
<li> m_feature1 Is a triangle index of trimesh.
</ul>

\param trimesh
\param center
\param radius
\param contacts A GIM_CONTACT array. Must be initialized
*/
void gim_trimesh_sphere_collision(GIM_TRIMESH * trimesh,vec3f center,GREAL radius, GDYNAMIC_ARRAY * contacts)
{
    contacts->m_size = 0;

    aabb3f test_aabb;
	test_aabb.minX = center[0]-radius;
	test_aabb.maxX = center[0]+radius;
	test_aabb.minY = center[1]-radius;
	test_aabb.maxY = center[1]+radius;
	test_aabb.minZ = center[2]-radius;
	test_aabb.maxZ = center[2]+radius;

	GDYNAMIC_ARRAY collision_result;
	GIM_CREATE_BOXQUERY_LIST(collision_result);

	gim_aabbset_box_collision(&test_aabb, &trimesh->m_aabbset , &collision_result);

	if(collision_result.m_size==0)
	{
	    GIM_DYNARRAY_DESTROY(collision_result);
	}

	//collide triangles
	//Locks trimesh
	gim_trimesh_locks_work_data(trimesh);
	 //dummy contacts
    GDYNAMIC_ARRAY dummycontacts;
    GIM_CREATE_CONTACT_LIST(dummycontacts);

	int cresult;
	unsigned int i;
	GUINT32 * boxesresult = GIM_DYNARRAY_POINTER(GUINT32,collision_result);
	GIM_TRIANGLE_CONTACT_DATA tri_contact_data;
	GIM_TRIANGLE_DATA tri_data;

	for(i=0;i<collision_result.m_size;i++)
	{
		gim_trimesh_get_triangle_data(trimesh,boxesresult[i],&tri_data);
		cresult = gim_triangle_sphere_collision(&tri_data,center,radius,&tri_contact_data);
		if(cresult!=0)
		{
		    GIM_PUSH_CONTACT(dummycontacts, tri_contact_data.m_points[0],tri_contact_data.m_separating_normal ,tri_contact_data.m_penetration_depth,trimesh, 0, boxesresult[i],0);
		}
	}
	///unlocks
	gim_trimesh_unlocks_work_data(trimesh);
	///Destroy box result
	GIM_DYNARRAY_DESTROY(collision_result);

	 //merge contacts
    gim_merge_contacts(&dummycontacts,contacts);

    //Destroy dummy
    GIM_DYNARRAY_DESTROY(dummycontacts);
}
/*!

In each contact
<ul>
<li> m_handle1 points to trimesh1.
<li> m_handle2 points to trimesh2.
<li> m_feature1 Is a triangle index of trimesh1.
<li> m_feature2 Is a triangle index of trimesh2.
</ul>

\param trimesh1 Collider
\param trimesh2 Collidee
\param contacts A GIM_CONTACT array. Must be initialized
*/
void gim_trimesh_trimesh_collision(GIM_TRIMESH * trimesh1, GIM_TRIMESH * trimesh2, GDYNAMIC_ARRAY * contacts)
{
    contacts->m_size = 0;
    GDYNAMIC_ARRAY collision_pairs;
    GIM_CREATE_PAIR_SET(collision_pairs)

    gim_aabbset_bipartite_intersections(&trimesh1->m_aabbset,&trimesh2->m_aabbset,&collision_pairs);

    if(collision_pairs.m_size==0)
    {
        GIM_DYNARRAY_DESTROY(collision_pairs);
        return; //no collisioin
    }

    //Locks meshes
    gim_trimesh_locks_work_data(trimesh1);
    gim_trimesh_locks_work_data(trimesh2);


    //pair pointer
    GIM_PAIR *pairs = GIM_DYNARRAY_POINTER(GIM_PAIR,collision_pairs);
    //dummy contacts
    GDYNAMIC_ARRAY dummycontacts;
    GIM_CREATE_CONTACT_LIST(dummycontacts);

    //Auxiliary triangle data
    GIM_TRIANGLE_CONTACT_DATA tri_contact_data;
    GIM_TRIANGLE_DATA tri1data,tri2data;


    GUINT32 i, ti1,ti2,ci;
    int colresult;
    for (i=0; i<collision_pairs.m_size; i++)
    {
        ti1 = pairs[i].m_index1;
        ti2 = pairs[i].m_index2;
        //Get triangles data
        gim_trimesh_get_triangle_data(trimesh1,ti1,&tri1data);
        gim_trimesh_get_triangle_data(trimesh2,ti2,&tri2data);

        //collide triangles
        colresult = gim_triangle_triangle_collision(&tri1data,&tri2data,&tri_contact_data);
        if(colresult == 1)
        {
            //Add contacts
            for (ci=0; ci<tri_contact_data.m_point_count ; ci++ )
            {
                GIM_PUSH_CONTACT(dummycontacts, tri_contact_data.m_points[ci],tri_contact_data.m_separating_normal ,tri_contact_data.m_penetration_depth,trimesh1, trimesh2, ti1, ti2);
            }
        }
    }

    if(dummycontacts.m_size == 0) //reject
    {
        GIM_DYNARRAY_DESTROY(dummycontacts);
        GIM_DYNARRAY_DESTROY(collision_pairs);
        return;
    }
    //merge contacts
    gim_merge_contacts(&dummycontacts,contacts);

    //Terminate
    GIM_DYNARRAY_DESTROY(dummycontacts);
    GIM_DYNARRAY_DESTROY(collision_pairs);

    //Unlocks meshes
    gim_trimesh_unlocks_work_data(trimesh1);
    gim_trimesh_unlocks_work_data(trimesh2);
}
/*!

\param triangle
\param s1
\param s2
\param contacts Contains the closest points on the segment (1,2), and the normal points to segment, and m_depth contains the distance

\post The contacts array is not set to 0. It adds aditional contacts
*/
void gim_closest_point_triangle_segment(GIM_TRIANGLE_DATA * triangle, vec3f s1,vec3f s2, GDYNAMIC_ARRAY * contacts)
{
    vec3f segment_points[4];
    vec3f closest_points[2];
    GUINT intersection_type, out_edge= 10;
    GREAL dis, dis_temp,perpend;
    vec4f sdiff;

    dis = DISTANCE_PLANE_POINT(triangle->m_planes.m_planes[0],s1);
    dis_temp = DISTANCE_PLANE_POINT(triangle->m_planes.m_planes[0],s2);

    if(dis<=0.0f && dis_temp<=0.0f) return;

    VEC_DIFF(sdiff,s2,s1);
    perpend = VEC_DOT(sdiff,triangle->m_planes.m_planes[0]);

    if(!IS_ZERO(perpend)) // Not perpendicular
    {
        if(dis<dis_temp)
        {
            VEC_COPY(closest_points[0],s1);
        }
        else
        {
            dis = dis_temp;
            VEC_COPY(closest_points[0],s2);
        }

        //Testing segment vertices over triangle
        if(dis>=0.0f && dis_temp>=0.0f)
        {
            POINT_IN_HULL(closest_points[0],(&triangle->m_planes.m_planes[1]),3,out_edge);

            if(out_edge==0)//Point over face
            {
                GIM_PUSH_CONTACT((*contacts),closest_points[0] ,triangle->m_planes.m_planes[0] ,dis,0, 0, 0,0);
                return;
            }
        }
        else
        {

            PLANE_CLIP_SEGMENT(s1,s2,triangle->m_planes.m_planes[0],closest_points[1]);

            POINT_IN_HULL(closest_points[1],(&triangle->m_planes.m_planes[1]),3,out_edge);

            if(out_edge==0)//Point over face
            {
                GIM_PUSH_CONTACT((*contacts),closest_points[0] ,triangle->m_planes.m_planes[0] ,dis,0, 0, 0,0);
                return;
            }
        }

    }
    else // Perpendicular Face
    {
        //out_edge=10
        //Clip segment by triangle
    //    Edge1
        PLANE_CLIP_SEGMENT_CLOSEST(s1,s2,triangle->m_planes.m_planes[1],segment_points[0],segment_points[1],intersection_type);
        if(intersection_type==0||intersection_type==1)
        {
            out_edge = 0;
            VEC_COPY(closest_points[0],segment_points[0]);
        }
        else
        {
            //Edge2
            PLANE_CLIP_SEGMENT_CLOSEST(segment_points[0],segment_points[1],triangle->m_planes.m_planes[2],segment_points[2],segment_points[3],intersection_type);
            if(intersection_type==0||intersection_type==1)
            {
                out_edge = 1;
                VEC_COPY(closest_points[0],segment_points[3]);
            }
            else
            {
                //Edge3
                PLANE_CLIP_SEGMENT_CLOSEST(segment_points[2],segment_points[3],triangle->m_planes.m_planes[3],closest_points[0],closest_points[1],intersection_type);
                if(intersection_type==0||intersection_type==1)
                {
                    out_edge = 2;
                }
            }
        }
        //POST closest_points[0] and closest_points[1] are inside the triangle, if out_edge>2
        if(out_edge>2) // Over triangle
        {
            dis = VEC_DOT(closest_points[0],triangle->m_planes.m_planes[0]);
            GIM_PUSH_CONTACT((*contacts),closest_points[0] ,triangle->m_planes.m_planes[0] ,dis,0, 0, 0,0);
            GIM_PUSH_CONTACT((*contacts),closest_points[1] ,triangle->m_planes.m_planes[0] ,dis,0, 0, 0,0);
            return;
        }
    }

    //Find closest edges
    out_edge = 10;
    dis = G_REAL_INFINITY;
    GUINT i;
    for(i=0;i<3;i++)
    {
        SEGMENT_COLLISION(s1,s2,triangle->m_vertices[i],triangle->m_vertices[(i+1)%3],segment_points[0],segment_points[1]);
        VEC_DIFF(sdiff,segment_points[0],segment_points[1]);
        dis_temp = VEC_DOT(sdiff,sdiff);
        if(dis_temp< dis)
        {
            dis = dis_temp;
            out_edge = i;
            VEC_COPY(closest_points[0],segment_points[0]);
            VEC_COPY(closest_points[1],sdiff);//normal
        }
    }
    if(out_edge>2) return ;// ???? ASSERT this please

    if(IS_ZERO(dis))
    {
        //Set face plane
        GIM_PUSH_CONTACT((*contacts),closest_points[0] ,triangle->m_planes.m_planes[0] ,0.0f,0, 0, 0,0);

    }
    else
    {
        GIM_SQRT(dis,dis);
        VEC_SCALE(closest_points[1],(1.0f/dis),closest_points[1]);//normal
        GIM_PUSH_CONTACT((*contacts),closest_points[0] ,closest_points[1],dis,0, 0, 0,0);
    }
}