#include "shadermanager.h"
#include "devicestatemanager.h"

/*CompiledEffect::CompiledEffect(EffectCompiler* const compiler) : ShaderEffect(compiler->GetManager(), "")
{
	m_Compiler = compiler;
}

CompiledEffect::~CompiledEffect(void)
{
	m_Literals.Clear();
	m_Params.Clear();
}

void CompiledEffect::AddParamSet(SHADERPARAMETER* const param)
{ 
	FOREACH(shp, m_Params, SHADERPARAMETER)
	{
		if(shp->GetInfo()->Handle == param->Handle)
		{
			delete shp->GetInfo();
			shp->SetInfo(param);
		}
	}

	m_Params.AddTail(param, 0); 
}

void CompiledEffect::AddLiteralSet(LITERALSET* const litset)
{
	FOREACH(lit, m_Literals, LITERALSET)
	{
		if(lit->GetInfo()->Handle == litset->Handle)
		{
			delete lit->GetInfo();
			lit->SetInfo(litset);
		}
	}

	m_Literals.AddTail(litset, 0); 
}

void CompiledEffect::Compile(bool debug)
{
	if(!m_Compiler)
		return;

	LPD3DXBUFFER pEffect;
	LPD3DXBUFFER pErrors = NULL;

	DWORD flags = 0;
	if(debug)
		flags |= D3DXSHADER_SKIPOPTIMIZATION | D3DXSHADER_DEBUG;

	LList<LITERALSET> LiteralsRestore;
	LList<SHADERPARAMETER> ParamsRestore;

	FOREACH(shp, m_Params, SHADERPARAMETER)
	{
		SHADERPARAMETER* const p = shp->GetInfo();
		SHADERPARAMETER* const prestore = new SHADERPARAMETER(*p);
		m_Compiler->GetCompiler()->GetValue(prestore->Handle, prestore->Value, prestore->ByteCount);
		m_Compiler->GetCompiler()->SetValue(p->Handle, p->Value, p->ByteCount);
	}

	FOREACH(lit, m_Literals, LITERALSET)
	{
		LITERALSET* const p = lit->GetInfo();
		LITERALSET* const prestore = new LITERALSET();
		prestore->Handle = p->Handle;
		m_Compiler->GetCompiler()->GetLiteral(prestore->Handle, (BOOL*)&(prestore->Literal));
		m_Compiler->GetCompiler()->SetLiteral(p->Handle, p->Literal);
	}

	HRESULT hr = m_Compiler->GetCompiler()->CompileEffect(flags, &pEffect, &pErrors);

	m_Shader = (LPD3DXEFFECT)pEffect->GetBufferPointer();

	if(!FAILED(hr))
	{
		m_Compiled = true;
        m_ErrorMsgs = "Compilation Successful!";
	}
	else
	{
		m_Compiled = false;
		if(pErrors)
			m_ErrorMsgs = (char*)pErrors->GetBufferPointer();
		else
			m_ErrorMsgs = "ERROR: Unknown Error!";
	}

	SAFERELEASE(pErrors);
	SAFERELEASE(pEffect);

	FOREACH(shp, ParamsRestore, SHADERPARAMETER)
	{
		SHADERPARAMETER* const p = shp->GetInfo();
		m_Compiler->GetCompiler()->SetValue(p->Handle, p->Value, p->ByteCount);
	}

	FOREACH(lit, LiteralsRestore, LITERALSET)
	{
		LITERALSET* const p = lit->GetInfo();
		m_Compiler->GetCompiler()->SetLiteral(p->Handle, p->Literal);
	}

	ParamsRestore.Clear();
	LiteralsRestore.Clear();
}

EffectCompiler::EffectCompiler(ShaderManager* const manager, const XString& fn) : ShaderProgram(manager, SH_CMP, fn)
{
	m_Compiler = NULL;
	m_NextEffect = NULL;

	m_NextEffect = new CompiledEffect(this);

	Open("");
}

void EffectCompiler::ReleaseSH(void)
{
	delete m_NextEffect;
	m_NextEffect = NULL;

	m_Effects.Clear();
}

void EffectCompiler::ReleaseEffect(CompiledEffect* const fx)
{
	ListElement<CompiledEffect> *el = m_Effects.GetElement((DWORD)fx);
	if(el)
		el->Delete();
}

void EffectCompiler::SetLiteral(const D3DXHANDLE h, const bool lit)
{ 
	if(m_NextEffect) 
		m_NextEffect->AddLiteralSet(new CompiledEffect::LITERALSET(h, lit)); 
}

void EffectCompiler::SetValue(const D3DXHANDLE h, const void* const pvalue, const unsigned bytecount)
{ 
	if(m_NextEffect) 
		m_NextEffect->AddParamSet(new SHADERPARAMETER(h, pvalue, bytecount)); 
}

void EffectCompiler::Compile(bool debug)
{
	SAFERELEASE(m_Compiler);

	m_Compiled = false;

	if(!m_Manager)
		return;

	if(m_Code.getlen() == 0)
	{
		m_ErrorMsgs = "ERROR: File not found or empty!";
		return;
	}

	LPD3DXBUFFER pErrors = NULL;

	DWORD flags = 0;
	if(debug) 
		flags |= D3DXSHADER_DEBUG | D3DXSHADER_SKIPOPTIMIZATION;

	HRESULT hr = D3DXCreateEffectCompiler(m_Code.getbuffer(), m_Code.getlen(), NULL, NULL, flags, &m_Compiler, &pErrors);

	if(!FAILED(hr) && m_Compiler)
	{
		m_Compiled = true;
        m_ErrorMsgs = "Compilation Successful!";
	}
	else
	{
		if(pErrors)
			m_ErrorMsgs = (char*)pErrors->GetBufferPointer();
		else
			m_ErrorMsgs = "ERROR: Unknown Error!";
	}

	SAFERELEASE(pErrors);
}


CompiledHLPS* CompilePS(const bool debug);
	CompiledHLVS* CompileVS(const bool debug);


CompiledHLPS* EffectCompiler::CompilePS(const bool debug)
{
	if(!m_Compiler && !m_NextEffect)
		return NULL;

	//try to find effect with same values and literals set
	FOREACH(el, m_Effects, CompiledEffect)
	{
		bool sameparam = false;
		bool samelit = false;

		CompiledEffect* fx = el->GetInfo();

		if(fx->m_Params.GetElementCount() == m_NextEffect->m_Params.GetElementCount() && fx->m_Params.GetElementCount() > 0)
		{
			sameparam = true;

			FOREACH(shp, fx->m_Params, SHADERPARAMETER)
			{
				
				bool found = false;
				FOREACH(neshp, m_NextEffect->m_Params, SHADERPARAMETER)
				{
					if(*(shp->GetInfo()) == *(neshp->GetInfo()))
					{
						found = true;
						break;
					}
				}
				if(!found)
				{
					sameparam = false;
					break;
				}
			}
		}

		if(fx->m_Literals.GetElementCount() == m_NextEffect->m_Literals.GetElementCount() && fx->m_Literals.GetElementCount() > 0)
		{
			samelit = true;

			FOREACH(lit, fx->m_Literals, CompiledEffect::LITERALSET)
			{
				
				bool found = false;
				FOREACH(nelit, m_NextEffect->m_Literals, CompiledEffect::LITERALSET)
				{
					if(*(lit->GetInfo()) == *(nelit->GetInfo()))
					{
						found = true;
						break;
					}
				}
				if(!found)
				{
					samelit = false;
					break;
				}
			}
		}

		if(sameparam && samelit)
		{
			el->GetInfo()->IncRefCount();
			return (CompiledEffect*)el->GetInfo();
		}
	}

	CompiledEffect *sh = m_NextEffect;
	sh->Compile(debug);
	m_Effects.AddHead(new ListElement<CompiledEffect>(sh, (DWORD)sh));

	//create new effect to record further set calls and copy current seted values
	m_NextEffect = new CompiledEffect(this);
	m_NextEffect->m_Literals.CopyDataFrom(sh->m_Literals);
	m_NextEffect->m_Params.CopyDataFrom(sh->m_Params);

	return sh;
}*/

