#pragma once
#define MLR_MLRSORTER_HPP

#include <MLR\MLR.hpp>
#include <MLR\MLRLight.hpp>
#include <MLR\GOSVertexPool.hpp>
#include <MLR\MLRClippingState.hpp>

namespace MidLevelRenderer {

	class MLRPrimitive;
	class MLREffect;
	class GOSVertex;
	class DrawScreenQuadsInformation;

	struct SortAlpha;

	class SortData {
	public:
		SortData () 
			{ vertices = NULL; numVertices = 0; indices = NULL; 
			  numIndices = 0; type = TriList; texture_2_3 = 0; }

		void DrawTriList();
		void DrawTriIndexedList();
		void DrawPointCloud();
		void DrawLineCloud();
		void DrawQuads();

		int LoadAlphaFromTriList(MLRSortByOrder*, int, Stuff::Scalar);
		int LoadAlphaFromTriIndexedList(MLRSortByOrder*, int, Stuff::Scalar);
		int LoadAlphaFromPointCloud(MLRSortByOrder*, int, Stuff::Scalar);
		int LoadAlphaFromLineCloud(MLRSortByOrder*, int, Stuff::Scalar);
		int LoadAlphaFromQuads(MLRSortByOrder*, int, Stuff::Scalar);

		enum {
			TriList = 0,
			TriIndexedList,
			PointCloud,
			Quads,
			LineCloud,
			LastMode
		};

		typedef void (SortData::* DrawFunc)();
		typedef int (SortData::* LoadSortAlphaFunc)(MLRSortByOrder*, int, Stuff::Scalar);

		static DrawFunc Draw[LastMode];
		static LoadSortAlphaFunc LoadSortAlpha[LastMode];

		MLRStateBase state;
		DWORD texture_2_3;
		void *vertices;
		int	numVertices;
		WORD *indices;
		int numIndices;
		int type;
	};


	class MLRPrimitiveBase;


#define CalDraw	


#ifdef CalDraw
	struct ToBeDrawnPrimitive {
		ToBeDrawnPrimitive();

		void *primitive;

		MLRStateBase state;
		Stuff::Point3D cameraPosition;
		Stuff::Normal3D cameraDirection;
		MLRClippingState clippingFlags;
		Stuff::Matrix4D shapeToClipMatrix;
		Stuff::LinearMatrix4D worldToShape;

		static GOSVertexPool *allVerticesToDraw;

		MLRLight* activeLights[Limits::Max_Number_Of_Lights_Per_Primitive];
		int	nrOfActiveLights;

		ColorType paintMeColor;

		int type;
	};

	struct ToBeDrawnPrimitiveIndex {
		int tbdpIndex;
		DWORD handle;
	};
#endif

	//##########################################################################
	//#########################    MLRSorter    ################################
	//##########################################################################

	class _declspec(novtable) MLRSorter :
		public Stuff::RegisteredClass
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Initialization
	//
	public:
		static void
			InitializeClass();
		static void
			TerminateClass();
		static ClassData
			*DefaultData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Constructors/Destructors
	//
	public:
		MLRSorter(ClassData *class_data, MLRTexturePool*);
		~MLRSorter();

		virtual void AddPrimitive(MLRPrimitiveBase*, int=0) = 0;
		virtual void AddEffect(MLREffect*, const MLRStateBase&) = 0;
		virtual void AddEffect(MLREffect*, const MLRStateBase&, int&) = 0;

		virtual void AddScreenQuads(GOSVertex*, const DrawScreenQuadsInformation*) = 0;

		virtual void AddSortRawData(int) = 0;

		virtual void
			DrawPrimitive(MLRPrimitiveBase*, int=0);

		void
			SetTexturePool(MLRTexturePool *tp)
				{ Check_Object(this); Check_Pointer(tp); texturePool = tp; }

		bool
			SetDifferences(const MLRStateBase& original, const MLRStateBase& newer, DWORD tex2=0, DWORD tex3=0)
			{
				return SetDifferences(original.renderState, newer.renderState, tex2, tex3);
			}

