/*
	This file is part of Floculate.
	Copyright (C) 2008  Bill Whitacre

	Floculate is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Floculate is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "draw.h"
#include <math.h>

static rat_real _r,_g,_b,_a;
int rat_pretty_draw=0;

void rat_line_width(rat_real width)
{
	glLineWidth(width);
}

void rat_point_size(rat_real size)
{
	glPointSize(size);
}

void rat_set_color(rat_real r,rat_real g,rat_real b,rat_real a)
{
	glColor4f(_r=r,_g=g,_b=b,_a=a);
}

void rat_get_color(rat_real *r,rat_real *g,rat_real *b,rat_real *a)
{
	*r=_r; *g=_g;
	*b=_b; *a=_a;
}

void rat_draw_point(rat_real x,rat_real y)
{
	glBegin(GL_POINTS);
		glVertex2d(x,y);
	glEnd();
}

void rat_draw_points(rat_real *points,unsigned int numpts)
{
	register unsigned int i;
	glBegin(GL_POINTS);
	{
		for (i=0; i<numpts; i++)
			glVertex2d(points[i*2],points[i*2+1]);
	}
	glEnd();
}

void rat_draw_line(rat_real x1,rat_real y1,rat_real x2,rat_real y2)
{
	glBegin(GL_LINES);
	{
		glVertex2d(x1,y1);
		glVertex2d(x2,y2);
	}
	glEnd();
}

void rat_draw_lines(rat_real *points,unsigned int numpts)
{
	register unsigned int i;
	glBegin(GL_LINES);
	{
		for (i=0; i<numpts; i++)
			glVertex2d(points[i*2],points[i*2+1]);
	}
	glEnd();
}

void rat_draw_line_strip(rat_real *points,unsigned int numpts)
{
	register unsigned int i;
	glBegin(GL_LINE_STRIP);
	{
		for (i=0; i<numpts; i++)
			glVertex2d(points[i*2],points[i*2+1]);
	}
	glEnd();
}

void rat_draw_line_loop(rat_real *points,unsigned int numpts)
{
	register unsigned int i;
	glBegin(GL_LINE_LOOP);
		for (i=0; i<numpts; i++)
			glVertex2d(points[i*2],points[i*2+1]);
	glEnd();
}

void rat_draw_quads(rat_real *points,unsigned int numpts)
{
	register unsigned int i;
	glBegin(GL_QUADS);
		for (i=0; i<numpts; i++)
			glVertex2d(points[i*2],points[i*2+1]);
	glEnd();
}

void rat_draw_triangles(rat_real *points,unsigned int numpts)
{
	register unsigned int i;
	glBegin(GL_TRIANGLES);
		for (i=0; i<numpts; i++)
			glVertex2d(points[i*2],points[i*2+1]);
	glEnd();
}

void rat_draw_triangle_fan(rat_real *points,unsigned int numpts)
{
	register unsigned int i;
	glBegin(GL_TRIANGLE_FAN);
		for (i=0; i<numpts; i++)
			glVertex2d(points[i*2],points[i*2+1]);
	glEnd();
}

void rat_draw_triangle_strip(rat_real *points,unsigned int numpts)
{
	register unsigned int i;
	glBegin(GL_TRIANGLE_STRIP);
		for (i=0; i<numpts; i++)
			glVertex2d(points[i*2],points[i*2+1]);
	glEnd();
}

void rat_draw_rect(rat_real x1,rat_real y1,rat_real x2,rat_real y2,int fill)
{
	if (fill)
		glBegin(GL_QUADS);
	else
		glBegin(GL_LINE_LOOP);
	{
		glVertex2d(x1,y1);
		glVertex2d(x1,y2);
		glVertex2d(x2,y2);
		glVertex2d(x2,y1);
	} glEnd();
}

#ifndef _MSC_VER
#	define cos cosf
#	define sin sinf
#endif

void rat_draw_circle(rat_real x,rat_real y,rat_real radius,rat_real stepdivisor,int fill)
{
	rat_real theta,xmod,ymod,step=MATH_PI/(4.0f*radius*stepdivisor);
	step=step>MATH_PI/4.0f?MATH_PI/4.0f:step;

	if (fill)
	{
		glBegin(GL_POLYGON);
			for (theta=0.0f; theta<=MATH_PI*2.0f; theta+=step)
			{
				xmod=cos(theta)*radius;
				ymod=sin(theta)*radius;
				glVertex2f(x+xmod,y+ymod);
			}
		glEnd();
	}
	else
	{
		glBegin(GL_LINE_STRIP);
			for (theta=0.0f; theta<MATH_PI*2.0f; theta+=step)
			{
				xmod=cos(theta)*radius;
				ymod=sin(theta)*radius;
				glVertex2f(x+xmod,y+ymod);
			}
			glVertex2f(x+xmod,y+ymod);
			glVertex2f(x+cos(0)*radius,y+sin(0)*radius);
		glEnd();
	}
}

void rat_draw_elipse(rat_real x,rat_real y,rat_real rx,rat_real ry,rat_real stepdivisor,int fill)
{
	rat_real theta,xmod,ymod,step=MATH_PI/(4.0f*(rx<ry?rx:ry)*stepdivisor);

	if (fill)
	{
		glBegin(GL_POLYGON);
			for (theta=0.0f; theta<=MATH_PI*2.0f; theta+=step)
			{
				xmod=cos(theta)*rx;
				ymod=sin(theta)*ry;
				glVertex2f(x+xmod,y+ymod);
			}
		glEnd();
	}
	else
	{
		glBegin(GL_LINE_STRIP);
			for (theta=0.0f; theta<MATH_PI*2.0f; theta+=step)
			{
				xmod=cos(theta)*rx;
				ymod=sin(theta)*ry;
				glVertex2f(x+xmod,y+ymod);
			}
			glVertex2f(x+xmod,y+ymod);
			glVertex2f(x+cos(0)*rx,y+sin(0)*ry);
		glEnd();
	}
}

void rat_draw_world(rat_world *world,int draw_bodymgr,int draw_arbiters,int draw_bboxes)
{
	register unsigned int i;
	rat_iterator *iter;

	rat_set_color(0,0,0,1);
	rat_line_width(1.5);
	rat_point_size(3.0);

	iter=rat_body_manager_all_bodies_iterator(world->body_manager);
	for (; rat_iterator_finished(iter); rat_iterator_next(iter))
	{
		rat_body *body=(rat_body *)rat_iterator_value(iter);
		rat_draw_body(body,0);
	}
	rat_iterator_destroy(iter);

	iter=rat_body_manager_all_bodies_iterator(world->body_manager);
	for (; rat_iterator_finished(iter); rat_iterator_next(iter))
	{
		rat_body *body=(rat_body *)rat_iterator_value(iter);
		rat_draw_body(body,1);
	}
	rat_iterator_destroy(iter);

	rat_set_color(0,0,0.2,0.8);
	for (i=0; i<world->numconsts; i++)
		rat_draw_constraint(world->constraints[i]);

	rat_line_width(1.5);
	for (i=0; i<world->numsprings; i++)
	{
		rat_spring *spring=world->springs[i];
		switch (spring->activity)
		{
		case RAT_SPRING_ACTIVITY_PULLING:
			rat_set_color(0,0.75,0,0.75);
			break;
		case RAT_SPRING_ACTIVITY_NEUTRAL:
			rat_set_color(0,0,0.75,0.75);
			break;
		case RAT_SPRING_ACTIVITY_PUSHING:
			rat_set_color(0.75,0,0,0.75);
			break;
		}
		rat_draw_spring(spring);
	}

	rat_set_color(0.2,0.2,0.2,1);
	rat_line_width(1);

	if (draw_bodymgr)
	{
		switch (world->body_manager->type)
		{
		case rat_body_manager_type_quad_tree:
			rat_draw_quad_tree_matrix
				(((rat_body_manager_quad_tree *)
				(world->body_manager))->quadtreematrix);
			break;
		default:;
		}
	}

	rat_set_color(0,0,1,1);
	rat_line_width(1);

	if (draw_bboxes)
	{
		iter=rat_body_manager_all_bodies_iterator(world->body_manager);
		for (; rat_iterator_finished(iter); rat_iterator_next(iter))
		{
			rat_hull *hull=((rat_body *)rat_iterator_value(iter))->hull;
			rat_draw_line(hull->cached_bb.a.x,hull->cached_bb.a.y,hull->cached_bb.a.x,hull->cached_bb.b.y);
			rat_draw_line(hull->cached_bb.a.x,hull->cached_bb.b.y,hull->cached_bb.b.x,hull->cached_bb.b.y);
			rat_draw_line(hull->cached_bb.b.x,hull->cached_bb.b.y,hull->cached_bb.b.x,hull->cached_bb.a.y);
			rat_draw_line(hull->cached_bb.b.x,hull->cached_bb.a.y,hull->cached_bb.a.x,hull->cached_bb.a.y);
		}
		rat_iterator_destroy(iter);
	}

	if (draw_arbiters)
	{
		rat_set_color(1,0,0,1);

		iter=rat_body_manager_arbiters_iterator(world->body_manager);
		for (; rat_iterator_finished(iter); rat_iterator_next(iter))
			rat_draw_arbiter((rat_arbiter *)rat_iterator_value(iter));
		rat_iterator_destroy(iter);
	}
}

//	for passes:
//		0 - first pass: fill
//		1 - second pass: lines
//		2 - all passes: do all
void rat_draw_body(rat_body *body,int pass)
{
	rat_real r,g,b,a;
	vector2 pos=rat_hull_get_pos(body->hull);
	rat_real angle=rat_hull_get_angle(body->hull);

	switch (body->hull->type)
	{
		case rat_hull_type_polygon:
		{
			register unsigned int i;
			rat_hull_polygon *hull_polygon=(rat_hull_polygon *)body->hull;

			rat_get_color(&r,&g,&b,&a);
			if (pass==0||pass==2)
			{
				if (rat_body_get_is_asleep(body))
					rat_set_color(0.0,0.0,0.25,0.5);
				else
					rat_set_color(0.1,0.1,0.1,0.5);
			
				rat_draw_triangle_fan((rat_real *)hull_polygon->world_verts,hull_polygon->numverts);
			}
			
			rat_set_color(r,g,b,a);
			if (pass==1||pass==2)
			{
				rat_draw_line_loop((rat_real *)hull_polygon->world_verts,hull_polygon->numverts);

				if (rat_pretty_draw)
				{
					for (i=0; i<hull_polygon->numverts; i++)
					{
						vector2 axisline_a;
						vector2 axisline_b;

						axisline_a=vector2_multf(vector2_add(hull_polygon->world_verts[i],hull_polygon->world_verts[(i+1)%hull_polygon->numverts]),0.5);
						axisline_b=vector2_add(axisline_a,vector2_multf(vector2_negative(hull_polygon->world_axes[i].normal),5.0));

						rat_draw_line(axisline_a.x,axisline_a.y,axisline_b.x,axisline_b.y);
					}
				}
			}
		} break;
		case rat_hull_type_circle:
		{
			const rat_real mpi2=MATH_PI/2.0;
			rat_hull_circle *hull_circle=(rat_hull_circle *)body->hull;
			
			rat_get_color(&r,&g,&b,&a);
			if (pass==0||pass==2)
			{
				if (rat_body_get_is_asleep(body))
					rat_set_color(0.0,0.0,0.25,0.5);
				else
					rat_set_color(0.1,0.1,0.1,0.5);
				rat_draw_circle(pos.x,pos.y,hull_circle->radius,0.2f,1);
			}
			
			rat_set_color(r,g,b,a);
			if (pass==1||pass==2)
			{
				if (rat_pretty_draw)
				{
					rat_real subrad=hull_circle->radius>10?hull_circle->radius-5:hull_circle->radius*0.5;

					rat_real ang1cos=cos(angle)*hull_circle->radius;
					rat_real ang1sin=sin(angle)*hull_circle->radius;
					rat_real ang2cos=cos(angle+mpi2)*hull_circle->radius;
					rat_real ang2sin=sin(angle+mpi2)*hull_circle->radius;

					rat_real ang1cossub=cos(angle)*subrad;
					rat_real ang1sinsub=sin(angle)*subrad;
					rat_real ang2cossub=cos(angle+mpi2)*subrad;
					rat_real ang2sinsub=sin(angle+mpi2)*subrad;

					rat_draw_circle(pos.x,pos.y,hull_circle->radius,0.2f,0);

					rat_draw_line(
						pos.x+ang1cossub,pos.y+ang1sinsub,
						pos.x+ang1cos,pos.y+ang1sin);
					rat_draw_line(
						pos.x-ang1cossub,pos.y-ang1sinsub,
						pos.x-ang1cos,pos.y-ang1sin);
					rat_draw_line(
						pos.x+ang2cossub,pos.y+ang2sinsub,
						pos.x+ang2cos,pos.y+ang2sin);
					rat_draw_line(
						pos.x-ang2cossub,pos.y-ang2sinsub,
						pos.x-ang2cos,pos.y-ang2sin);
					rat_draw_point(pos.x,pos.y);
				}
				else
				{
					rat_real cosine=cos(angle)*hull_circle->radius;
					rat_real sine=sin(angle)*hull_circle->radius;

					rat_draw_circle(pos.x,pos.y,hull_circle->radius,0.2f,0);
					rat_draw_line(pos.x,pos.y,pos.x+cosine,pos.y+sine);
				}
			}
		} break;
		case rat_hull_type_particle:
		{
			if (pass==1||pass==2)
				rat_draw_point(pos.x,pos.y);
		} break;
	}
}

void rat_draw_constraint(rat_constraint *constraint)
{
	vector2 anch_a,anch_b,length;

	switch (constraint->type)
	{
	case rat_constraint_type_bar:
		anch_a=vector2_add(
			((rat_constraint_bar *)constraint)->world_anch_a,
			rat_hull_get_pos(constraint->body_a->hull));
		anch_b=vector2_add(
			((rat_constraint_bar *)constraint)->world_anch_b,
			rat_hull_get_pos(constraint->body_b->hull));

		rat_draw_line(anch_a.x,anch_a.y,anch_b.x,anch_b.y);
		break;
	case rat_constraint_type_slider:
		anch_a=vector2_add(
			((rat_constraint_slider *)constraint)->world_anch_a,
			rat_hull_get_pos(constraint->body_a->hull));
		anch_b=vector2_add(
			((rat_constraint_slider *)constraint)->world_anch_b,
			rat_hull_get_pos(constraint->body_b->hull));

		rat_draw_line(anch_a.x,anch_a.y,anch_b.x,anch_b.y);
		break;
	}
}

void rat_draw_spring(rat_spring *spring)
{
	vector2 anch_a,anch_b;

	anch_a=vector2_add(
		vector2_getrotated(spring->anch_a,rat_hull_get_angle(spring->body_a->hull)),
		rat_hull_get_pos(spring->body_a->hull));
	anch_b=spring->body_b==NULL?spring->anch_b:vector2_add(
		vector2_getrotated(spring->anch_b,rat_hull_get_angle(spring->body_b->hull)),
		rat_hull_get_pos(spring->body_b->hull));

	rat_draw_line(anch_a.x,anch_a.y,anch_b.x,anch_b.y);
}

void rat_draw_arbiter(rat_arbiter *arbiter)
{
	rat_real r,g,b,a;
	register unsigned int i;
	rat_contact_array *contacts=arbiter->contacts;
	register unsigned int numcons=rat_count_array_contacts(contacts);

	if (rat_pretty_draw)
	{
		vector2 pos_a=rat_hull_get_pos(arbiter->body_a->hull);
		vector2 pos_b=rat_hull_get_pos(arbiter->body_b->hull);

		rat_line_width(3.0);
		rat_get_color(&r,&g,&b,&a);
		rat_set_color(0.8,0.0,0.8,1.0);
		rat_draw_line(pos_a.x,pos_a.y,pos_b.x,pos_b.y);
		rat_set_color(r,g,b,a);
	}

	rat_point_size(5.0);
	for (i=0; i<numcons; i++)
	{
		rat_contact *con=rat_indexed_contact(contacts,i);

		vector2 point=con->point;
		vector2 normal=vector2_negative(con->normal);
		vector2 penetration=vector2_multf(normal,con->penetration);
#ifdef RAT_PHYSICS_MSVC
		line2 penetrator;
		line2 normline;

		line2_set(&penetrator,point,vector2_add(point,penetration));
		line2_set(&normline,penetrator.b,vector2_add(penetrator.b,vector2_multf(normal,10)));
#else
		line2 penetrator=
		{
			point,
			vector2_add(point,penetration)
		};
		line2 normline=
		{
			penetrator.b,
			vector2_add(penetrator.b,vector2_multf(normal,10))
		};
#endif

		rat_draw_point(point.x,point.y);
		rat_line_width(1.0);
		rat_draw_line(penetrator.a.x,penetrator.a.y,penetrator.b.x,penetrator.b.y);
		rat_draw_point(penetrator.b.x,penetrator.b.y);
		rat_line_width(2.0);
		rat_draw_line(normline.a.x,normline.a.y,normline.b.x,normline.b.y);
	}
}

static void quad_node_draw(rat_quad_tree_node *node)
{
	if (node->are_children)
	{
		rat_draw_line(node->quad.a.x,node->children[0]->quad.b.y,node->quad.b.x,node->children[0]->quad.b.y);
		rat_draw_line(node->children[0]->quad.b.x,node->quad.a.y,node->children[0]->quad.b.x,node->quad.b.y);

		quad_node_draw(node->children[0]);
		quad_node_draw(node->children[1]);
		quad_node_draw(node->children[2]);
		quad_node_draw(node->children[3]);
	}
}

void rat_draw_quad_tree_matrix(rat_quad_tree_matrix *matrix)
{
	register unsigned int i;
	for (i=0; i<matrix->numcells; i++)
		rat_draw_quad_tree(matrix->quadtrees[i]);
}

void rat_draw_quad_tree(rat_quad_tree *tree)
{
	rat_set_color(0,0,0,1.0);
	if (tree->is_built)
	{
		rat_draw_rect(
			tree->root->quad.a.x,
			tree->root->quad.a.y,
			tree->root->quad.b.x,
			tree->root->quad.b.y,0);
		quad_node_draw(tree->root);
	}
}