ShaderManager::ShaderManager(DeviceStateManager*device)
{
	m_Device = device;
}

ShaderManager::~ShaderManager()
{
	m_Shaders.Clear();
}

VertexShader* ShaderManager::CreateVS(const XString& fn)
{
	ListElement<ShaderProgram> *el=m_Shaders.GetHead();
	while(el)
	{
		if(el->GetInfo()->GetType() == SH_ASMVS && el->GetInfo()->GetFilename() == fn)
		{
			el->GetInfo()->IncRefCount();
			return (VertexShader*)el->GetInfo();
		}
		el = el->GetNext();
	}
	VertexShader *sh = new VertexShader(this, fn);
	m_Shaders.AddHead(new ListElement<ShaderProgram>(sh,(DWORD)sh));
	return sh;
}

PixelShader* ShaderManager::CreatePS(const XString& fn)
{
	ListElement<ShaderProgram> *el=m_Shaders.GetHead();
	while(el)
	{
		if(el->GetInfo()->GetType() == SH_ASMPS && el->GetInfo()->GetFilename() == fn)
		{
			el->GetInfo()->IncRefCount();
			return (PixelShader*)el->GetInfo();
		}
		el = el->GetNext();
	}
	PixelShader *sh = new PixelShader(this,fn);
	m_Shaders.AddHead(new ListElement<ShaderProgram>(sh,(DWORD)sh));
	return sh;
}

