/*	Renegade Scripts.dll
	D3DXEffect Wrapper and related loaders
	Copyright 2007 Jonathan Wilson, Mark Sararu

	This file is part of the Renegade scripts.dll
	The Renegade scripts.dll is free software; you can redistribute it and/or modify it under
	the terms of the GNU General Public License as published by the Free
	Software Foundation; either version 2, or (at your option) any later
	version. See the file COPYING for more details.
	In addition, an exemption is given to allow Run Time Dynamic Linking of this code with any closed source module that does not contain code covered by this licence.
	Only the source code to the module(s) containing the licenced code has to be released.
*/
#include "scripts.h"
#include "shadereng.h"
#include "resourcemanager.h"
#include "shaderstatemanager.h"
#include "effect.h"

DebugOutputClass *EffectClassDebug;

EffectClass::EffectClass()
{
	NumRefs = 0;
	Name = 0;
	D3DEffect = 0;
	Initialized = false;
	Add_Ref();
}
EffectClass::EffectClass(char *name)
{
	NumRefs = 0;
	Name = newstr(name);
	D3DEffect = 0;
	Initialized = false;
	Add_Ref();
}
EffectClass::~EffectClass()
{
	SAFE_DELETE_ARRAY(Name);
}
void EffectClass::Delete_This()
{
	if (D3DEffect)
	{
		D3DEffect->Release();
	}
	D3DEffect = 0;
	delete this;
}
HRESULT EffectClass::Init()
{
	EffectLoadTask = new EffectLoadTaskClass(this);
	ResourceFactory->EnqueueTask(EffectLoadTask,RESOURCELOADPRIORITY_NORMAL);
	return S_OK;
}
HRESULT EffectClass::ForceReset()
{
	SAFE_DELETE(EffectLoadTask);
	EffectLoadTask = new EffectLoadTaskClass(this);
	Initialized = false;
	if (D3DEffect)
	{
		D3DEffect->OnLostDevice();
		D3DEffect->Release();
	}
	D3DEffect = 0;
	ResourceFactory->ProcessTaskImmediate(EffectLoadTask);
	delete EffectLoadTask;
	EffectLoadTask = 0;
	return S_OK;
}
HRESULT EffectClass::Begin(unsigned int *passes, DWORD flags)
{
	if (!Initialized)
	{
		*passes = 1;
		return S_OK;
	}
	
	return D3DEffect->Begin(passes,flags | D3DXFX_DONOTSAVESTATE);
}

HRESULT EffectClass::BeginPass(unsigned int pass)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->BeginPass(pass);
}

HRESULT EffectClass::CommitChanges()
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->CommitChanges();
}

HRESULT EffectClass::EndPass()
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->EndPass();
}

HRESULT EffectClass::End()
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->End();
}

HRESULT EffectClass::OnDeviceLost()
{	
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->OnLostDevice();
}

HRESULT EffectClass::OnDeviceReset()
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->OnResetDevice();
}

HRESULT EffectClass::ValidateTechnique(D3DXHANDLE technique)
{
	if (!Initialized)
	{
		return D3DERR_INVALIDCALL;
	}
	return D3DEffect->ValidateTechnique(technique);
}

