#pragma once

#include "ElementProxies.hpp"
#include <MLR\MLR_I_PMesh.hpp>
#include <MLR\MLR_I_L_PMesh.hpp>
#include <MLR\MLR_I_L_DT_PMesh.hpp>
#include "MLR\MLRHeaders.hpp"

namespace ElementProxies {

	class ElementSceneProxy;
	class ElementGroupProxy;
	class MLRPolygonProxy;
	class MLRIndexProxy;

	class ApplyDetailTexturesProcess;

	//
	//#########################################################################
	//#####################    ElementPolygonMeshProxy    ##########################
	//#########################################################################
	//
	class ElementPolygonMeshProxy:
		public Proxies::PolygonMeshProxy
	{
		friend class MLRPolygonProxy;
		friend class MLRIndexProxy;
		friend class MLRVertexProxy;

	public:
		static void
			InitializeClass();
		static void
			TerminateClass();

		static ClassData
			*DefaultData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Constructors
	//
	public:
		static ElementPolygonMeshProxy*
			MakeProxy(
				ElementSceneProxy *scene,
				Proxies::GroupProxy *parent,
				ElementRenderer::ShapeElement *shape,
				Stuff::ChainIterator *iterator
			)
				{
					return
						new ElementPolygonMeshProxy(
							scene,
							parent,
							shape,
							iterator
						);
				}

		void
			Destroy();

		void*
			operator new(size_t)
				{return AllocatedMemory->New();}
		void
			operator delete(void *where)
				{AllocatedMemory->Delete(where);}

	protected:
		ElementPolygonMeshProxy(
			ElementSceneProxy *scene,
			Proxies::GroupProxy *parent,
			ElementRenderer::ShapeElement *element,
			Stuff::ChainIterator *iterator
		);
		~ElementPolygonMeshProxy();

		static Stuff::MemoryBlock
			*AllocatedMemory;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Process support
	//
	public:
		bool
			SplitByState(Proxies::SplitByStateProcess *process);
		bool
			MakeSingleSided(Proxies::MakeSingleSidedProcess *process);
		bool
			OptimizeFlatShading(Proxies::OptimizeFlatShadingProcess *process);
		virtual void
			ApplyDetailTextures(ApplyDetailTexturesProcess *process);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Testing
	//
	public:
		void
			TestInstance() const;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Polygon mesh management functions
	//
	public:
		//
		// Position traversal functions
		//
		ElementSceneProxy*
			GetSceneProxy()
				{
					Check_Object(this);
					return Cast_Pointer(ElementSceneProxy*, sceneProxy);
				}

		void
			TransferAndAppendToParentGroup(Proxies::GroupProxy *parent);

		Proxies::ChildProxy*
			UseNextSiblingProxy();
		Proxies::ChildProxy*
			UsePreviousSiblingProxy();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// polygon mesh functions
	//
	public:
		//
		// name functions
		//
		bool
			GetName(Stuff::MString *name);
		void
			SetName(const char* name);

		//
		// Position functions
		//
		bool
			GetLocalToParent(Stuff::LinearMatrix4D *matrix);
		void
			SetLocalToParent(const Stuff::LinearMatrix4D &matrix);

		//
		// Bounding functions
		//
		void
			GetCentroid(Stuff::Point3D *center);
		bool
			GetOBB(Stuff::OBB *obb);
		void
			SetOBB(const Stuff::OBB &obb);
		bool
			GetBoundingSphere(Stuff::Sphere *sphere);
		void
			SetBoundingSphere(const Stuff::Sphere &sphere);

		//
		// Get the number of polygons in the mesh
		//
		unsigned
			UsePolygonArray(Stuff::DynamicArrayOf<Proxies::PolygonProxy*> *polygons);
		void
			AddPolygons(
				Proxies::Process *process,
				Stuff::DynamicArrayOf<Proxies::PolygonProxy*> &polygons
			);
		void
			SetToMatchMultiState(Proxies::MultiState* multi_state);

		//
		// polygon traversal
		//
		Proxies::PolygonProxy*
			UseFirstPolygonProxy();
		Proxies::PolygonProxy*
			UseLastPolygonProxy();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Vertex functions
	//
	public:
		//
		// Get the number of vertices in the mesh
		//
		unsigned
			UseVertexArray(Stuff::DynamicArrayOf<Proxies::VertexProxy*> *vertices);

		//
		// vertex traversal
		//
		Proxies::VertexProxy*
			UseFirstVertexProxy();
		Proxies::VertexProxy*
			UseLastVertexProxy();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Local implementation details
	//
	public:
		ElementRenderer::ShapeElement*
			GetProxiedShape()
				{Check_Object(this); return proxiedShape;}
		unsigned
			GetPrimitiveCount()
				{Check_Object(this); return primitiveArray.GetLength();}
		MidLevelRenderer::MLR_I_PMesh*
			GetPrimitive(unsigned index)
				{Check_Object(this); return primitiveArray[index];}
		Proxies::PolygonProxy*
			GetPolygonProxy(
				int primitive_index,
				int polygon_index
			);
		Proxies::VertexProxy*
			GetVertexProxy(
				int primitive_index,
				int vertex_index
			);

		void
			LoadArrays();
		void
			SetPolyMeshArrays(
				MidLevelRenderer::MLR_I_L_PMesh *mesh,
				Stuff::DynamicArrayOf<Proxies::PolygonProxy*> &polygons,
				int polygon_count,
				Proxies::Process *process
			);
		void
			SetPolyMeshArrays(
				MidLevelRenderer::MLR_I_L_DT_PMesh *mesh,
				Stuff::DynamicArrayOf<Proxies::PolygonProxy*> &polygons,
				int polygon_count,
				Proxies::Process *process,
				bool isInverted
			);
		MLRPrimitiveBase* 
			CreateNewMesh(Proxies::MultiState* multiState);

	protected:
		ElementRenderer::ShapeElement
			*proxiedShape;
		MidLevelRenderer::MLRShape
			*mlrShape;
		Stuff::ChainIterator
			*siblingIterator;

		Stuff::DynamicArrayOf<MidLevelRenderer::MLR_I_PMesh*>
			primitiveArray;
		Stuff::DynamicArrayOf<int>
			vertexCountArray;
		Stuff::DynamicArrayOf<BYTE*>
			indexArray;
		Stuff::DynamicArrayOf<BYTE*>
			indexCountArray;
		Stuff::DynamicArrayOf<int>
			polygonCountArray;
		Stuff::DynamicArrayOf<Stuff::Point3D*>
			positionArray;
		Stuff::DynamicArrayOf<Stuff::Vector3D*>
			normalArray;
		Stuff::DynamicArrayOf<MidLevelRenderer::ColorType*>
			colorArray;
		Stuff::DynamicArrayOf<Stuff::Vector2DOf<Stuff::Scalar>*>
			uvArray;
		Stuff::MString m_name;
	};

}
