/*	Renegade Scripts.dll
	Implementation of the shader plugin interface, do not use outside of shaders.dll
	Copyright 2007 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 "shader_scene.h"
#include "shaderplugin.h"
#include "shaderplugin_impl.h"

#ifndef SHADERS_EXPORTS
#error This file is not designed to be used outside of shaders.dll
#endif

ShaderCommandClass *ShaderCommands = 0;
ShaderPluginNotifyClass *ShaderPluginNotify = 0;

void InitShaderPluginSystem()
{
	ShaderCommands = new ShaderCommandClass();
	ShaderPluginNotify = new ShaderPluginNotifyClass();
	ShaderPluginNotify->RegisterPlugin(new DefaultShaderPluginClass());
}

void DestroyShaderPluginSystem()
{
	delete ShaderCommands;
	delete ShaderPluginNotify;
}

ShaderPluginNotifyClass::ShaderPluginNotifyClass()
{
}

ShaderPluginNotifyClass::~ShaderPluginNotifyClass()
{
	for (int i = 0; i < Plugins.Count(); i++)
	{
		if (Plugins[i])
		{
			delete Plugins[i];
			Plugins[i] = 0;
		}
	}
	for (int i = 0; i < PluginHandles.Count(); i++)
	{
		if (PluginHandles[i])
		{
			FreeLibrary(PluginHandles[i]);
			PluginHandles[i] = 0;
		}
	}
	Plugins.Delete_All();
	PluginHandles.Delete_All();
}

bool ShaderPluginNotifyClass::RegisterPlugin(ShaderPluginClass *plugin)
{
	if (HIWORD(plugin->InterfaceVersion) == HIWORD(SHADERPLUG_INTERFACEVERSION))
	{
		if (LOWORD(plugin->InterfaceVersion) <= LOWORD(SHADERPLUG_INTERFACEVERSION))
		{
			Plugins.Add(plugin);
			return true;
		}
	}
	return false;
}

bool ShaderPluginNotifyClass::LoadPluginFromFile(const char *pluginName)
{
	if (GetFileAttributes(pluginName) != 0xFFFFFFFF) // plugin exists
	{
		HMODULE plugin;
		plugin = LoadLibrary(pluginName);
		if (!plugin)
		{
			DWORD error = GetLastError();
			FILE *f = fopen("pluginload.txt","at");
			if (f)
			{
				char errorMessage[2048];
				FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,error,0,errorMessage,2048,NULL);
				fprintf(f,"[%s] Failed to load, error %d. %s",pluginName,error,errorMessage);
				fclose(f);
			}
			return false;
		}
		
		_SetShaderCommands SetShaderCommands = (_SetShaderCommands) GetProcAddress(plugin,"SetShaderCommands");
		_GetPluginInterface GetPluginInterface = (_GetPluginInterface) GetProcAddress(plugin,"GetPluginInterface");
		
		if (!SetShaderCommands | !GetPluginInterface)
		{
			DWORD error = GetLastError();
			FILE *f = fopen("pluginload.txt","at");
			if (f)
			{
				char errorMessage[2048];
				FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,error,0,errorMessage,2048,NULL);
				fprintf(f,"[%s] Failed to get one or more required function addresses, error %d. %s",pluginName,error,errorMessage);
				fclose(f);
			}
			FreeLibrary(plugin);
			return false;
		}
		SetShaderCommands(ShaderCommands);

		ShaderPluginClass *pluginInterface = NULL;
		GetPluginInterface(&pluginInterface);
		if (pluginInterface)
		{
			bool res = RegisterPlugin(pluginInterface);
			if (res)
			{
				PluginHandles.Add(plugin);
				return true;
			}
			else
			{
				FreeLibrary(plugin);
				return false;
			}
		}
		else
		{
			FILE *f = fopen("pluginload.txt","at");
			if (f)
			{
				fprintf(f,"[%s] Plugin does not expose the plugin interface.\n",pluginName);
				fclose(f);
			}
			FreeLibrary(plugin);
			return false;
		}

	}
	else
	{
		return false;
	}
}

void ShaderPluginNotifyClass::OnReleaseResources()
{
	HRESULT r = S_CONTINUE;
	for (int i = Plugins.Count() - 1; (i >= 0) && (r == S_CONTINUE); i--)
	{
		if (Plugins[i])
		{
			r = Plugins[i]->OnReleaseResources();
		}
	}
}

void ShaderPluginNotifyClass::OnReloadResources()
{
	HRESULT r = S_CONTINUE;
	for (int i = Plugins.Count() - 1; (i >= 0) && (r == S_CONTINUE); i--)
	{
		if (Plugins[i])
		{
			r = Plugins[i]->OnReloadResources();
		}
	}
}

void ShaderPluginNotifyClass::OnDestroyResources()
{
	HRESULT r = S_CONTINUE;
	for (int i = Plugins.Count() - 1; (i >= 0) && (r == S_CONTINUE); i--)
	{
		if (Plugins[i])
		{
			r = Plugins[i]->OnDestroyResources();
		}
	}
}

void ShaderPluginNotifyClass::OnMapLoad()
{
	HRESULT r = S_CONTINUE;
	for (int i = Plugins.Count() - 1; (i >= 0) && (r == S_CONTINUE); i--)
	{
		if (Plugins[i])
		{
			r = Plugins[i]->OnMapLoad();
		}
	}
}

void ShaderPluginNotifyClass::OnMapUnload()
{
	HRESULT r = S_CONTINUE;
	for (int i = Plugins.Count() - 1; (i >= 0) && (r == S_CONTINUE); i--)
	{
		if (Plugins[i])
		{
			r = Plugins[i]->OnMapUnload();
		}
	}
}

void ShaderPluginNotifyClass::OnCustom(unsigned int eventid, Vector4 parameter)
{
	HRESULT r = S_CONTINUE;
	for (int i = Plugins.Count() - 1; (i >= 0) && (r == S_CONTINUE); i--)
	{
		if (Plugins[i])
		{
			r = Plugins[i]->OnCustom(eventid,parameter);
		}
	}
}

void ShaderPluginNotifyClass::OnCustom(unsigned int eventid, float parameter)
{
	HRESULT r = S_CONTINUE;
	for (int i = Plugins.Count() - 1; (i >= 0) && (r == S_CONTINUE); i--)
	{
		if (Plugins[i])
		{
			r = Plugins[i]->OnCustom(eventid,parameter);
		}
	}
}

void ShaderPluginNotifyClass::OnFrameStart()
{
	HRESULT r = S_CONTINUE;
	for (int i = Plugins.Count() - 1; (i >= 0) && (r == S_CONTINUE); i--)
	{
		if (Plugins[i])
		{
			r = Plugins[i]->OnFrameStart();
		}
	}
}

void ShaderPluginNotifyClass::OnFrameEnd()
{
	HRESULT r = S_CONTINUE;
	for (int i = Plugins.Count() - 1; (i >= 0) && (r == S_CONTINUE); i--)
	{
		if (Plugins[i])
		{
			r = Plugins[i]->OnFrameEnd();
		}
	}
}

void ShaderPluginNotifyClass::OnScreenFadeRender()
{
	HRESULT r = S_CONTINUE;
	for (int i = Plugins.Count() - 1; (i >= 0) && (r == S_CONTINUE); i--)
	{
		if (Plugins[i])
		{
			r = Plugins[i]->OnScreenFadeRender();
		}
	}
}

void ShaderPluginNotifyClass::OnRender(unsigned int primitive_type, unsigned short start_index, unsigned short polygon_count, unsigned short min_vertex_index, unsigned short vertex_count)
{
	HRESULT r = S_CONTINUE;
	for (int i = Plugins.Count() - 1; (i >= 0) && (r == S_CONTINUE); i--)
	{
		if (Plugins[i])
		{
			r = Plugins[i]->OnRender(primitive_type,start_index,polygon_count,min_vertex_index,vertex_count);
		}
	}
}

extern "C"
{
void __declspec(dllexport) Release_Resources()
{
	ShaderPluginNotify->OnReleaseResources();
}

void __declspec(dllexport) Reload_Resources()
{
	ShaderPluginNotify->OnReloadResources();
}

void __declspec(dllexport) Destroy_Resources()
{
	ShaderPluginNotify->OnDestroyResources();
}

void __declspec(dllexport) MapLoaded()
{
	CalculateTangents = true;
	ShaderPluginNotify->OnMapLoad();
}
void __declspec(dllexport) MapUnloaded()
{
	CalculateTangents = false;
	ShaderPluginNotify->OnMapUnload();
}

void __declspec(dllexport) FrameStart()
{
	ShaderPluginNotify->OnFrameStart();
}

void __declspec(dllexport) FrameEnd()
{
	ShaderPluginNotify->OnFrameEnd();
}

void __declspec(dllexport) ShaderSet2(int value,Vector4 value2)
{
	ShaderPluginNotify->OnCustom(value,value2);
}

void __declspec(dllexport) ShaderSet(int value,float value2)
{
	ShaderPluginNotify->OnCustom(value,value2);
}

void __declspec(dllexport) ScopeTrigger(bool enabled)
{
	ShaderPluginNotify->OnCustom(EVENT_ENABLESCOPESHADER,enabled);
}

void __declspec(dllexport) ScopeChange(int scope)
{
	ShaderPluginNotify->OnCustom(EVENT_SETSCOPESHADER,(float)scope);
}

void __declspec(dllexport) ScreenFadeRender()
{
	ShaderPluginNotify->OnScreenFadeRender();
}

void __declspec(dllexport) Render(unsigned int primitive_type, unsigned short start_index, unsigned short polygon_count, unsigned short min_vertex_index, unsigned short vertex_count)
{
	ShaderPluginNotify->OnRender(primitive_type,start_index,polygon_count,min_vertex_index,vertex_count);
}

}