#pragma once

#include "Compost.hpp"
#include "FeaturePool.hpp"


namespace Compost
{

struct FeatureInstance {

	unsigned char StatFlags,Tag;	

	FeatureInstance() {
		feature = NULL; feature_x_pos = feature_z_pos = 0; 
		texture_x_pos = texture_z_pos = 0;		
		currentTexturePtr = NULL; currentMask0Ptr = NULL;  currentMask1Ptr = NULL;
		StatFlags=0;
		Tag=0;
	}

	void
		LoadInstance(
			Stuff::MemoryStream *stream,
			FeaturePool*,
			int version
		);
	void
		SaveInstance(
			Stuff::MemoryStream *stream, int xoffset=0, int zoffset=0
		);

	void
		LoadInstance(
			Stuff::Page *inst_page,
			FeaturePool*,
			int version
		);
	void
		SaveInstance(
			Stuff::Page *inst_page,int xoffset=0,int zoffset=0
		);

	bool
		CalculateCurrentPtr(int, int, int=0);

	int GetTextureXPos() {return texture_x_pos;}
	int GetTextureZPos() {return texture_z_pos;}
	void SetTextureXPos( unsigned short x) {texture_x_pos=x;}
	void SetTextureZPos( unsigned short z) {texture_z_pos=z;}

//	void
//		AdvanceCurrentPtr();

	Feature *feature;

	FeatureInstance&
		operator=(FeatureInstance&);

//	position in pixel %256(&0xff) = position in tile, /256(>>8) tile
	int	feature_x_pos, feature_z_pos;
	int texture_x_pos, texture_z_pos;
	int position;

	unsigned char *currentTexturePtr;
	int countDownToBorder;

	unsigned char *currentMask0Ptr;
	int mask0_x_off, mask0_z_off;

	unsigned char *currentMask1Ptr;
	int mask1_x_off, mask1_z_off;
};

struct EdgeListPlug {
	EdgeListPlug();
	~EdgeListPlug();

	bool
		AddUp(FeatureInstance*, int row, int column, int gridSize=256);

	int x_pos_start;
	int x_pos_end;
	int z_pos;
	int count;

	FeatureInstance *featureInstance;

	bool key;

	EdgeListPlug *next;
	EdgeListPlug *prev;
};

struct ActiveEdgeListPlug {
	ActiveEdgeListPlug () { elp = NULL; counter = 0; next = NULL; prev = NULL; }

	EdgeListPlug *elp;

	int counter;

	ActiveEdgeListPlug *next;
	ActiveEdgeListPlug *prev;
};

struct TextureHolder 
{
	TextureHolder() {
		handle = -1; textureHandle = -1; priority = NULL; flags = Free; lastLine = -1;
		elp = NULL; nextLineElp = NULL;
		aelp = NULL; aelp_ptr = NULL; taelp = NULL;
		pix_dest = NULL;
#ifdef LAB_ONLY
		borderKey = false; borderColor = 0;
#endif
#ifdef _ARMOR
		queuePosition = -1;
#endif
#ifdef _DEBUG
		row = -1;
		column = -1;
#endif
	}

	bool
		ShutDown();

	int handle;
	
	int textureHandle;

	BYTE *priority;

	enum TextureHolderState {
		Free = 0,
		InWork = 1,
		Done = 2
	};
	int flags;

	int lastLine;
	int linesToProcess;

	EdgeListPlug *elp;
	EdgeListPlug *nextLineElp;

	ActiveEdgeListPlug *aelp;
	ActiveEdgeListPlug *aelp_ptr;
	ActiveEdgeListPlug *taelp;

	unsigned char *pix_dest;
	int pitch;

#ifdef _ARMOR
	int	queuePosition;
#endif
#ifdef LAB_ONLY
	DWORD borderColor;
	bool borderKey;
#endif
#ifdef _DEBUG
	int	row, column;
#endif
};

class TerrainTextureLogistic;

class FeatureGrid
{
	friend class TerrainTextureLogistic;

	public:
		FeatureGrid(int, int, FeaturePool*, bool=false);
		FeatureGrid(Stuff::MemoryStream *stream, FeaturePool*);
		FeatureGrid(Stuff::NotationFile *note_file, FeaturePool*);

		~FeatureGrid();

		void
			LoadGrid(
				Stuff::MemoryStream *stream
			);
		void
			SaveGrid(
				Stuff::MemoryStream *stream,int xoffset=0,int zoffset=0
			);

		void
			LoadGrid(
				Stuff::NotationFile *stream
			);
		void
			SaveGrid(
				Stuff::NotationFile *stream,int xoffset=0,int zoffset=0
			);

		FeatureInstance *
			Add(Feature*, unsigned short x_pos, unsigned short z_pos);
		void
			Remove(FeatureInstance*);

		void
			RedoFeaturePosition();

		bool
			ComposeGrid(int row, int column, unsigned char *dest, int downSample=0, int type=0);

		bool
			ComposeGrid(int row, int column, TextureHolder*, int downSample=0, int type=0);

