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


#ifndef	__NGEOMETRY__
#define	__NGEOMETRY__


		#include "core/tangent_frame.h"
		#include "geometry/bounding_box.h"
		#include "color/color.h"
		#include "memory/nshared_ptr.h"
		#include "memory/nauto_ptr.h"
		#include "memory/bit_field.h"
		#include "nstring/nstring.h"


namespace GS {
	namespace Core {
		using namespace NML;

//------------------------------------------------------------------------------
struct	GeometrySkin
{
		float		w[__PV_BONE_LIMIT__];
		ushort		bone_index[__PV_BONE_LIMIT__];
};
struct	Polygon
{
		ushort		vtx_count;
		ushort		material;
		uint		*binding;
};
struct	VertexToPolygon
{
		ushort		pol_count;
		Array <uint> pol_index;
};
struct	PolygonVertex
{
		uint		pol_index;
		uint		vtx_index;
};
struct	VertexToVertex
{
		ushort		vtx_count;
		Array <PolygonVertex> vtx;
};

typedef	Polygon * pPolygon;
//------------------------------------------------------------------------------

/*!
	@short	Geometry class.
	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
class	Geometry : public SharedObject
{
public:

		NPLACEMENT_NEW(Geometry)

/// Item flag.
enum
{
		FlagHidden			=	(1 << 0),
		FlagNullLodProxy	=	(1 << 1),
		FlagNullShadowProxy	=	(1 << 2),

		// Non-serialized.
		FlagNoMaterialCache	=	(1 << 3)
};

private:

		/// Low-level vertex properties comparison.
		char			VertexPropertiesCompare(uint, uint, uint, uint, uint);

/*!
	@name	Serialization
	@{
*/
		void			ParseVertex(const Tag *);
		void			ParseAsciiVertex(const Tag *);
		void			ParseSkin(const Tag *);
		void			ParsePolygon(const Tag *);
		void			ParseAsciiPolygon(const Tag *);
		void			ParsePolygonNormal(const Tag *);
		void			ParseVertexNormal(const Tag *);
		void			ParseVertexTangent(const Tag *);
		void			ParseRGB(const Tag *);
		void			ParseAsciiRGB(const Tag *);
		void			ParseUV(const Tag *);
		void			ParseAsciiUV(const Tag *);
		void			ParseMaterials(const Tag *);

		void			ParseMisc(const Tag &);
/// @}

public:

		String			name;
		BitField		flag;

		String			copy_lock;				///< Used to prevent publishing.

/*!
	@name	Proxies.
	@{
*/
		String			lod_proxy;				///< LOD proxy geometry.
		float			lod_distance;			///< LOD distance.

		String			shadow_proxy;			///< Shadow proxy geometry.
/// @}

		/// Return the number of triangles in the mesh. Note: this does not perform any conversion.
		uint			GetTriangleCount() const
		{
			uint n, c = 0;
			for	(n = 0; n < pol.GetCount(); ++n)
				if	(pol[n].vtx_count >= 3)
					c += pol[n].vtx_count - 2;
			return c;
		}

		/*!
			@short	Flag vertices sharing the same properties as homogeneous.

			All vertices attributes are checked and compared. If they all lie
			under a given threshold then the vertex is reported as homogeneous.
			This function is heavily used by the geometry stripper and triangle
			list builder.

			@param	material_index	Optionally restrict attribute comparison
									to one material only.
									(default: -1 to include all materials in
									the	geometry.)
		*/
		void			FlagHomogeneousVertex(Array <bool> &flag, const Array <uint> &polygon_index, const Array <VertexToPolygon> &, int material_index = -1) const;

/*!
	@name	Topology section.
	@{
*/
		/// Compute the geometry min-max from current vertex set (AABB).
		MinMax			ComputeMinMax(const Matrix4 * = 0) const;
		/// Compute geometry bones bounding volumes.
		bool			ComputeBoneBoundingVolumes(Array <MinMax> &) const;

		Array <Vector4> vtx, vtx_normal;
		Array <TangentFrame> vtx_tangent;
		Array <Color>	rgb;
		Array <Vector2> uv[__UV_PER_GEOMETRY__];

		/// Return the number of UV channels used by the geometry.
		uint			GetUVCount() const;

		Array <Polygon>	pol;
		Array <uint> binding;

		bool			AllocateVertex(uint);
		bool			AllocatePolygon(uint);

		///	Allocate polygon binding array.
		bool			AllocatePolygonBinding();
		/// Compute polygon binding count.
		uint			ComputePolygonBindingCount() const;
/// @}

/*!
	@name	Skinning section.
	@{
*/
		/// Allocate bones array (not the skin weights array).
		bool			AllocateBone(uint);
		/// Return the number of bones referenced by the geometry.
		uint			GetBoneCount() const;

		Array <String>	bone_name;
		Array <Matrix4>	bone_bind_matrix;

		Array <GeometrySkin> skin;				///< Skin weights.
/// @}

		Array <Vector4>	pol_normal;
		Array <TangentFrame> pol_tangent;

		bool			ComputePolygonNormal(bool force = false);
		bool			ComputePolygonTangent(uint uv_index = 0, bool force = false);

		bool			ComputeVertexNormal(float msa = -1.f, bool force = false);
		bool			ComputeVertexNormal(Array <Vector4> &, float msa = -1.f);
		bool			ComputeVertexTangent(bool reverse_t = false, bool reverse_b = false, bool force = false);

/*!
	@name	Geometry tools.
	@{
*/
		/// Compute polygon start index.
		void			ComputePolygonIndex(Array <uint> &) const;
		/// Compute vertex to polygon table.
		void			ComputeVertexToPolygon(Array <VertexToPolygon> &) const;
		/// Compute vertex to vertex table.
		void			ComputeVertexToVertex(Array <VertexToVertex> &, const Array <VertexToPolygon> * = 0) const;
/// @}

		void			SmoothRGB(uint pass_count, float max_smooth_angle);
		void			FreeRGB();

		void			FreeUV();

/*!
	@name	Material section.
	@{
*/
	struct MaterialSlot
	{
		String		name;
		bool		use_cache;

		MaterialSlot() : use_cache(true) {}
	};

		Array <MaterialSlot> material_table;

		/// Merge duplicate materials in table.
		uint			MergeDuplicateMaterials();
/// @}

		void			Free();

/*!
	@name	Serialization.
	@{
*/
		bool			FromMetaTag(const Tag &);
		Tag				*AsMetaTag() const;
/// @}

		Geometry();
virtual	~Geometry();
};

		/// Compute the minmax of a vertex array.
extern	bool		ComputeVertexArrayMinMax(const Array <Vector4> &, MinMax &, const Matrix4 * = 0);

typedef	SharedPtr <Geometry> sGeometry;
typedef	Geometry * pGeometry;

	} // Core
} // GS


#endif	// __NGEOMETRY__