HRESULT EffectClass::SetTechnique(D3DXHANDLE parameter)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetTechnique(parameter);
}
HRESULT EffectClass::SetBool(D3DXHANDLE parameter, BOOL value)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetBool(parameter, value);
}
HRESULT EffectClass::SetBoolArray(D3DXHANDLE parameter, const BOOL* values, unsigned int count)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetBoolArray(parameter, values, count);
}
HRESULT EffectClass::SetFloat(D3DXHANDLE parameter, float value)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetFloat(parameter,value);
}
HRESULT EffectClass::SetFloatArray(D3DXHANDLE parameter, const float* values, unsigned int count)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetFloatArray(parameter, values, count);
}
HRESULT EffectClass::SetInt(D3DXHANDLE parameter, int value)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetInt(parameter, value);
}
HRESULT EffectClass::SetIntArray(D3DXHANDLE parameter, const int* values, unsigned int count)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetIntArray(parameter, values, count);
}
HRESULT EffectClass::SetMatrix(D3DXHANDLE parameter, const Matrix4* value)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetMatrix(parameter,(D3DXMATRIX*) value);

}
HRESULT EffectClass::SetMatrixArray(D3DXHANDLE parameter, const Matrix4* values, unsigned int count)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetMatrixArray(parameter,(D3DXMATRIX*) values, count);

}
HRESULT EffectClass::SetMatrixPointerArray (D3DXHANDLE parameter, const Matrix4** values, unsigned int count)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetMatrixPointerArray(parameter,(const D3DXMATRIX**) values, count);
}
HRESULT EffectClass::SetMatrixTranspose(D3DXHANDLE parameter, const Matrix4* value)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetMatrixTranspose(parameter,(D3DXMATRIX*) value);
}
HRESULT EffectClass::SetMatrixTransposeArray(D3DXHANDLE parameter, const Matrix4* values, unsigned int count)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetMatrixTransposeArray(parameter,(D3DXMATRIX*) values, count);
}
HRESULT EffectClass::SetMatrixTransposePointerArray (D3DXHANDLE parameter, const Matrix4** values, unsigned int count)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetMatrixTransposePointerArray(parameter,(const D3DXMATRIX**) values, count);
}
HRESULT EffectClass::SetString(D3DXHANDLE parameter, const char* value)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetString(parameter, value);
}
HRESULT EffectClass::SetTexture(D3DXHANDLE parameter, IDirect3DBaseTexture9* value)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetTexture(parameter, value);
}
HRESULT EffectClass::SetValue(D3DXHANDLE parameter, const void* data, unsigned int bytes)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetValue(parameter, data, bytes);
}
HRESULT EffectClass::SetVector(D3DXHANDLE parameter, const Vector4* value)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetVector(parameter, (D3DXVECTOR4*)value);
}
HRESULT EffectClass::SetVectorArray(D3DXHANDLE parameter, const Vector4* values, unsigned int count)
{
	if (!Initialized)
	{
		return S_OK;
	}
	return D3DEffect->SetVectorArray(parameter, (D3DXVECTOR4*)values, count);
}
D3DXHANDLE EffectClass::GetAnnotation(D3DXHANDLE object, unsigned int index)
{
	if (!Initialized)
	{
		return newstr("");
	}
	return D3DEffect->GetAnnotation(object,index);
}
D3DXHANDLE EffectClass::GetAnnotationByName(D3DXHANDLE object, const char* name)
{
	if (!Initialized)
	{
		return newstr("");
	}
	return D3DEffect->GetAnnotationByName(object,name);
}
HRESULT EffectClass::GetBool(D3DXHANDLE parameter, BOOL* value)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetBool(parameter,value);
}
HRESULT EffectClass::GetBoolArray(D3DXHANDLE parameter, BOOL* values, unsigned int count)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetBoolArray(parameter,values,count);
}
HRESULT EffectClass::GetDesc(D3DXEFFECT_DESC* desc)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetDesc(desc);
}
HRESULT EffectClass::GetFloat(D3DXHANDLE parameter, float* value)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetFloat(parameter,value);
}
HRESULT EffectClass::GetFloatArray(D3DXHANDLE parameter, float* values, unsigned int count)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetFloatArray(parameter,values,count);
}
D3DXHANDLE EffectClass::GetFunction(unsigned int index)
{
	if (!Initialized)
	{
		return newstr("");
	}
	return D3DEffect->GetFunction(index);
}
D3DXHANDLE EffectClass::GetFunctionByName(const char* name)
{
	if (!Initialized)
	{
		return newstr("");
	}
	return D3DEffect->GetFunctionByName(name);
}
HRESULT EffectClass::GetFunctionDesc(D3DXHANDLE shader, D3DXFUNCTION_DESC* desc)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetFunctionDesc(shader,desc);
}
HRESULT EffectClass::GetInt(D3DXHANDLE parameter, int* value)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetInt(parameter,value);
}
HRESULT EffectClass::GetIntArray(D3DXHANDLE parameter, int* values, unsigned int count)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetIntArray(parameter,values,count);
}
HRESULT EffectClass::GetMatrix(D3DXHANDLE parameter, Matrix4* value)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetMatrix(parameter,(D3DXMATRIX *)value);
}
HRESULT EffectClass::GetMatrixArray(D3DXHANDLE parameter, Matrix4* values, unsigned int count)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetMatrixArray(parameter,(D3DXMATRIX *)values,count);
}
HRESULT EffectClass::GetMatrixPointerArray(D3DXHANDLE parameter, Matrix4** values, unsigned int count)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetMatrixPointerArray(parameter,(D3DXMATRIX **)values,count);
}
HRESULT EffectClass::GetMatrixTransposeArray(D3DXHANDLE parameter, Matrix4* values, unsigned int count)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetMatrixTransposeArray(parameter,(D3DXMATRIX *)values,count);
}
HRESULT EffectClass::GetMatrixTransposePointerArray(D3DXHANDLE parameter, Matrix4** values, unsigned int count)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetMatrixTransposePointerArray(parameter,(D3DXMATRIX **)values,count);
}
D3DXHANDLE EffectClass::GetParameter(D3DXHANDLE parameter, unsigned int index)
{
	if (!Initialized)
	{
		return newstr("");
	}
	return D3DEffect->GetParameter(parameter,index);
}
D3DXHANDLE EffectClass::GetParameterByName(D3DXHANDLE parameter, const char *name)
{
	if (!Initialized)
	{
		return newstr("");
	}
	return D3DEffect->GetParameterByName(parameter,name);
}
D3DXHANDLE EffectClass::GetParameterBySemantic(D3DXHANDLE parameter, const char* semantic)
{
	if (!Initialized)
	{
		return newstr("");
	}
	return D3DEffect->GetParameterBySemantic(parameter,semantic);
}
HRESULT EffectClass::GetParameterDesc(D3DXHANDLE parameter, D3DXPARAMETER_DESC* desc)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetParameterDesc(parameter,desc);
}
D3DXHANDLE EffectClass::GetParameterElement(D3DXHANDLE parameter, unsigned int index)
{
	if (!Initialized)
	{
		return newstr("");
	}
	return D3DEffect->GetParameterElement(parameter,index);
}
D3DXHANDLE EffectClass::GetPass(D3DXHANDLE technique, unsigned int index)
{
	if (!Initialized)
	{
		return newstr("");
	}
	return D3DEffect->GetPass(technique,index);
}
D3DXHANDLE EffectClass::GetPassByName(D3DXHANDLE technique, const char* name)
{
	if (!Initialized)
	{
		return newstr("");
	}
	return D3DEffect->GetPassByName(technique,name);
}
HRESULT EffectClass::GetString(D3DXHANDLE parameter, const char** string)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetString(parameter,string);
}
D3DXHANDLE EffectClass::GetTechnique(unsigned int index)
{
	if (!Initialized)
	{
		return newstr("");
	}
	return D3DEffect->GetTechnique(index);
}
D3DXHANDLE EffectClass::GetTechniqueByName(const char* name)
{
	if (!Initialized)
	{
		return newstr("");
	}
	return D3DEffect->GetTechniqueByName(name);
}
HRESULT EffectClass::GetTechniqueDesc(D3DXHANDLE technique, D3DXTECHNIQUE_DESC* desc)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetTechniqueDesc(technique,desc);
}
HRESULT EffectClass::GetTexture(D3DXHANDLE parameter, IDirect3DBaseTexture9** value)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetTexture(parameter,value);
}
HRESULT EffectClass::GetValue(D3DXHANDLE parameter, void* data, unsigned int bytes)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetValue(parameter,data,bytes);
}
HRESULT EffectClass::GetVector(D3DXHANDLE parameter, Vector4* value)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetVector(parameter,(D3DXVECTOR4*) value);
}
HRESULT EffectClass::GetVectorArray(D3DXHANDLE parameter, Vector4* values, unsigned int count)
{
	if (!Initialized)
	{
		return E_FAIL;
	}
	return D3DEffect->GetVectorArray(parameter,(D3DXVECTOR4*) values, count);
}