		int
			GetRows() 
				{ return rows; }

		int
			GetColumns() 
				{ return columns; }
		
		Stuff::DynamicArrayOf<FeatureInstance*>*
			GetGridElement(int row, int column)
				{ return &featureInstance[row*columns+column]; }

		int
			ShootRay(unsigned short x, unsigned short z);

		int
			ShootRectangle(
				Stuff::DynamicArrayOf<FeatureInstance*>& list,
				unsigned short x, 
				unsigned short z,
				unsigned short width,
				unsigned short height
			);

		static unsigned char depthColors[16][3];

		void
			LoadGridFromUnique();
		void
			LoadGridFromUnique(int row, int column);
		
		void
			LoadEdgeListFromGrid();

		FeatureInstance* 
			GetCollectedNr(int nr)
				{ return rayCollect[nr]; }

		int 
			CountFeature(FeatureInstance *fI) { return CountFeature(fI->feature); }
		int 
			CountFeature(Feature*);

		const Stuff::DynamicArrayOf<FeatureInstance*> *
			GetUniqueFeatureInstances(int& count)
				{ count = used; return &uniqueFeatureInstance; }
		int 
			GetGridSize() {return gridSize;}

		int 
			GetGridShift() {return gridSizeShift;}

	protected:
		int
			gridSize, gridSizeShift;

		inline void
			AddToRayCollect(FeatureInstance*, unsigned short&);
		inline void
			DeleteFromRayCollect(FeatureInstance*, unsigned short&);
		inline void
			AddToActiveEdgeList(ActiveEdgeListPlug**, EdgeListPlug*);

		static void	Blend555_1(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_1_TH(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_1_M0H(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_1_TH_M0H(FeatureInstance*, unsigned char*, int, int, int=0);

		static void	Blend555_2(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_2_TH(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_2_M0H(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_2_TH_M0H(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_2_M1H(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_2_TH_M1H(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_2_M0H_M1H(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_2_TH_M0H_M1H(FeatureInstance*, unsigned char*, int, int, int=0);

		static void	BlendSM(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	BlendSM_M0H(FeatureInstance*, unsigned char*, int, int, int=0);

		static void	Blend4444(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend4444_TH(FeatureInstance*, unsigned char*, int, int, int=0);

		static void	Blend555_1_1(FeatureInstance*, unsigned char*, int, int, int, int, int=0);
		static void	Blend555_1_1_TH(FeatureInstance*, unsigned char*, int, int, int, int, int=0);
		static void	Blend555_1_1_M0H(FeatureInstance*, unsigned char*, int, int, int, int, int=0);
		static void	Blend555_1_1_TH_M0H(FeatureInstance*, unsigned char*, int, int, int, int, int=0);

		static void	Blend555_1_S(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_1_S_TH(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_1_S_M0H(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_1_S_TH_M0H(FeatureInstance*, unsigned char*, int, int, int=0);

		static void	BlendSS(FeatureInstance*, unsigned char*, int, int, int=0);

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

		static void	Blend555_1_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_1_TH_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_1_M0H_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_1_TH_M0H_MAT(FeatureInstance*, unsigned char*, int, int, int=0);

		static void	Blend555_2_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_2_TH_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_2_M0H_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_2_TH_M0H_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_2_M1H_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_2_TH_M1H_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_2_M0H_M1H_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_2_TH_M0H_M1H_MAT(FeatureInstance*, unsigned char*, int, int, int=0);

		static void	BlendSM_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	BlendSM_M0H_MAT(FeatureInstance*, unsigned char*, int, int, int=0);

		static void	Blend4444_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend4444_TH_MAT(FeatureInstance*, unsigned char*, int, int, int=0);

		static void	Blend555_1_1_MAT(FeatureInstance*, unsigned char*, int, int, int, int, int=0);
		static void	Blend555_1_1_TH_MAT(FeatureInstance*, unsigned char*, int, int, int, int, int=0);
		static void	Blend555_1_1_M0H_MAT(FeatureInstance*, unsigned char*, int, int, int, int, int=0);
		static void	Blend555_1_1_TH_M0H_MAT(FeatureInstance*, unsigned char*, int, int, int, int, int=0);

		static void	Blend555_1_S_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_1_S_TH_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_1_S_M0H_MAT(FeatureInstance*, unsigned char*, int, int, int=0);
		static void	Blend555_1_S_TH_M0H_MAT(FeatureInstance*, unsigned char*, int, int, int=0);

		static void	BlendSS_MAT(FeatureInstance*, unsigned char*, int, int, int=0);

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

		int rows, columns;
		FeaturePool* featurePool;

		FeatureInstance* rayCollect[MAX_NUMBER_LAYERS];

		int max, used;
		Stuff::DynamicArrayOf<FeatureInstance*> uniqueFeatureInstance;
		Stuff::DynamicArrayOf<FeatureInstance*> *featureInstance;
		Stuff::DynamicArrayOf<EdgeListPlug*> edgeList;
};

}