/*	Renegade Scripts.dll
	Main code for shaders.dll implementation
	Copyright 2007 Jonathan Wilson

	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 "shader.h"
#include "shadermgr.h"
#include "shader_scene.h"
#include "coreshader.h"
#include "shaderstatemanager.h"
#include "shaderplugin.h"
#include "shaderplugin_impl.h"

CoreShaderClass *DefaultShader;
bool ShaderHooks = false;
bool RenderScreenFade = true;
extern bool pluginsRequired;
extern char *MapTexture;
extern Vector2 *MapOffset;
extern DebugOutputClass *EffectClassDebug;
extern bool ShaderCheckMaterial;
ScriptNotify scriptnotify;

#pragma comment(lib, "DelayImp.lib")
#pragma comment(lib, "d3dx9.lib")
#if D3DX_SDK_VERSION != 30
#error The Renegade shaders.dll requires the DirectX August 2006 SDK installed in order to produce a proper executable. If you wish to use a different version of the SDK, modify the delay load dll in linker settings to point to the newer d3dx.dll
#endif

ShaderCommandClass::ShaderCommandClass()
{
	InterfaceVersion = SHADER_INTERFACEVERSION;	
}

void ShaderCommandClass::RegisterPlugin(ShaderPluginClass *plugin)
{
	ShaderPluginNotify->RegisterPlugin(plugin);
}

void ShaderCommandClass::GetStateManager(ShaderStateManager **statemanager)	
{
	if (statemanager != NULL)
	{
		if (StateManager)
		{
			*statemanager = StateManager;
		}
		else
		{
			*statemanager = NULL;
		}
	}
}

void ShaderCommandClass::SendCustom(unsigned int eventid, int parameter)
{
	scriptnotify(eventid,parameter);
}

void *ShaderCommandClass::AllocateMemory(size_t reportedSize)	
{
	return AllocMemory(reportedSize);
}

void ShaderCommandClass::DeallocateMemory(void *reportedAddress)
{
	DeallocMemory(reportedAddress);
}

void ShaderCommandClass::ConsoleOutput(const char *output,...)
{
	va_list args;
	char buffer[1024];
	va_start(args,output);
	vsprintf(buffer,output,args);
	va_end(args);
	Console_Output(buffer);
}

void ShaderCommandClass::ConsoleInput(const char *input)
{
	Console_Input(input);
}

void ShaderCommandClass::AddCombatMessage(const char *message, unsigned int red, unsigned int green, unsigned int blue)
{
	::AddCombatMessage(message, red, green, blue);
}

DefaultShaderPluginClass::DefaultShaderPluginClass()
{
	this->InterfaceVersion = SHADERPLUG_INTERFACEVERSION;	
}

HRESULT DefaultShaderPluginClass::OnReleaseResources()
{
	StateManager->Reset();
	ResetDeclerationBuffer();
	if (ShaderHooks)
	{
		TheShaderManager->Release_Resources();
		SceneShaderController->OnDeviceLost();
	}
	return S_CONTINUE;
}

HRESULT DefaultShaderPluginClass::OnReloadResources()
{
	if (ShaderHooks)
	{
		TheShaderManager->Reload_Resources();
		SceneShaderController->OnDeviceReset();
	}
	return S_CONTINUE;
}

HRESULT DefaultShaderPluginClass::OnDestroyResources()
{
	ResetDeclerationBuffer();
	StateManager->SetTexture(0,NULL);
	StateManager->SetTexture(1,NULL);
	if (ShaderHooks)
	{
		TheShaderManager->Release_Resources();
		TheShaderManager->Unload_Database();
		SceneShaderController->OnDeviceLost();
		SceneShaderController->UnloadDatabase();
		StateManager->SetVertexShader(NULL);
		StateManager->SetPixelShader(NULL);
	}
	return S_CONTINUE;
}

void ReadMapINI();
HRESULT DefaultShaderPluginClass::OnMapLoad()
{
	if (ShaderHooks)
	{
		char *c = newstr(The_Game()->MapName);
		char *c2 = strchr(c,'.');
		if (c2)
		{
			c2[1] = 's';
			c2[2] = 'd';
			c2[3] = 'b';
		}
		FileClass *f = Get_Data_File(c);
		if (f)
		{
			f->Open(1);
			TheShaderManager->Load_Map_Database(f);
			Close_Data_File(f);
		}
		TheShaderManager->Reset_Cache();
		delete[] c;
	}
	ReadMapINI();
	return S_CONTINUE;
}	

HRESULT DefaultShaderPluginClass::OnMapUnload()
{
	if (ShaderHooks)
	{
		TheShaderManager->Unload_Map_Database();
	}
	return S_CONTINUE;
}	

HRESULT DefaultShaderPluginClass::OnCustom(unsigned int eventid, Vector4 parameter)
{
	return S_CONTINUE;
}

HRESULT DefaultShaderPluginClass::OnCustom(unsigned int eventid, float parameter)
{
	if (ShaderHooks)
	{
		SceneShaderController->OnCustom(eventid,parameter);
	}
	return S_CONTINUE;
}

HRESULT DefaultShaderPluginClass::OnFrameStart()
{
	SceneShaderController->OnFrameStart();
	return S_CONTINUE;
}

HRESULT DefaultShaderPluginClass::OnFrameEnd()
{
	SceneShaderController->OnFrameEnd();
	return S_CONTINUE;
}


HRESULT DefaultShaderPluginClass::OnScreenFadeRender()
{
	if (RenderScreenFade)
	{
		(*ScreenFade)->Render();
	}
	return S_CONTINUE;
}

HRESULT DefaultShaderPluginClass::OnRender(unsigned int primitive_type, unsigned short start_index, unsigned short polygon_count, unsigned short min_vertex_index, unsigned short vertex_count)
{
	if (ShaderHooks && !pluginsRequired)
	{
		if (ShaderCheckMaterial)
		{
			if ((render_state->material) && (render_state->material->Name))
			{
				TheShaderManager->Set_Current_Name(render_state->material->Name);
			}
			else
			{
				TheShaderManager->Set_Current_Name("dummy");
			}
		}
		else
		{
			if ((render_state->Textures[0]) && (render_state->Textures[0]->Name))
			{
				TheShaderManager->Set_Current_Name(render_state->Textures[0]->Name);
			}
			else
			{
				TheShaderManager->Set_Current_Name("dummy");
			}
		}
		if (!TheShaderManager->Render(primitive_type,start_index,polygon_count,min_vertex_index,vertex_count))
		{
			DefaultShader->Render(primitive_type,start_index,polygon_count,min_vertex_index,vertex_count);
		}
	}
	else
	{
		DefaultShader->Render(primitive_type,start_index,polygon_count,min_vertex_index,vertex_count);
	}
	return S_OK;
}

const unsigned char Code[19] = {0x6A,0x40,0xFF,0x74,0x24,0x0C,0xFF,0x74,0x24,0x0C,0xE8,0xC0,0xFF,0xFF,0xFF,0x83,0xC4,0x0C,0xC3};
BOOL TryLoadD3DX()
{
	__try
	{
		BOOL test = D3DXCheckVersion(D3D_SDK_VERSION, D3DX_SDK_VERSION); // will cause an exception if the dll is missing
		test;
		return TRUE;
	}
	__except (EXCEPTION_EXECUTE_HANDLER) 
	{
		return FALSE;
	}
}

extern "C"
{
BOOL __declspec(dllexport) __stdcall DllMain(HINSTANCE hinstDLL,
						DWORD	ul_reason_for_call,
						LPVOID	lpReserved
					)
{
	switch (ul_reason_for_call)
	{
		FileClass *f;
		case DLL_PROCESS_ATTACH:
			if(!TryLoadD3DX())
			{
			 	FILE *f2 = fopen("dllload.txt","at");
				fprintf(f2,"[shaders.dll] D3DX not found, unable to continue. Please update your version of DirectX using the web updater.\n");
				MessageBox(NULL,"You need to update your version of DirectX in order to use this copy of scripts.dll.","Error",MB_OK|MB_ICONEXCLAMATION|MB_TOPMOST);
				fclose(f2);
				ExitProcess(1);
			}
			ResourceFactory = new ResourceLoaderClass();
			DisableThreadLibraryCalls(hinstDLL);
			if (!memcmp((void *)0x0078CE49,(void *)Code,19))
			{
				if (Exe == 6)
				{
					InitEngine();
				}
				if ((ShaderCaps::VertexShaderVersion) && (ShaderCaps::PixelShaderVersion))
				{
					ShaderHooks = Get_Registry_Int("Shaders",1);
				}
				else
				{
					ShaderHooks = false;
				}
				InitShaderPluginSystem();
				DefaultShader = new CoreShaderClass();
				MapTexture = new char[260];
				MapOffset = new Vector2(0,0);
				if (ShaderHooks)
				{
					EffectClassDebug = new DebugOutputClass("_shader.txt");
					if (!TheShaderManager)
					{
						TheShaderManager = new ShaderManagerClass();
					}
					f = Get_Data_File("shaders.sdb");
					if (f)
					{
						f->Open(1);
						TheShaderManager->Load_Database(f);
						Close_Data_File(f);
					}
					SceneShaderController = new SceneShaderControllerClass();
					f = Get_Data_File("sceneshaders.sdb");
					if (f)
					{
						f->Open(1);
						SceneShaderController->LoadDatabase(f);
						SceneShaderController->IntializeShaders();
						SceneShaderController->OnCustom(EVENT_SETSHADER,1);
						Close_Data_File(f);
					}
				}
			}
			break;
		case DLL_PROCESS_DETACH:
			DestroyShaderPluginSystem();
			SAFE_DELETE(SceneShaderRegistrar)
			SAFE_DELETE(TheShaderManager)
			SAFE_DELETE(ResourceFactory)
			SAFE_DELETE(SceneShaderController)
			SAFE_DELETE(EffectClassDebug);
			delete[] MapTexture;
			delete MapOffset;
			delete DefaultShader;
			DestroyEngine3D();
			DestroyEngineMath();
#if DEBUG
			m_dumpMemoryReport("memreport_shaders.log",false);
#endif
			break;
	}
	return TRUE;
}

void __declspec(dllexport) DrawSkin(char *fvfcc)
{
	Draw_Skin(fvfcc);
}

void __declspec(dllexport) DrawRigid(char *fvfcc,char *MeshClass)
{
	Draw_Rigid(fvfcc,MeshClass);
}

void __declspec(dllexport) SetScriptNotify(ScriptNotify notify)
{
	scriptnotify = notify;
}

}