void EffectLoadTaskClass::LoadResource()
{
	EffectClass *effect = (EffectClass*) Data;
	if (effect->Initialized)
	{
		return;
	}
	FileClass *file = Get_Data_File(effect->Name);
	char *effectData; 
	int effectData_size;
	if ((file) && (file->Open(1)))
	{
		effectData_size = file->Size();
		effectData = new char[effectData_size];
		file->Read(effectData,effectData_size);
		file->Close();
		Close_Data_File(file);
	}
	else 
	{
		EffectClassDebug->Error(DEBUGLEVEL_ERROR,"[%s] Failed to open required effect definition file.\n",effect->Name);
		effect->EffectLoadTask = 0;
		return;
	}
	LPD3DXBUFFER bufferErrors = NULL;
	EffectIncludeFileClass *include = new EffectIncludeFileClass;
#ifndef DEBUG
	HRESULT res = D3DXCreateEffect(Direct3DDevice,effectData,effectData_size,NULL,include,D3DXFX_NOT_CLONEABLE,NULL,&effect->D3DEffect,&bufferErrors);
#else
	HRESULT res = D3DXCreateEffect(Direct3DDevice,effectData,effectData_size,NULL,include,D3DXFX_NOT_CLONEABLE|D3DXSHADER_DEBUG,NULL,&effect->D3DEffect,&bufferErrors);
#endif
	delete[] effectData;
	delete include;
	effectData_size = 0;
	if (FAILED(res) && bufferErrors)
	{
		WCHAR wsz[256];
		MultiByteToWideChar(CP_ACP,0,(LPSTR)bufferErrors->GetBufferPointer(), -1, wsz,256);
		wsz[255] = 0;
		EffectClassDebug->Error(DEBUGLEVEL_ERROR,"[%s] Failed to load. Errors follow this message.\n %S",effect->Name,wsz);
		SAFE_RELEASE(bufferErrors);
		effect->EffectLoadTask = 0;
		effect->D3DEffect = NULL;
		return;
	}
	SAFE_RELEASE(bufferErrors);
	effect->D3DEffect->SetStateManager(StateManager);
	effect->EffectLoadTask = 0;
	effect->Initialized = true;
}

HRESULT EffectIncludeFileClass::Open(D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes)
{
	File = Get_Data_File((char *)pFileName);
	char *data;
	int data_size;
	if ((File) && (File->Open(1)))
	{
		data_size = File->Size();
		data = new char[data_size];
		File->Read(data,data_size);
		File->Close();
		Close_Data_File(File);
		*ppData = data;
		*pBytes = data_size;
		return S_OK;
	}
	else 
	{
		EffectClassDebug->Trace(DEBUGLEVEL_ERROR,"[%s] Failed to open required include file.\n",pFileName);
		return E_FAIL;
	}
}
HRESULT EffectIncludeFileClass::Close(LPCVOID pData)
{
	delete[] pData;
	return S_OK;
}
