#pragma once
#define COMPOSITE_HPP

#if !defined(STUFF_STUFF_HPP)
	#include <Stuff\Stuff.hpp>
#endif


namespace Compost
{

enum {
	Current_Compost_Version = 6,
	Number_Of_Reserved_Textures = 50,
	MAX_NUMBER_LAYERS=64,
	Length_Of_Compost_Queue = 128,
	Length_Of_Compost_Queue_Tracker = 4	//	this value is (int)ln(Length_Of_Compost_Queue) + (fmod(ln(Length_Of_Compost_Queue))>0.0) ? 1 : 0
};

struct MaterialEntry
{
	Stuff::MString MatName;
	int darkEntryIndex;
	DWORD color;
};

extern MaterialEntry MaterialEntries[128];
extern int MaterialEntriesCount;
extern int DarkFactor;

inline DWORD
	DarkMachine(DWORD color)
{
	for(int i=0;i<MaterialEntriesCount;i++)
	{
		if(MaterialEntries[i].color == color)
		{
			return MaterialEntries[MaterialEntries[i].darkEntryIndex].color;
		}
	}
	return color;
}

inline int
	GetPow(int value)
	{
		if(value<=0)
		{
			return -1;
		}
		
		int count = (sizeof(value)-1)<<3;
		int mask = 1<<count;
		
		while(!(value&mask))
		{
			count--;
			mask>>=1;
		}
		
		return count;
	}
inline bool
	IsPowerOf2(int X)
	{
		return !( X & ( X - 1 ) );
	}

struct Feature_Texture :
		public Stuff::Plug
{
	Feature_Texture() : Plug (DefaultData) { data = NULL; flags = 0; dataSize = 0; }

	enum {
		TextureTypeBit = 0,
		TextureTypeBits = 3,
		TextureTypeMask =
			(0xFFFFFFFF >> (Stuff::INT_BITS - TextureTypeBits)) << TextureTypeBit,

		StatusBit = TextureTypeBit + TextureTypeBits,
		StatusBits = 1,
		StatusMask =  (0xFFFFFFFF >> (Stuff::INT_BITS - StatusBits)) << StatusBit,

		UsedBits = StatusBit + StatusBits,
		UsedMask = 0xFFFFFFFF >> (Stuff::INT_BITS - UsedBits)
	};

	enum TextureMode {
		NoMode = 0,
		FTT_555 = 1 << TextureTypeBit,
		FTT_4444 = 2 << TextureTypeBit,
		FTT_1 = 3 << TextureTypeBit,
		FTT_1_1 = 4 << TextureTypeBit,
		FTT_1_M = 5 << TextureTypeBit
	};

	enum StatusMode {
		NoStatus = 0,
		Loaded = 1 << StatusBit
	};

	void
		SetTextureMode(TextureMode mode)
			{ flags &= ~TextureTypeMask; flags |= mode; }
	TextureMode
		GetTextureMode() const
				{ return static_cast<TextureMode>(flags & TextureTypeMask); }

	void
		SetLoadedOn()
			{ flags |= Loaded; }
	void
		SetLoadedOff()
			{ flags &= ~Loaded; }
	bool
		GetLoadedMode() const
			{ return (flags & Loaded)!=0;}

	void
		SetTextureDepth(int d)
			{ Verify(d>0&&d<5); depth = d; }

	int
		RecalculateSize();

	int	width, height;
	int	width_mask, height_mask;
	
	int	depth;

	int	flags;
	void *handle;

	int dataSize;
	unsigned char	*data;

	virtual Stuff::MString* GetName() { return NULL; }
};

struct Tool_Feature_Texture :
		public Feature_Texture
{
	Stuff::MString name;

	virtual Stuff::MString* GetName() { return &name; }
};

extern void FetchTextureData(Feature_Texture*, int index, int);
extern void FetchTextureData(Feature_Texture*, const char *name, int);

extern void DestroyTextureData(Feature_Texture*);

class FeatureGrid;
class FeaturePool;
struct FeatureInstance;

class Feature {
	friend class FeatureGrid;
	friend class FeaturePool;
	friend struct FeatureInstance;
public:
	Feature()
		{
			flags = Paste;
			featureTexture = featureMask0 = featureMask1 = NULL; 
			feature_width = feature_height = 256;
			scaleXTexture=scaleXMask0=scaleXMask1=0;
			scaleZTexture=scaleZMask0=scaleZMask1=0;
		}
	virtual ~Feature() {} 