		bool
			SetDifferences(const DWORD original, const DWORD newer, DWORD tex2, DWORD tex3);

		bool
			SetDifferences(const MLRStateBase& newer, DWORD tex2=0, DWORD tex3=0)
			{
				bool ret = SetDifferences(theCurrentState, newer.renderState, tex2, tex3);
				theCurrentState = newer.renderState;

				return ret;
			}

		bool
			SetDifferences(const DWORD newer, DWORD tex2, DWORD tex3)
			{
				bool ret = SetDifferences(theCurrentState, newer, tex2, tex3);
				theCurrentState = newer;

				return ret;
			}

	//	starts the action
		virtual void RenderNow () = 0;

	//	resets the sorting
		virtual void Reset ();

	//	lets begin the dance
		virtual void StartDraw(const MLRState &default_state);

	//	enter raw data
		int
			SetRawData
				(	void *vertices, 
					int numVertices,
					const MLRStateBase& state,
					const int& mode,
					int tex2 = 0
				);

		int
			SetRawIndexedData
				(	void* vertices, 
					int numVertices, 
					WORD *indices,
					int numIndices,
					const MLRStateBase& state, 
					const int& mode,
					int tex2 = 0
				);

		int
			SetRawData(MLRPrimitiveBase*, int=0 );

//	Just scaling down a bit to keep z under 1.0f	
		void
			SetFarClipReciprocal(Stuff::Scalar fcr)
				{ Check_Object(this); farClipReciprocal = fcr*(1.0f-Stuff::SMALL); }

#ifdef CalDraw
		ToBeDrawnPrimitive*
			GetCurrentTBDP()
				{ Check_Object(this); if(lastUsedDraw>=Limits:: Max_Number_Primitives_Per_Frame) return NULL; else return &(*drawData)[lastUsedDraw]; }
		ToBeDrawnPrimitive*
			GetCurrentTBDP(int index)
				{ Check_Object(this); if(lastUsedDraw>=Limits:: Max_Number_Primitives_Per_Frame) return NULL; else return &(*drawData)[index]; }
		void
			IncreaseTBDPCounter();
#endif

		bool
			CompareToCurrentState(const MLRState &state)
				{ Check_Object(this); return theCurrentState==state.renderState; }

		void
			SetCurrentState()
				{ Check_Object(this); MLRStateBase state; state.renderState = theCurrentState; state.SetRendererState(texturePool); }

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

		static maxUsedAlphaVertex, lastUsedAlphaVertex, maxUsedAlphaBumpVertex, lastUsedAlphaBumpVertex;

		static Stuff::DynamicArrayOf<GOSVertex>  //	3*Max_Number_Primitives_Per_Frame + Max_Number_ScreenQuads_Per_Frame
			*alphaVertices;
		static Stuff::DynamicArrayOf<GOSVertex3UV>  //	Max_Number_Primitives_Per_Frame + Max_Number_ScreenQuads_Per_Frame
			*alphaBumpVertices;

	protected:

		static Stuff::DynamicArrayOf<SortData> *rawDrawData; // Max_Number_Primitives_Per_Frame
		static int maxUsedRaw, lastUsedRaw;
		static int lastUsedInBucketNotDrawn[MLRState::PriorityCount];

#ifdef CalDraw
		static int maxUsedDraw, lastUsedDraw, maxUsedInBucket;
		static Stuff::DynamicArrayOf<ToBeDrawnPrimitive> *drawData; // Max_Number_Primitives_Per_Frame
		static Stuff::DynamicArrayOf<ToBeDrawnPrimitiveIndex>  //, Max_Number_Primitives_Per_Frame + Max_Number_ScreenQuads_Per_Frame
			*priorityBucketsNotDrawn[MLRState::PriorityCount];
#endif

	protected:
		DWORD theCurrentState;
		MLRTexturePool *texturePool;


		Stuff::Scalar
			farClipReciprocal;
	};

}
