#ifndef LWO_H
#define LWO_H

#include "ssVector.hpp"
#include "color.h"
#include <string>
#include <vector>

//=============================================================================
//=============================================================================

class Mesh
{
public:
#pragma pack( push, 1 )
	struct Vertex
	{
		void			reset()
		{
			m_position.set(0,0,0);
			m_normal.set(0,0,0);
			m_t1.set(0,0,0);
			m_t2.set(0,0,0);
			m_maxc = 0;
			m_minc = 0;
			m_uv.set(0,0);
			m_diffuse.set(0,0,0,0);
		}
		void			add( const Vertex &v )
		{
			m_position += v.m_position;
			m_normal += v.m_normal;
			m_t1 += v.m_t1;
			m_t2 += v.m_t2;
			m_maxc += v.m_maxc;
			m_minc += v.m_minc;
			m_uv += v.m_uv;
			m_diffuse += v.m_diffuse;
		}
		void			mad( const Vertex &v, float c )
		{
			m_position += v.m_position * c;
			m_normal += v.m_normal * c;
			m_t1 += v.m_t1 * c;
			m_t2 += v.m_t2 * c;
			m_maxc += v.m_maxc * c;
			m_minc += v.m_minc * c;
			m_uv += v.m_uv * c;
			m_diffuse += v.m_diffuse * c;
		}
		void			normalize()
		{
			m_normal.normalize();
			m_t1.normalize();
			m_t2.normalize();
		}
		Vector3		m_position;
		Vector3		m_normal;
		Vector3		m_t1;
		Vector3		m_t2;
		float		m_maxc;
		float		m_minc;
		int			m_triangleUsing;	//30msb polyindex 2lsb edgeindex
		Vector2		m_uv;
		Vector4		m_diffuse;
		bool		m_reliable;
	};

	struct Triangle
	{
		Vector3		m_normal;
		float		m_area;
		int			m_surface;
		int			m_v[3];
		int			m_n[3];
		char		m_nedge[3];
		char		m_dummy;
		Vector2		m_uv[3];
		int			m_baseTriangle;		//triangle index before subdivision
	};

	struct Edge
	{
		bool operator<( const Edge &e ) const	{ if( m_v[0] < e.m_v[0] ) return true; else if( m_v[0] > e.m_v[0] ) return false; else return (m_v[1] < e.m_v[1]) ? true : false; }
		int			m_v[2];
		int			m_t[2];
	};

	struct Surface
	{
		std::string	m_name;
		Color		m_color;
		int			m_polygons;
	};
#pragma pack( pop )

	Mesh();
	Mesh( int vertices, int triangles );
	Mesh( const char *fileName );
	Mesh( const Mesh &m );
	~Mesh();

	void				load( const char *fileName );

	Vertex				&getVertex( int i )				{ SS_ASSERT(i>=0&&i<m_vertices.size()); return m_vertices[i]; }
	Triangle			&getTriangle( int i )			{ SS_ASSERT(i>=0&&i<m_triangles.size()); return m_triangles[i]; }
	Edge				&getEdge( int i )				{ SS_ASSERT(i>=0&&i<m_edges.size()); return m_edges[i]; }
	Surface				&getSurface( int i )			{ SS_ASSERT(i>=0&&i<m_surfaces.size()); return m_surfaces[i]; }

	int					getVertexCount() const			{ return m_vertices.size(); }
	int					getTriangleCount() const		{ return m_triangles.size(); }
	int					getEdgeCount() const			{ return m_edges.size(); }
	int					getSurfaceCount() const			{ return m_surfaces.size(); }

	std::vector<Vertex>		&getVertices()				{ return m_vertices; }
	std::vector<Triangle>	&getTriangles()				{ return m_triangles; }
	std::vector<Edge>		&getEdges()					{ return m_edges; }
	std::vector<Surface>	&getSurfaces()				{ return m_surfaces; }

	const Vector3		&getBoundingBoxMin() const		{ return m_boundingBoxMin; }
	const Vector3		&getBoundingBoxMax() const		{ return m_boundingBoxMax; }
	float				getBoundingRadius() const		{ return m_boundingRadius; }

	void				calculateAdjacency();	//calculates also edges
	void				setTriangleUsing();
	void				calculateNormals();
	void				setParametrization();
	void				subdivide( int n );
	void				calculateBoundingBox();

	struct ventry
	{
		int		m_vertex;
		int		m_i;
	};
	const std::vector<ventry> &get1Neighbourhood( int ver, int &valence, bool &boundary, int &left, int &right );
protected:
	std::vector<Vertex>		m_vertices;
	std::vector<Triangle>	m_triangles;
	std::vector<Edge>		m_edges;
	std::vector<Surface>	m_surfaces;
	std::string				m_filename;

	Vector3					m_boundingBoxMin;
	Vector3					m_boundingBoxMax;
	float					m_boundingRadius;
	int						m_ambiguousNeighbours;
	bool					m_adjacencyCalculated;

	static UINT8			*m_fileptr;
	static UINT8			*m_nextchunkptr;
	static UINT8			*m_nextsubchunkptr;
	static UINT8			*m_fileend;
	static UINT8			*m_readptr;

	UINT32					readID4();
	int						readI1();
	int						readI2();
	int						readI4();
	UINT8					readU1();
	UINT16					readU2();
	UINT32					readU4();
	float					readF4();
	const char				*readS0();
	UINT32					readVX();
	Vector3					readCOL12();
	Vector3					readVEC12();

	void					loadLW5( );
	void					loadLW6( );
};

//=============================================================================
//=============================================================================

#endif