	enum {
		FlipBit = 0,
		FlipBits = 6,
		FlipMask =
			(0xFFFFFFFF >> (Stuff::INT_BITS - FlipBits)) << FlipBit,

		BlendBit = FlipBit + FlipBits,
		BlendBits = 8,
		BlendMask =  (0xFFFFFFFF >> (Stuff::INT_BITS - BlendBits)) << BlendBit,

		StretchBit = BlendBit + BlendBits,
		StretchBits = 1,
		StretchMask =  (0xFFFFFFFF >> (Stuff::INT_BITS - StretchBits)) << StretchBit,

		UsedBits = StretchBit + StretchBits,
		UsedMask = 0xFFFFFFFF >> (Stuff::INT_BITS - UsedBits)
	};

	enum Flip_Mode {
		Texture_Horizontal_Flip=1<<FlipBit,
		Texture_Vertical_Flip=2<<FlipBit,
		Mask0_Horizontal_Flip=4<<FlipBit,
		Mask0_Vertical_Flip=8<<FlipBit,
		Mask1_Horizontal_Flip=16<<FlipBit,
		Mask1_Vertical_Flip=32<<FlipBit
	};

	enum Blend_Mode {
		Paste=1<<BlendBit,
		Added=2<<BlendBit,
		Multiply=3<<BlendBit,
		Blend555_1=4<<BlendBit,			//	555 texture blended with one 8-bit mask
		Blend555_2=5<<BlendBit,			//	555 texture blended with two 8-bit masks
		Blend4444=6<<BlendBit,			//	4444 texture - alpha channel is mask
		StretchMultiply=7<<BlendBit,	//	texture is stretched across feature and multiplied to destination
		Blend555_1_1=8<<BlendBit,		//	555 texture blended with one 1-bit mask
		StretchPaste=9<<BlendBit,		//	555 texture streched across feature and pasted
		Blend555_1_S=10<<BlendBit		//	555 texture blended with one 8-bit mask of the size of the feature
	};

	enum Stretch_Mode {
		StretchOn=1<<StretchBit			//	this is tricky :) you can use odd-sized masks in this mode, 
	};									//	scale[XZ]Mask[01] will be calculated in a way that following equation is true
										//	mask_width * (1<<scale_mask) > feature_width, scale_mask is the smallest 
										//	possible value to make the equation true

	virtual Stuff::MString* GetName() { return NULL; }

	virtual int GetIndex() { return index; }

	void SetFlipMode (Flip_Mode mode) { flags |= mode; }
	void ClearFlipMode (Flip_Mode mode) { flags &= ~mode; }
	int GetFlipMode ()
		{ return (flags & FlipMask); }

	void SetBlendMode (Blend_Mode mode) { flags &= ~BlendMask; flags |= mode; }
	Blend_Mode GetBlendMode ()
		{ return static_cast<Blend_Mode>(flags & BlendMask); }
	static const char*
		GetBlendModeName(Blend_Mode);

	void SetStretchMode(bool b=true) { if(b) flags |= StretchOn; else flags &= ~StretchOn; }

	bool
		IsStretched() { return (flags & StretchOn)!=0; }

	int IsValidFeature();

	void SetFeatureTexture(Feature_Texture *texture) { featureTexture=texture; }
	void SetFeatureMask0(Feature_Texture *texture)
		{
			featureMask0 = texture;
			if(featureMask0)
			{
				SetXMask0Scale(GetPow(feature_width/featureMask0->width) + (IsPowerOf2(feature_width/featureMask0->width) ? 0 : 1));
				SetZMask0Scale(GetPow(feature_height/featureMask0->height) + (IsPowerOf2(feature_height/featureMask0->height) ? 0 : 1));
			}
		}
	void SetFeatureMask1(Feature_Texture *texture)
		{
			featureMask1 = texture;
			if(featureMask1)
			{
				SetXMask1Scale(GetPow(feature_width/featureMask1->width) + (IsPowerOf2(feature_width/featureMask1->width) ? 0 : 1));
				SetZMask1Scale(GetPow(feature_height/featureMask1->height) + (IsPowerOf2(feature_height/featureMask1->height) ? 0 : 1));
			}
		}

