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


		#include <cmath>
		#include "core/renderer_toolbox.h"
		#include "core/core_profiler.h"
		#include "core/item.h"
		#include "core/raster_font.h"
		#include "log/log.h"
	#if	__PLATFORM_NINTENDO_WII__
		#include "system/wii_memory.h"
		#include "wii_platform.h"
	#endif

		using namespace GS::Units;
		using namespace GS::Math;


namespace GS {
	namespace RendererToolbox {

//------------------------------------------------------------------------------
void				Triangle3D(Renderer &render, const Vector4 vtx[3], const Color color[3], const Vector2 uv[3], const Render::Texture *t, Material::BlendOperator blendop, Material::RenderWord rword)
{	render.DrawTriangle(1, vtx, NULL, color, uv, t, blendop, rword);	}
void				Line3D(Renderer &render, const Vector4 &a, const Vector4 &b, const Color *c, Material::BlendOperator bo, Material::RenderWord rw)
{
	Vector4	v[2] = { a, b };
	Color	u[2] = { c ? *c : Color(1, 1, 1), c ? *c : Color(1, 1, 1) };
	render.DrawLine(1, v, u, bo, rw);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				DrawCircle(Renderer &render, const Vector4 &position, float radius, const Matrix3 *m, const Color *color, Material::BlendOperator bo)
{
	render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());

	Matrix3 m3 = m ? *m : Matrix3::IdentityMatrix();
	for (float a = Deg(0.f); a < Deg(360.f); a += Deg(10.f))
		Line3D(render, Vector4(Cos(a), Sin(a), 0) * m3 * radius + position, Vector4(Cos(a + Deg(10.f)), Sin(a + Deg(10.f)), 0) * m3 * radius + position, color, bo);
}
void				DrawCylinder(Renderer &render, const Vector4 &position, float radius, float length, const Matrix3 *m, const Color *color, Material::BlendOperator bo)
{
	render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());

	Matrix3 m3 = m ? *m : Matrix3::IdentityMatrix();
	DrawCircle(render, position + Vector4(0, 0, length * 0.5f) * m3, radius, &m3, color);
	DrawCircle(render, position + Vector4(0, 0, length * -0.5f) * m3, radius, &m3, color);
	Line3D(render, position + Vector4(radius, 0, length * 0.5f) * m3, position + Vector4(radius, 0, length * -0.5f) * m3, color, bo);
	Line3D(render, position + Vector4(-radius, 0, length * 0.5f) * m3, position + Vector4(-radius, 0, length * -0.5f) * m3, color, bo);
	Line3D(render, position + Vector4(0, radius, length * 0.5f) * m3, position + Vector4(0, radius, length * -0.5f) * m3, color, bo);
	Line3D(render, position + Vector4(0, -radius, length * 0.5f) * m3, position + Vector4(0, -radius, length * -0.5f) * m3, color, bo);
}
void				DrawCapsule(Renderer &render, const Vector4 &position, float radius, float length, const Matrix3 *m, const Color *color, Material::BlendOperator bo)
{
	render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());

