/*
 * Copyright (c) 2008-2009 Bill Whitacre http://rampancy.g0dsoft.com
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
*/

#include "hull_polygon.h"

static rat_hull *polygon_create(vector2 pos,unsigned int numverts,vector2 *verts)
{
	register unsigned int i;

	rat_hull_polygon *hull=rat_alloc(sizeof(rat_hull_polygon));
	memset(hull,0,sizeof(rat_hull_polygon));

	rat_init_hull_classes();

	hull->type=rat_hull_type_polygon;
	hull->clas=&rat_polygon_class;

	hull->userdata=NULL;
	hull->numverts=numverts;

	hull->pos=pos;
	hull->angle=0;

	hull->verts=(vector2 *)rat_alloc(sizeof(vector2)*numverts);
	hull->world_verts=(vector2 *)rat_alloc(sizeof(vector2)*numverts);
	hull->axes=(rat_polygon_axis *)rat_alloc(sizeof(rat_polygon_axis)*numverts);
	hull->world_axes=(rat_polygon_axis *)rat_alloc(sizeof(rat_polygon_axis)*numverts);

	memcpy(hull->verts,verts,sizeof(vector2)*numverts);
	for (i=0; i<numverts; i++)
	{
		hull->axes[i].normal=vector2_getnormalized(vector2_perp_left(
			vector2_sub(hull->verts[(i+1)%numverts],hull->verts[i])));
		hull->axes[i].dist=vector2_dot(hull->axes[i].normal,hull->verts[i]);
	}

	rat_hull_polygon_update_cache((rat_hull *)hull,0);
	return (rat_hull *)hull;
}

rat_hull *rat_hull_polygon_create(vector2 pos,unsigned int numverts,vector2 *verts)
{
	return polygon_create(pos,numverts,verts);
}

rat_hull *rat_hull_polygon_create_box(vector2 pos,vector2 extent)
{
	rat_hull_polygon *box;
	vector2 boxverts[4];

	boxverts[0]=vector2_negative(extent);
	boxverts[1].x=-extent.x;
	boxverts[1].y=extent.y;
	boxverts[2]=extent;
	boxverts[3].x=extent.x;
	boxverts[3].y=-extent.y;

	box=(rat_hull_polygon *)polygon_create(pos,4,boxverts);
	return (rat_hull *)box;
}

void rat_hull_polygon_destroy(rat_hull *hull)
{
	rat_dealloc((void *)((rat_hull_polygon *)hull)->verts);
	rat_dealloc((void *)((rat_hull_polygon *)hull)->axes);
	rat_dealloc((void *)((rat_hull_polygon *)hull)->world_verts);
	rat_dealloc((void *)((rat_hull_polygon *)hull)->world_axes);
}

void rat_hull_polygon_update_cache(rat_hull *hull,int is_static)
{
	if (!is_static)
	{
		register unsigned int i;
		aabb temp_bb;
		rat_hull_polygon *polygon=(rat_hull_polygon *)hull;
		rat_real pos_buf[2]={polygon->pos.x,polygon->pos.y};

		rat_real *temp_buf=(rat_real *)rat_alloc(sizeof(vector2)*polygon->numverts);

		register rat_real cosine=cos(polygon->angle);
		register rat_real sine=sin(polygon->angle);

		rotate_points_with_precalc_sine2(cosine,sine,(rat_real *)polygon->verts,temp_buf,polygon->numverts);
		translate_points2(pos_buf,temp_buf,(rat_real *)polygon->world_verts,polygon->numverts);

		rat_dealloc((void *)temp_buf);

		for (i=0; i<polygon->numverts; i++)
		{
			polygon->world_axes[i].normal.x=
				polygon->axes[i].normal.x*cosine-
				polygon->axes[i].normal.y*sine;
			polygon->world_axes[i].normal.y=
				polygon->axes[i].normal.y*cosine+
				polygon->axes[i].normal.x*sine;
			polygon->world_axes[i].dist=vector2_dot(polygon->world_axes[i].normal,polygon->world_verts[i]);
		}

		rat_points_bounding_box(polygon->world_verts,polygon->numverts,&(polygon->cached_bb));
	}
}

int rat_hull_polygon_get_verts(rat_hull *hull,vector2 **verts)
{
	*verts=((rat_hull_polygon *)hull)->verts;
	return ((rat_hull_polygon *)hull)->numverts;
}

rat_real rat_hull_polygon_get_angle(rat_hull *hull)	{return ((rat_hull_polygon *)hull)->angle;}
vector2 rat_hull_polygon_get_pos(rat_hull *hull)	{return ((rat_hull_polygon *)hull)->pos;}

void rat_hull_polygon_set_angle(rat_hull *hull,rat_real angle)
{
	((rat_hull_polygon *)hull)->angle=angle;
}

void rat_hull_polygon_set_pos(rat_hull *hull,vector2 pos)
{
	((rat_hull_polygon *)hull)->pos=pos;
}

rat_real rat_polygon_point_dist(vector2 *verts,rat_polygon_axis *axes,unsigned int numverts,vector2 point)
{
	register unsigned int i;
	register rat_real dist,min_dist;

	min_dist=vector2_dot(axes[0].normal,point)-axes[0].dist;
	for (i=1; i<numverts; i++)
	{
		dist=vector2_dot(axes[i].normal,point)-axes[i].dist;
		if (dist>0.0) return 0;
		min_dist=dist>min_dist?dist:min_dist;
	}

	return min_dist;
}

int rat_polygon_point_manifold_depth(vector2 *verts,rat_polygon_axis *axes,unsigned int numverts,vector2 point,rat_real *min_dist)
{
	register unsigned int i;
	register rat_real dist;

	register int min_i=-1;
	*min_dist=0;

	dist=vector2_dot(axes[0].normal,point)-axes[0].dist;
	if (dist>0.0) return -1;
	if (dist<*min_dist)
	{
		*min_dist=dist;
		min_i=0;
	}

	for (i=1; i<numverts; i++)
	{
		dist=vector2_dot(axes[i].normal,point)-axes[i].dist;
		if (dist>0.0) return -1;
		if (dist<*min_dist)
		{
			*min_dist=dist;
			min_i=i;
		}
	}

	return min_i;
}

int rat_hull_polygon_point_query(rat_hull *hull,vector2 point)
{
	return rat_polygon_point_dist(
		((rat_hull_polygon *)hull)->world_verts,
		((rat_hull_polygon *)hull)->world_axes,
		((rat_hull_polygon *)hull)->numverts,
		point)<0.0;
}

rat_real rat_polygon_axis_value(vector2 *verts,unsigned int numverts,rat_polygon_axis axis)
{
	register unsigned int i;
	rat_real min=vector2_dot(axis.normal,verts[0]);

	for (i=1; i<numverts; i++)
	{
		register rat_real temp=vector2_dot(axis.normal,verts[i]);
		min=min<temp?min:temp;
	}

	return min-axis.dist;
}
