#pragma once

#include "dllinterface.h"
#include "..\Utilities\util.h"
#include "..\Utilities\list.h"
#include "..\Utilities\XString.h"
#include "DXVector4.h"
#include "DXMatrix.h"
#include "DXTexture.h"

#include "PixelShader.h"
#include "VertexShader.h"
#include "HLPixelShader.h"
#include "HLVertexShader.h"
#include "ShaderEffect.h"
#include "EffectCompiler.h"

class DeviceStateManager;

struct DXFUNCTIONS_API SHADERPARAMETER
{
	D3DXHANDLE Handle;
	bool bPtr; //when true the value pointer points to an external buffer
	void* Value;
	unsigned ByteCount;

	SHADERPARAMETER(void)
	{
		ZeroMemory(this, sizeof(SHADERPARAMETER));
	}

	SHADERPARAMETER(const D3DXHANDLE handle, void* value, const unsigned bytecount, const bool bptr)
	{
		Handle = handle;
		if(bptr)
		{
			Value = value;
		}
		else
		{
			Value = new BYTE[bytecount];
			if(value)
				memcpy(Value, value, bytecount);
		}
		ByteCount = bytecount;
		bPtr = bptr;
	}

	SHADERPARAMETER(const SHADERPARAMETER& shparam)
	{
		Handle = shparam.Handle;
		Value = new BYTE[shparam.ByteCount];
		memcpy(Value, shparam.Value, shparam.ByteCount);
		ByteCount = shparam.ByteCount;
		bPtr = false;
	}

	~SHADERPARAMETER()
	{
		if(!bPtr)
			delete [] Value;
	}
	
	void operator = (const SHADERPARAMETER& shparam)
	{
		if(shparam.bPtr)
			SetValuePtr(shparam.Handle, shparam.Value, shparam.ByteCount);
		else
			SetValue(shparam.Handle, shparam.Value, shparam.ByteCount);
	}

	bool operator == (const SHADERPARAMETER& shparam) const
	{
		if(Handle == shparam.Handle && ByteCount == shparam.ByteCount)
		{
			return memcmp(Value, shparam.Value, ByteCount) == 0;
		}
		return false;
	}

	void SetValue(const D3DXHANDLE handle, const void* const value, const unsigned bytecount)
	{
		if(!bPtr)
			delete [] Value;
		Handle = handle;
		Value = new BYTE[bytecount];
		if(value)
			memcpy(Value, value, bytecount);
		ByteCount = bytecount;
		bPtr = false;
	}

	void SetValuePtr(const D3DXHANDLE handle, void* const value, const unsigned bytecount)
	{
		if(!bPtr)
			delete [] Value;
		Handle = handle;
		Value = value;
		ByteCount = bytecount;
		bPtr = true;
	}

	void UpdateValue(void* const pvalue)
	{
		if(!bPtr)
			memcpy(Value, pvalue, ByteCount);
		else
			Value = pvalue;
	}
};

//TODO: this part has to be rewritten for compied HL Vertex and Pixel shader
/*class DXFUNCTIONS_API CompiledEffect : public ShaderProgram
{
	friend EffectCompiler;

	EffectCompiler* m_Compiler;

protected:
	friend EffectCompiler;
	friend ListElement<CompiledEffect>;
	friend LList<CompiledEffect>;

	struct LITERALSET
	{
		D3DXHANDLE Handle;
		bool Literal;

		LITERALSET() {}
		LITERALSET(const LITERALSET& ls) { Handle = ls.Handle; Literal = ls.Literal;}
		LITERALSET(const D3DXHANDLE handle, const bool lit) { Handle = handle; Literal = lit; }
		~LITERALSET(){}

		bool operator == (const LITERALSET& lit) const { return Handle == lit.Handle && Literal == lit.Literal; }
	};

	LList<LITERALSET> m_Literals;
	LList<SHADERPARAMETER> m_Params;

	CompiledEffect(EffectCompiler* const compiler);
	~CompiledEffect(void);

	//this functions record the configuration of the compiler and also filter overwriting set calls
	void AddParamSet(SHADERPARAMETER* const param);
	void AddLiteralSet(LITERALSET* const litset);

	

public:
	//set values for compiler, compile and restore old values
	void Compile(bool debug = false);
};

class DXFUNCTIONS_API ShaderCompiler : public ShaderProgram
{
	friend ShaderManager;

	LPD3DXEFFECTCOMPILER m_Compiler;

	LList<ShaderProgram> m_Shader;

	//this records all setliterals and setvalue calls
	CompiledEffect* m_NextPS;
	CompiledEffect* m_NextVS;

protected:

	friend CompiledEffect;

	EffectCompiler(ShaderManager* const manager, const XString& fn);
	void ReleaseSH(void);

	//direct access not granted because setvalue calls have to recorded to compile effects properly
	LPD3DXEFFECTCOMPILER GetCompiler(void) { return m_Compiler; }

	void ReleaseEffect(CompiledEffect* const fx);

public:
	//recompiles all effects
	void Compile(bool debug = false);
	//XString GetDisassembly(void);

	//record setliteral and setvalue calls and also ensure to add previouse values before set calls to already created effects
	void SetLiteral(const D3DXHANDLE h, const bool lit);
	void SetValue(const D3DXHANDLE h, const void* const pvalue, const unsigned bytecount);
  
	//compiles a shader with current literal set
	CompiledHLPS* CompilePS(const bool debug);
	CompiledHLVS* CompileVS(const bool debug);
};*/

class DXFUNCTIONS_API ShaderManager
{
	friend ShaderProgram;
	friend PixelShader;
	friend VertexShader;

	DeviceStateManager *m_Device;
	LList<ShaderProgram> m_Shaders;

	void Release(ShaderProgram*sh);

public:
	ShaderManager(DeviceStateManager*device);
	~ShaderManager(void);

	VertexShader* CreateVS(const XString& fn);
	PixelShader* CreatePS(const XString& fn);
	HLVertexShader* CreateHLVS(const XString& fn, const XString& profile);
	HLPixelShader* CreateHLPS(const XString& fn, const XString& profile);
	ShaderEffect* CreateFX(const XString& fn);
	EffectCompiler* CreateFXCompiler(const XString& fn);

	const LList<ShaderProgram>& GetShaders(void) const {return m_Shaders;}
	DeviceStateManager* GetDevice(void) const {return m_Device;}

	void Restore(void);
	void Invalidate(void);

	void ReloadShader(const bool debug);
};