	Feature_Texture *GetFeatureTexture() { return featureTexture;}
	Feature_Texture *GetFeatureMask0()	{ return featureMask0;}
	Feature_Texture *GetFeatureMask1()	{ return featureMask1;}


	int GetFeatureWidth() { return feature_width; }
	int GetFeatureHeight() { return feature_height; }
	void SetFeatureWidth( unsigned short x) {feature_width=x;}
	void SetFeatureHeight( unsigned short z) {feature_height=z;}

	int GetXTextureScale () { return scaleXTexture; }
	int GetZTextureScale () { return scaleZTexture; }

	void SetXTextureScale(int s) { scaleXTexture = s; }
	void SetZTextureScale(int s) { scaleZTexture = s; }

	void SetXMask0Scale(int s) { scaleXMask0 = s; }
	void SetZMask0Scale(int s) { scaleZMask0 = s; }
	int GetXMask0Scale () { return scaleXMask0; }
	int GetZMask0Scale () { return scaleZMask0; }

	void SetXMask1Scale(int s) { scaleXMask1 = s; }
	void SetZMask1Scale(int s) { scaleZMask1 = s; }
	int GetXMask1Scale () { return scaleXMask1; }
	int GetZMask1Scale () { return scaleZMask1; }

	void ReCalculateScale()
	{
			if(featureMask0)
			{
				SetXMask0Scale(GetPow(feature_width/featureMask0->width) + (IsPowerOf2(feature_width/featureMask0->width) ? 0 : 1));
				SetZMask0Scale(GetPow(feature_height/featureMask0->height) + (IsPowerOf2(feature_height/featureMask0->height) ? 0 : 1));
			}

			if(featureMask1)
			{
				SetXMask1Scale(GetPow(feature_width/featureMask1->width) + (IsPowerOf2(feature_width/featureMask1->width) ? 0 : 1));
				SetZMask1Scale(GetPow(feature_height/featureMask1->height) + (IsPowerOf2(feature_height/featureMask1->height) ? 0 : 1));
			}
	}
protected:	
//	position of texture with feature in pixel
	int	feature_width, feature_height;

//	scale of texture in x and z, we just store the power of 2
	int	scaleXTexture, scaleZTexture;

//	scale of mask0 in x and z, we just store the power of 2
	int	scaleXMask0, scaleZMask0;

//	scale of mask1 in x and z, we just store the power of 2
	int scaleXMask1, scaleZMask1;

	Feature_Texture *featureTexture;
	
	Feature_Texture *featureMask0;
	Feature_Texture *featureMask1;

	int flags;
	int index;
};


class Tool_Feature :
		public Feature
{
	friend class FeatureGrid;
	friend class FeaturePool;
	friend struct FeatureInstance;
	protected:
		Stuff::MString name;
		Feature_Texture *HFTexture;
		DWORD material;
	public:
		int HFBLendMode,BlendVal;
		unsigned char StatFlags;
		Tool_Feature() : Feature () {HFTexture=NULL; StatFlags=0; HFBLendMode=0; BlendVal=128; material=0;}
		virtual ~Tool_Feature() {} 

		void SetHFTexture(Feature_Texture *texture) { HFTexture=texture; }
		Feature_Texture *GetHFTexture()	{ return HFTexture;}

		virtual Stuff::MString* GetName() { return &name; }
		void SetName(const char* _name) { name = _name; index = name.GetHashValue(); }

		void SetMaterial(int mat) { material = mat; }
		int GetMaterial () { return material; }

		virtual int GetIndex() { return name.GetHashValue(); }
};

	void InitializeClasses(Stuff::NotationFile *startup_ini = NULL);
	void TerminateClasses(Stuff::NotationFile *startup_ini=NULL);

	extern HGOSHEAP Heap;

	extern DWORD Number_Of_Worked_On_Textures;
	DECLARE_TIMER(extern, Composting_Time);
	extern DWORD Number_Of_Used_128_Textures;
	extern DWORD Number_Of_Used_256_Textures;

#define BOUNDARY_TEST

}