HLVertexShader* ShaderManager::CreateHLVS(const XString& fn, const XString& profile)
{
	ListElement<ShaderProgram> *el=m_Shaders.GetHead();
	while(el)
	{
		if(el->GetInfo()->GetType() == SH_HLVS && el->GetInfo()->GetFilename() == fn)
		{
			el->GetInfo()->IncRefCount();
			return (HLVertexShader*)el->GetInfo();
		}
		el = el->GetNext();
	}
	HLVertexShader *sh = new HLVertexShader(this,fn,profile);
	m_Shaders.AddHead(new ListElement<ShaderProgram>(sh,(DWORD)sh));
	return sh;
}

HLPixelShader* ShaderManager::CreateHLPS(const XString& fn, const XString& profile)
{
	ListElement<ShaderProgram> *el=m_Shaders.GetHead();
	while(el)
	{
		if(el->GetInfo()->GetType() == SH_HLPS && el->GetInfo()->GetFilename() == fn)
		{
			el->GetInfo()->IncRefCount();
			return (HLPixelShader*)el->GetInfo();
		}
		el = el->GetNext();
	}
	HLPixelShader *sh = new HLPixelShader(this,fn,profile);
	m_Shaders.AddHead(new ListElement<ShaderProgram>(sh,(DWORD)sh));
	return sh;
}

ShaderEffect* ShaderManager:: CreateFX(const XString& fn)
{
	ListElement<ShaderProgram> *el=m_Shaders.GetHead();
	while(el)
	{
		if(el->GetInfo()->GetType() == SH_FX && el->GetInfo()->GetFilename() == fn)
		{
			el->GetInfo()->IncRefCount();
			return (ShaderEffect*)el->GetInfo();
		}
		el = el->GetNext();
	}
	ShaderEffect *sh = new ShaderEffect(this,fn);
	m_Shaders.AddHead(new ListElement<ShaderProgram>(sh,(DWORD)sh));
	return sh;
}

EffectCompiler* ShaderManager::CreateFXCompiler(const XString& fn)
{
	ListElement<ShaderProgram> *el=m_Shaders.GetHead();
	while(el)
	{
		if(el->GetInfo()->GetType() == SH_CMP && el->GetInfo()->GetFilename() == fn)
		{
			el->GetInfo()->IncRefCount();
			return (EffectCompiler*)el->GetInfo();
		}
		el = el->GetNext();
	}
	EffectCompiler *sh = new EffectCompiler(this, fn);
	m_Shaders.AddHead(new ListElement<ShaderProgram>(sh,(DWORD)sh));
	return sh;
}

void ShaderManager::Release(ShaderProgram*sh)
{
	ListElement<ShaderProgram> *el=m_Shaders.GetElement((DWORD)sh);
	if(el)
	{
		el->Unlink(); //shaderprogramm already delete
		delete el;
	}
}

void ShaderManager::Invalidate(void)
{
	FOREACH(el, m_Shaders, ShaderProgram)
	{
		el->GetInfo()->Invalidate();
		//if(el->GetInfo()->GetType() == SH_FX && el->GetInfo()->IsCompiled())
		//	((ShaderEffect*)el->GetInfo())->GetShader()->OnLostDevice();
	}
}

void ShaderManager::Restore(void)
{
	FOREACH(el, m_Shaders, ShaderProgram)
	{
		el->GetInfo()->Restore();
		//if(el->GetInfo()->GetType() == SH_FX && el->GetInfo()->IsCompiled())
		//	((ShaderEffect*)el->GetInfo())->GetShader()->OnResetDevice();
	}
}

void ShaderManager::ReloadShader(const bool debug)
{
	//CClock asdf;
	//asdf.StartCapture(0);

	FOREACH(el, m_Shaders, ShaderProgram)
	{
		if (el->GetInfo()->GetFilename().getlen () > 0)
		{
			el->GetInfo()->Reload();
			//el->GetInfo()->Open(el->GetInfo()->GetFilename());
			//el->GetInfo()->Compile(false);
		}
	}

	//asdf.EndCapture(0);
	//asdf.GetCaptureTime(0);

	//double x = asdf.GetCaptureTime(0) * 1000.0 / (double)asdf.GetTicksPerSec();

	//wlog("ReloadShader: %fms", (float)x);
}