	Matrix3 m3 = m ? *m : Matrix3::IdentityMatrix();
	DrawSphere(render, position + Vector4(0, 0, length * 0.5f) * m3, radius, color);
	DrawSphere(render, position - Vector4(0, 0, length * 0.5f) * m3, radius, color);
	Line3D(render, position + Vector4(radius, 0, length * 0.5f) * m3, position + Vector4(radius, 0, length * -0.5f) * m3, color, bo);
	Line3D(render, position + Vector4(-radius, 0, length * 0.5f) * m3, position + Vector4(-radius, 0, length * -0.5f) * m3, color, bo);
}
void				DrawCone(Renderer &render, const Vector4 &position, float radius, float length, const Matrix3 *m, const Color *color, Material::BlendOperator bo)
{
	render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());

	Matrix3	m3 = m ? *m : Matrix3::IdentityMatrix();
	DrawCircle(render, position + Vector4(0, 0, length * -0.5f) * m3, radius, &m3, color);
	Line3D(render, position + Vector4(0, 0, length * 0.5f) * m3, position + Vector4(radius, 0, length * -0.5f) * m3, color, bo);
	Line3D(render, position + Vector4(0, 0, length * 0.5f) * m3, position + Vector4(-radius, 0, length * -0.5f) * m3, color, bo);
	Line3D(render, position + Vector4(0, 0, length * 0.5f) * m3, position + Vector4(0, radius, length * -0.5f) * m3, color, bo);
	Line3D(render, position + Vector4(0, 0, length * 0.5f) * m3, position + Vector4(0, -radius, length * -0.5f) * m3, color, bo);
}
void				DrawSphere(Renderer &render, const Vector4 &p, float r, const Color *color, Material::BlendOperator bo)
{
	render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());

	#define	SPHERE_NSEG		36

	uint	n;
	float	sn, cs;
	Vector4	a[2], b[2], c[2];
	float	angle, dt;
	Color	_r(1, 1, 0), _g(1, 1, 0), _b(1, 1, 0);
	
	angle = 0;
	dt = Deg(360.f) / SPHERE_NSEG;

	sn = Sin(angle) * r;
	cs = Cos(angle) * r;

	a[1].Set(p.x, p.y + cs, p.z + sn);
	b[1].Set(p.x + sn, p.y + cs, p.z);
	c[1].Set(p.x + cs, p.y, p.z + sn);

	for	(n = 0; n < SPHERE_NSEG; n++)
	{
		a[0] = a[1]; b[0] = b[1]; c[0] = c[1];

		angle += dt;
		sn = Sin(angle) * r;
		cs = Cos(angle) * r;

		a[1].Set(p.x, p.y + cs, p.z + sn);
		b[1].Set(p.x + sn, p.y + cs, p.z);
		c[1].Set(p.x + cs, p.y, p.z + sn);
		
		Line3D(render, a[0], a[1], color ? color : &_r);
		Line3D(render, b[0], b[1], color ? color : &_g);
		Line3D(render, c[0], c[1], color ? color : &_b);
	}
}
void				DrawBall(Renderer &render, const Matrix4 &m, float r, Color *uc, Material::BlendOperator bo)
{
	render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());

	#define	BALL_NSEG		24

	uint	n;
	float	sn, cs;
	Vector4	a[2], b[2], c[2];
	float	angle, dt;

	angle = 0;
	dt = Deg(360.f) / BALL_NSEG;

	sn = Sin(angle) * r;
	cs = Cos(angle) * r;

	a[1].Set(0, cs, sn);
	b[1].Set(sn, cs, 0);
	c[1].Set(cs, 0, sn);
	a[1] *= m;
	b[1] *= m;
	c[1] *= m;
	Color	color(1, 1, 1);
	if	(!uc)
		uc = &color;

	for	(n = 0; n < BALL_NSEG; n++)
	{
		a[0] = a[1]; b[0] = b[1]; c[0] = c[1];

		angle += dt;
		sn = Sin(angle) * r;
		cs = Cos(angle) * r;

		a[1].Set(0, cs, sn);
		b[1].Set(sn, cs, 0);
		c[1].Set(cs, 0, sn);
		a[1] *= m;
		b[1] *= m;
		c[1] *= m;
		
		Line3D(render, a[0], a[1], uc, bo);
		Line3D(render, b[0], b[1], uc, bo);
		Line3D(render, c[0], c[1], uc, bo);
	}
}
void				DrawCross(Renderer &render, const Vector4 &c, float size, const Color *color, Material::BlendOperator bo)
{
	render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());

	const Color dft(1, 1, 1), *cl;

	if	(color)
			cl = color;
	else	cl = &dft;

	Vector4		o(c);
	Vector4		a[2];

	a[0] = o; a[1] = o;
	a[0].x -= size;
	a[1].x += size;
	Line3D(render, a[0], a[1], cl, bo);
	a[0] = o; a[1] = o;
	a[0].y -= size;
	a[1].y += size;
	Line3D(render, a[0], a[1], cl, bo);
	a[0] = o; a[1] = o;
	a[0].z -= size;
	a[1].z += size;
	Line3D(render, a[0], a[1], cl, bo);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				DrawAABB(Renderer &render, const MinMax &minmax, const Color *c, Material::BlendOperator bo)
{
	render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());

	const Color color(0.5, 1.0, 0);
	if	(!c)
		c = &color;
	const Vector4 *min = &minmax.mn, *max = &minmax.mx;
	Vector4		v[2];

	v[0].Set(min->x, min->y, min->z); v[1].Set(max->x, min->y, min->z);
	Line3D(render, v[0], v[1], c, bo);
	v[0].Set(min->x, min->y, min->z); v[1].Set(min->x, min->y, max->z);
	Line3D(render, v[0], v[1], c, bo);
	v[0].Set(max->x, min->y, min->z); v[1].Set(max->x, min->y, max->z);
	Line3D(render, v[0], v[1], c, bo);
	v[0].Set(min->x, min->y, max->z); v[1].Set(max->x, min->y, max->z);
	Line3D(render, v[0], v[1], c, bo);

	v[0].Set(min->x, max->y, min->z); v[1].Set(max->x, max->y, min->z);
	Line3D(render, v[0], v[1], c, bo);
	v[0].Set(min->x, max->y, min->z); v[1].Set(min->x, max->y, max->z);
	Line3D(render, v[0], v[1], c, bo);
	v[0].Set(max->x, max->y, min->z); v[1].Set(max->x, max->y, max->z);
	Line3D(render, v[0], v[1], c, bo);
	v[0].Set(min->x, max->y, max->z); v[1].Set(max->x, max->y, max->z);
	Line3D(render, v[0], v[1], c, bo);

	v[0].Set(min->x, min->y, min->z); v[1].Set(min->x, max->y, min->z);
	Line3D(render, v[0], v[1], c, bo);
	v[0].Set(max->x, min->y, min->z); v[1].Set(max->x, max->y, min->z);
	Line3D(render, v[0], v[1], c, bo);
	v[0].Set(max->x, min->y, max->z); v[1].Set(max->x, max->y, max->z);
	Line3D(render, v[0], v[1], c, bo);
	v[0].Set(min->x, min->y, max->z); v[1].Set(min->x, max->y, max->z);
	Line3D(render, v[0], v[1], c, bo);
}
void				DrawOBB(Renderer &render, const OBB &obb, const Color *color, Material::BlendOperator bo)
{
	Vector4		vtx[8], _vtx[8];

	_vtx[0].Set(-0.5,  0.5,  0.5);
	_vtx[1].Set( 0.5,  0.5,  0.5);
	_vtx[2].Set( 0.5, -0.5,  0.5);
	_vtx[3].Set(-0.5, -0.5,  0.5);
	_vtx[4].Set(-0.5,  0.5, -0.5);
	_vtx[5].Set( 0.5,  0.5, -0.5);
	_vtx[6].Set( 0.5, -0.5, -0.5);
	_vtx[7].Set(-0.5, -0.5, -0.5);

	Matrix4	mtx = Matrix4::FromMatrix3(obb.bb_rotation * Matrix3::ScaleMatrix(obb.bb_scale));
	mtx.SetRow(3, obb.bb_position);
	mtx.Apply(vtx, _vtx, 8);

	const Color _color(1, 0.5f, 0);
	if	(!color)
		color = &_color;

	render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());

	Line3D(render, vtx[0], vtx[1], color, bo);
	Line3D(render, vtx[1], vtx[2], color, bo);
	Line3D(render, vtx[2], vtx[3], color, bo);
	Line3D(render, vtx[3], vtx[0], color, bo);

	Line3D(render, vtx[4], vtx[5], color, bo);
	Line3D(render, vtx[5], vtx[6], color, bo);
	Line3D(render, vtx[6], vtx[7], color, bo);
	Line3D(render, vtx[7], vtx[4], color, bo);

	Line3D(render, vtx[0], vtx[4], color, bo);
	Line3D(render, vtx[1], vtx[5], color, bo);
	Line3D(render, vtx[2], vtx[6], color, bo);
	Line3D(render, vtx[3], vtx[7], color, bo);
}
void				DrawFilledOBB(Renderer &render, const OBB &obb, const Color *_color, Material::BlendOperator bo, Material::RenderWord rw)
{
	Vector4	vtx[8], v[4];
	vtx[0].Set(-0.5,  0.5,  0.5);
	vtx[1].Set( 0.5,  0.5,  0.5);
	vtx[2].Set( 0.5, -0.5,  0.5);
	vtx[3].Set(-0.5, -0.5,  0.5);
	vtx[4].Set(-0.5,  0.5, -0.5);
	vtx[5].Set( 0.5,  0.5, -0.5);
	vtx[6].Set( 0.5, -0.5, -0.5);
	vtx[7].Set(-0.5, -0.5, -0.5);

	Matrix4	mtx = Matrix4::FromMatrix3(obb.bb_rotation * Matrix3::ScaleMatrix(obb.bb_scale));
	mtx.SetRow(3, obb.bb_position);
	render.SetWorldMatrix(mtx);

	Color		color[3];
	for	(int n = 0; n < 3; ++n)
		color[n] = _color ? *_color : Color(1, 0.5f, 0);

	v[0] = vtx[0]; v[1] = vtx[1]; v[2] = vtx[2];
	Triangle3D(render, v, color, NULL, NULL, bo, rw);
	v[0] = vtx[0]; v[1] = vtx[2]; v[2] = vtx[3];
	Triangle3D(render, v, color, NULL, NULL, bo, rw);

	v[0] = vtx[4]; v[1] = vtx[5]; v[2] = vtx[1];
	Triangle3D(render, v, color, NULL, NULL, bo, rw);
	v[0] = vtx[4]; v[1] = vtx[1]; v[2] = vtx[0];
	Triangle3D(render, v, color, NULL, NULL, bo, rw);

	v[0] = vtx[7]; v[1] = vtx[6]; v[2] = vtx[5];
	Triangle3D(render, v, color, NULL, NULL, bo, rw);
	v[0] = vtx[7]; v[1] = vtx[5]; v[2] = vtx[4];
	Triangle3D(render, v, color, NULL, NULL, bo, rw);

	v[0] = vtx[3]; v[1] = vtx[2]; v[2] = vtx[6];
	Triangle3D(render, v, color, NULL, NULL, bo, rw);
	v[0] = vtx[3]; v[1] = vtx[6]; v[2] = vtx[7];
	Triangle3D(render, v, color, NULL, NULL, bo, rw);

	v[0] = vtx[2]; v[1] = vtx[1]; v[2] = vtx[5];
	Triangle3D(render, v, color, NULL, NULL, bo, rw);
	v[0] = vtx[2]; v[1] = vtx[5]; v[2] = vtx[6];
	Triangle3D(render, v, color, NULL, NULL, bo, rw);

	v[0] = vtx[0]; v[1] = vtx[3]; v[2] = vtx[7];
	Triangle3D(render, v, color, NULL, NULL, bo, rw);
	v[0] = vtx[0]; v[1] = vtx[7]; v[2] = vtx[4];
	Triangle3D(render, v, color, NULL, NULL, bo, rw);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				DrawCube(Renderer &render, const Vector4 &p, float size, const Color *color, Material::BlendOperator bo)
{
	const Color _color(0, 0.5, 1.f);
	if	(!color)
		color = &_color;

	Vector4 dt(size, size, size);
	Vector4 t = p - dt;
	Vector4 t2 = p + dt;

	MinMax mnmx(t, t2);
	DrawAABB(render, mnmx, color, bo);
}
void				DrawSquare(Renderer &render, const Vector4 &p, float size, Material::BlendOperator bo)
{
	render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());

	Vector4	a(p), b(p), c(p), d(p);
	a.x -= size; a.y -= size;
	b.x += size; b.y -= size;
	c.x += size; c.y += size;
	d.x -= size; d.y += size;

	Color cs(1, 1, 1);
	Line3D(render, a, b, &cs, bo);
	Line3D(render, b, c, &cs, bo);
	Line3D(render, c, d, &cs, bo);
	Line3D(render, d, a, &cs, bo);
}
//------------------------------------------------------------------------------

	} // RendererToolbox
} // GS
