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


		#include "core/geometry_to_triangle_list.h"
		#include "core/triangle_list.h"
		#include "core/geometry.h"
		#include "log/log.h"

		using namespace GS;
		using namespace GS::Core;

		/*!
			Converter max vertex per polygon.
		*/
		#define		PolygonMaxVertexMap	128
		/*
			@short	Define a larger step to skip polygons during the conversion.
			For debug purpose only.
		*/
		#define		TrilistStep		1


//-------------------------------------------------------------------------------
bool				GeometryToTriangleList::Convert(const Geometry &g, AutoList <Trilist *> &trilist, uint mxtri, uint optimize_cache)
{
	__LOG__ << "Building geometry triangle list...\n";

	if	(!g.pol.GetCount())
		__ERR__(__LOG_E__ << "Invalid geometry (" << g.name << ").\n", false)

	Array <uint> pol_index;
	g.ComputePolygonIndex(pol_index);

	Array <int> vmap(g.vtx.GetCount());
	if	(!vmap)
		__ERR__(__LOG_E__ << "Could not allocate memory to remap vertice!\n", false)
	bool warn_vertex_count_exceeded = false;

	Array <VertexToPolygon> vtx_to_pol;
	g.ComputeVertexToPolygon(vtx_to_pol);

	// Triangle list are split by material.
	Array <bool> flag;

	for	(uint n = 0; n < g.material_table.GetCount(); n++)
	{
		uint _p = 0, p;

		// Flag homogeneous vertex for this material.
		g.FlagHomogeneousVertex(flag, pol_index, vtx_to_pol, n);

		while (_p < g.pol.GetCount())
		{
			ushort bone_map[256];
			int bone_count = 0;

			// Count how many vertex will go in this list.
			uint tl_vtxc = 0, tl_tric = 0;
			for	(p = 0; p < g.vtx.GetCount(); p++)
				vmap[p] = -1;

			for	(p = _p; p < g.pol.GetCount(); p += TrilistStep)
				if	((g.pol[p].vtx_count > 2) && (g.pol[p].material == n))
				{
					// Check limit constraints.
					if	((tl_tric + g.pol[p].vtx_count - 2) >= mxtri)
						break;
					if	(bone_count >= (__PL_BONE_LIMIT__ - 4))
						break;

					// Valid polygon.
					for	(uint v = 0; v < g.pol[p].vtx_count; v++)
					{
						uint pol_crel = g.pol[p].binding[v];

						// Keep track of the bone set for this list.
						if	(g.skin)
							for	(int n = 0; n < 4; ++n)
							{
								for (int b = 0; b < bone_count; ++b)
									if	(bone_map[b] == g.skin[pol_crel].bone_index[n])
										goto bone_registered;

								// Register bone.
								if	(bone_count < __PL_BONE_LIMIT__)
										bone_map[bone_count++] = g.skin[pol_crel].bone_index[n];
								else	__LOG_E__ << "Bone array safeguard exceeded!\n";

							bone_registered:;
							}

						if	(flag[pol_crel])
						{
							if	(vmap[pol_crel] == -1)
								vmap[pol_crel] = tl_vtxc++;
						}
						else
							tl_vtxc++;
					}
					tl_tric += g.pol[p].vtx_count - 2;
				}

			uint break_at = p;

			/*
				If we have a non-zero vertices triangle list, let's allocate it
				and its buffers then fill it.
			*/
			if	(tl_vtxc)
			{
				// Allocate trilist.
				Trilist *ptrilist = new Trilist;
				if	(!ptrilist)
					__ERR__(__LOG_E__ << "Couldn't allocate triangle list container objects.\n", false)
				trilist.Add(ptrilist);

				// Allocate trilist buffers.
				ptrilist->vtx.Allocate(tl_vtxc);
				ptrilist->idx.Allocate(tl_tric * 3);
				if	(g.skin)
					ptrilist->skin.Allocate(tl_vtxc);
				if	(bone_count)
				{
					ptrilist->bone.Allocate(bone_count);
					Memory::Copy(&ptrilist->bone[0], bone_map, sizeof(ushort) * bone_count);
				}

				if	(g.vtx_normal)
					ptrilist->nrm.Allocate(tl_vtxc);
				if	(g.vtx_tangent)
					ptrilist->tangent.Allocate(tl_vtxc);

				for	(uint u = 0; u < __UV_PER_GEOMETRY__; u++)	// Note: never remap geometry UV channels.
					if	(g.uv[u])
						ptrilist->uv[u].Allocate(tl_vtxc);

				if	(g.rgb)
					ptrilist->rgb.Allocate(tl_vtxc);

				// Fill trilist.
				ptrilist->mat = n;
				for	(p = 0; p < g.vtx.GetCount(); p++)
					vmap[p] = -1;

				uint c_vtx = 0, c_tri = 0;
				for	(p = _p; p < g.pol.GetCount(); p += TrilistStep)
				{
					if	((g.pol[p].vtx_count > 2) && (g.pol[p].material == n))
					{
						// Do not overrun.
						if	((c_tri + g.pol[p].vtx_count - 2) >= mxtri)
							break;
						if	(p == break_at)
							break;

						// STOP! Lists will break a few polygons before they are actually ended!
					#if		0
						if	(ptrilist->vtx.GetCount() ==  tl_vtxc)
							break;
					#endif

						// Append polygon.
						uint cpol_vmap[PolygonMaxVertexMap], v;

						for	(v = 0; v < g.pol[p].vtx_count; v++)
						{
							uint pol_crel = g.pol[p].binding[v];

							// Map or insert vertex.
							bool do_insert_vtx = false;

							if	(flag[pol_crel])
							{
								if	(vmap[pol_crel] == -1)
								{
									vmap[pol_crel] = c_vtx;
									do_insert_vtx = true;
								}
								if	(v < PolygonMaxVertexMap)
									cpol_vmap[v] = vmap[pol_crel];
								else
									warn_vertex_count_exceeded = true;
							}
							else
							{
								if	(v < PolygonMaxVertexMap)
								{
									cpol_vmap[v] = c_vtx;
									do_insert_vtx = true;
								}
								else
									warn_vertex_count_exceeded = true;
							}

							// Perform vertex insertion.
							if	(do_insert_vtx)
							{
								// Position dump.
								ptrilist->vtx[c_vtx] = g.vtx[pol_crel];

								// Normal dump.
								if	(ptrilist->nrm)
									ptrilist->nrm[c_vtx] = g.vtx_normal[pol_index[p] + v];

								// Tangent dump.
								if	(ptrilist->tangent)
									ptrilist->tangent[c_vtx] = g.vtx_tangent[pol_index[p] + v];

								// Skin dump.
								if	(ptrilist->skin)
									for (int b = 0; b < 4; ++b)
									{
										// Resolve skin bone in local trilist bone map.
										int n;
										for (n = 0; n < bone_count; ++n)
											if	(bone_map[n] == g.skin[pol_crel].bone_index[b])
												break;

										// Failed to resolve, cancel this bone out.
										if	(n == bone_count)
										{
											ptrilist->skin[c_vtx].w[b] = 0;
											ptrilist->skin[c_vtx].bone_index[b] = 0;
										}
										else
										{
											ptrilist->skin[c_vtx].w[b] = g.skin[pol_crel].w[b];
											ptrilist->skin[c_vtx].bone_index[b] = (uchar)n;
										}
									}

								// UV sets dump.
								for	(uint u = 0; u < __UV_PER_GEOMETRY__; u++)
									if	(ptrilist->uv[u] && g.uv[u])
										ptrilist->uv[u][c_vtx] = g.uv[u][pol_index[p] + v];

								// RGB color.
								if	(ptrilist->rgb)
									ptrilist->rgb[c_vtx] = g.rgb[pol_index[p] + v];

								// Insertion done.
								++c_vtx;
							}
						}

						// Remap and convert this polygon to triangle list index.
						for	(v = 1; v < uint(g.pol[p].vtx_count - 1); v++)
						{
							if	(v == PolygonMaxVertexMap)
								break;

							uint itri = c_tri * 3;
							ptrilist->idx[itri + 0] = cpol_vmap[0];
							ptrilist->idx[itri + 1] = cpol_vmap[v];
							ptrilist->idx[itri + 2] = cpol_vmap[v + 1];
							++c_tri;
						}
					}
				}
			}
			_p = p;
		}
	}

	int ttri = 0, tbone = 0;
	ListForeachPtr(Trilist *, ptl, trilist)
	{
		ttri += ptl->GetTriangleCount();
		tbone += ptl->bone.GetCount();
	}

	if	(warn_vertex_count_exceeded)
		__LOG_E__ << "One or more polygon vertex count exceeded remapping capability (" << PolygonMaxVertexMap << ") in geometry '" << g.name << "'.\n";
	__LOG__ << "Done, average: " << (float)ttri / trilist.GetCount() << " tri/list, " << (float)tbone / trilist.GetCount() << " bone/list.\n";

#if	0	// Grabs a few FPS at the cost of much longer load time.
	if	(optimize_cache > 0)
		Trilist::Optimize(trilist, optimize_cache);
#endif

	return true;
}
//-------------------------------------------------------------------------------
