/****************************************************************************
*                                                                           *
* Project64 - A Nintendo 64 emulator.                                      *
* http://www.pj64-emu.com/                                                  *
* Copyright (C) 2012 Project64. All rights reserved.                        *
*                                                                           *
* License:                                                                  *
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html                        *
*                                                                           *
****************************************************************************/
#include "stdafx.h"

CPlugins::CPlugins(const stdstr & PluginDir) :
	m_RenderWindow(NULL), m_DummyWindow(NULL),
m_PluginDir(PluginDir),
m_Gfx(NULL),
m_Audio(NULL),
m_RSP(NULL),
m_Control(NULL)
{
	CreatePlugins();
	g_Settings->RegisterChangeCB(Plugin_RSP_Current, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->RegisterChangeCB(Plugin_GFX_Current, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->RegisterChangeCB(Plugin_AUDIO_Current, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->RegisterChangeCB(Plugin_CONT_Current, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->RegisterChangeCB(Plugin_UseHleGfx, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->RegisterChangeCB(Plugin_UseHleAudio, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->RegisterChangeCB(Game_EditPlugin_Gfx, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->RegisterChangeCB(Game_EditPlugin_Audio, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->RegisterChangeCB(Game_EditPlugin_Contr, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->RegisterChangeCB(Game_EditPlugin_RSP, this, (CSettings::SettingChangedFunc)PluginChanged);
}

CPlugins::~CPlugins(void)
{
	g_Settings->UnregisterChangeCB(Plugin_RSP_Current, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->UnregisterChangeCB(Plugin_GFX_Current, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->UnregisterChangeCB(Plugin_AUDIO_Current, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->UnregisterChangeCB(Plugin_CONT_Current, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->UnregisterChangeCB(Plugin_UseHleGfx, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->UnregisterChangeCB(Plugin_UseHleAudio, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->UnregisterChangeCB(Game_EditPlugin_Gfx, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->UnregisterChangeCB(Game_EditPlugin_Audio, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->UnregisterChangeCB(Game_EditPlugin_Contr, this, (CSettings::SettingChangedFunc)PluginChanged);
	g_Settings->UnregisterChangeCB(Game_EditPlugin_RSP, this, (CSettings::SettingChangedFunc)PluginChanged);

	DestroyGfxPlugin();
	DestroyAudioPlugin();
	DestroyRspPlugin();
	DestroyControlPlugin();
}

void CPlugins::PluginChanged(CPlugins * _this)
{
	if (g_Settings->LoadBool(Game_TempLoaded) == true)
	{
		return;
	}
	bool bGfxChange = _stricmp(_this->m_GfxFile.c_str(), g_Settings->LoadStringVal(Game_Plugin_Gfx).c_str()) != 0;
	bool bAudioChange = _stricmp(_this->m_AudioFile.c_str(), g_Settings->LoadStringVal(Game_Plugin_Audio).c_str()) != 0;
	bool bRspChange = _stricmp(_this->m_RSPFile.c_str(), g_Settings->LoadStringVal(Game_Plugin_RSP).c_str()) != 0;
	bool bContChange = _stricmp(_this->m_ControlFile.c_str(), g_Settings->LoadStringVal(Game_Plugin_Controller).c_str()) != 0;

	if (bGfxChange || bAudioChange || bRspChange || bContChange)
	{
		if (g_Settings->LoadBool(GameRunning_CPU_Running))
		{
			//Ensure that base system actually exists before we go triggering the event
			if (g_BaseSystem)
			{
				g_BaseSystem->ExternalEvent(SysEvent_ChangePlugins);
			}
		}
		else
		{
			_this->Reset(NULL);
			Notify().RefreshMenu();
		}
	}
}

template <typename plugin_type>
static void LoadPlugin(SettingID PluginSettingID, SettingID PluginVerSettingID, plugin_type * & plugin, const char * PluginDir, stdstr & FileName, TraceType TraceLevel, const char * type)
{
	if (plugin != NULL)
	{
		return;
	}
	FileName = g_Settings->LoadStringVal(PluginSettingID);
	CPath PluginFileName(PluginDir, FileName.c_str());
	plugin = new plugin_type();
	if (plugin)
	{
		WriteTraceF(TraceLevel, __FUNCTION__ ": %s Loading (%s): Starting", type, (const char *)PluginFileName);
		if (plugin->Load(PluginFileName))
		{
			WriteTraceF(TraceLevel, __FUNCTION__ ": %s Current Ver: %s", type, plugin->PluginName());
			g_Settings->SaveString(PluginVerSettingID, plugin->PluginName());
		}
		else
		{
			WriteTraceF(TraceError, __FUNCTION__ ": Failed to load %s", (const char *)PluginFileName);
			delete plugin;
			plugin = NULL;
		}
		WriteTraceF(TraceLevel, __FUNCTION__ ": %s Loading Done", type);
	}
	else
	{
		WriteTraceF(TraceError, __FUNCTION__ ": Failed to allocate %s plugin", type);
	}
}

void CPlugins::CreatePlugins(void)
{
	LoadPlugin(Game_Plugin_Gfx, Plugin_GFX_CurVer, m_Gfx, m_PluginDir.c_str(), m_GfxFile, TraceGfxPlugin, "GFX");
	LoadPlugin(Game_Plugin_Audio, Plugin_AUDIO_CurVer, m_Audio, m_PluginDir.c_str(), m_AudioFile, TraceDebug, "Audio");
	LoadPlugin(Game_Plugin_RSP, Plugin_RSP_CurVer, m_RSP, m_PluginDir.c_str(), m_RSPFile, TraceRSP, "RSP");
	LoadPlugin(Game_Plugin_Controller, Plugin_CONT_CurVer, m_Control, m_PluginDir.c_str(), m_ControlFile, TraceDebug, "Control");

	//Enable debugger
	if (m_RSP != NULL && m_RSP->EnableDebugging)
	{
		WriteTrace(TraceRSP, __FUNCTION__ ": EnableDebugging starting");
		m_RSP->EnableDebugging(bHaveDebugger());
		WriteTrace(TraceRSP, __FUNCTION__ ": EnableDebugging done");
	}

	if (bHaveDebugger())
	{
		Notify().RefreshMenu();
	}
}

void CPlugins::GameReset(void)
{
	if (m_Gfx)
	{
		m_Gfx->GameReset();
	}
	if (m_Audio)
	{
		m_Audio->GameReset();
	}
	if (m_RSP)
	{
		m_RSP->GameReset();
	}
	if (m_Control)
	{
		m_Control->GameReset();
	}
}

void CPlugins::DestroyGfxPlugin(void)
{
	if (m_Gfx == NULL)
	{
		return;
	}
	WriteTrace(TraceGfxPlugin, __FUNCTION__ ": before delete m_Gfx");
	delete m_Gfx;
	WriteTrace(TraceGfxPlugin, __FUNCTION__ ": after delete m_Gfx");
	m_Gfx = NULL;
	//		g_Settings->UnknownSetting_GFX = NULL;
	DestroyRspPlugin();
}

void CPlugins::DestroyAudioPlugin(void)
{
	if (m_Audio == NULL)
	{
		return;
	}
	WriteTrace(TraceDebug, __FUNCTION__ ": 5");
	m_Audio->Close();
	WriteTrace(TraceDebug, __FUNCTION__ ": 6");
	delete m_Audio;
	WriteTrace(TraceDebug, __FUNCTION__ ": 7");
	m_Audio = NULL;
	WriteTrace(TraceDebug, __FUNCTION__ ": 8");
	//		g_Settings->UnknownSetting_AUDIO = NULL;
	DestroyRspPlugin();
}

void CPlugins::DestroyRspPlugin(void)
{
	if (m_RSP == NULL)
	{
		return;
	}
	WriteTrace(TraceDebug, __FUNCTION__ ": 9");
	m_RSP->Close();
	WriteTrace(TraceDebug, __FUNCTION__ ": 10");
	delete m_RSP;
	WriteTrace(TraceDebug, __FUNCTION__ ": 11");
	m_RSP = NULL;
	WriteTrace(TraceDebug, __FUNCTION__ ": 12");
	//		g_Settings->UnknownSetting_RSP = NULL;
}

void CPlugins::DestroyControlPlugin(void)
{
	if (m_Control == NULL)
	{
		return;
	}
	WriteTrace(TraceDebug, __FUNCTION__ ": 13");
	m_Control->Close();
	WriteTrace(TraceDebug, __FUNCTION__ ": 14");
	delete m_Control;
	WriteTrace(TraceDebug, __FUNCTION__ ": 15");
	m_Control = NULL;
	WriteTrace(TraceDebug, __FUNCTION__ ": 16");
	//		g_Settings->UnknownSetting_CTRL = NULL;
}

void CPlugins::SetRenderWindows( CMainGui * RenderWindow, CMainGui * DummyWindow )
{
	m_RenderWindow = RenderWindow;
	m_DummyWindow  = DummyWindow;
}

void CPlugins::RomOpened(void)
{
	m_Gfx->RomOpened();
	m_RSP->RomOpened();
	m_Audio->RomOpened();
	m_Control->RomOpened();
}

void CPlugins::RomClosed(void)
{
	m_Gfx->RomClose();
	m_RSP->RomClose();
	m_Audio->RomClose();
	m_Control->RomClose();
}

bool CPlugins::Initiate(CN64System * System)
{
	WriteTrace(TraceDebug, __FUNCTION__ ": Start");
	//Check to make sure we have the plugin available to be used
	if (m_Gfx == NULL) { return false; }
	if (m_Audio == NULL) { return false; }
	if (m_RSP == NULL) { return false; }
	if (m_Control == NULL) { return false; }

	WriteTrace(TraceGfxPlugin, __FUNCTION__ ": Gfx Initiate Starting");
	if (!m_Gfx->Initiate(System,m_RenderWindow))   { return false; }
	WriteTrace(TraceGfxPlugin, __FUNCTION__ ": Gfx Initiate Done");
	WriteTrace(TraceDebug, __FUNCTION__ ": Audio Initiate Starting");
	if (!m_Audio->Initiate(System,m_RenderWindow)) { return false; }
	WriteTrace(TraceDebug, __FUNCTION__ ": Audio Initiate Done");
	WriteTrace(TraceDebug, __FUNCTION__ ": Control Initiate Starting");
	if (!m_Control->Initiate(System,m_RenderWindow)) { return false; }
	WriteTrace(TraceDebug, __FUNCTION__ ": Control Initiate Done");
	WriteTrace(TraceRSP, __FUNCTION__ ": RSP Initiate Starting");
	if (!m_RSP->Initiate(this, System))   { return false; }
	WriteTrace(TraceRSP, __FUNCTION__ ": RSP Initiate Done");
	WriteTrace(TraceDebug, __FUNCTION__ ": Done");
	return true;
}

bool CPlugins::ResetInUiThread(CN64System * System)
{
#if defined(WINDOWS_UI)
	return m_RenderWindow->ResetPlugins(this, System);
#else
	g_Notify -> BreakPoint(__FILEW__, __LINE__);
	return false;
#endif
}

bool CPlugins::Reset(CN64System * System)
{
	WriteTrace(TraceDebug, __FUNCTION__ ": Start");

	bool bGfxChange = _stricmp(m_GfxFile.c_str(), g_Settings->LoadStringVal(Game_Plugin_Gfx).c_str()) != 0;
	bool bAudioChange = _stricmp(m_AudioFile.c_str(), g_Settings->LoadStringVal(Game_Plugin_Audio).c_str()) != 0;
	bool bRspChange = _stricmp(m_RSPFile.c_str(), g_Settings->LoadStringVal(Game_Plugin_RSP).c_str()) != 0;
	bool bContChange = _stricmp(m_ControlFile.c_str(), g_Settings->LoadStringVal(Game_Plugin_Controller).c_str()) != 0;

	//if GFX and Audio has changed we also need to force reset of RSP
	if (bGfxChange || bAudioChange)
		bRspChange = true;

	if (bGfxChange) { DestroyGfxPlugin(); }
	if (bAudioChange) { DestroyAudioPlugin(); }
	if (bRspChange) { DestroyRspPlugin(); }
	if (bContChange) { DestroyControlPlugin(); }

	CreatePlugins();

	if (m_Gfx && bGfxChange)
	{
		WriteTrace(TraceGfxPlugin, __FUNCTION__ ": Gfx Initiate Starting");
		if (!m_Gfx->Initiate(System,m_RenderWindow))   { return false; }
		WriteTrace(TraceGfxPlugin, __FUNCTION__ ": Gfx Initiate Done");
	}
	if (m_Audio && bAudioChange)
	{
		WriteTrace(TraceDebug, __FUNCTION__ ": Audio Initiate Starting");
		if (!m_Audio->Initiate(System,m_RenderWindow)) { return false; }
		WriteTrace(TraceDebug, __FUNCTION__ ": Audio Initiate Done");
	}
	if (m_Control && bContChange)
	{
		WriteTrace(TraceDebug, __FUNCTION__ ": Control Initiate Starting");
		if (!m_Control->Initiate(System,m_RenderWindow)) { return false; }
		WriteTrace(TraceDebug, __FUNCTION__ ": Control Initiate Done");
	}
	if (m_RSP && bRspChange)
	{
		WriteTrace(TraceRSP, __FUNCTION__ ": RSP Initiate Starting");
		if (!m_RSP->Initiate(this, System))   { return false; }
		WriteTrace(TraceRSP, __FUNCTION__ ": RSP Initiate Done");
	}
	WriteTrace(TraceDebug, __FUNCTION__ ": Done");
	return true;
}

void CPlugins::ConfigPlugin(uint32_t hParent, PLUGIN_TYPE Type)
{
	switch (Type)
	{
	case PLUGIN_TYPE_RSP:
		if (m_RSP == NULL || m_RSP->DllConfig == NULL) { break; }
		if (!m_RSP->Initialized())
		{
			if (!m_RSP->Initiate(NULL, NULL))
			{
				break;
			}
		}
		m_RSP->DllConfig(hParent);
		break;
	case PLUGIN_TYPE_GFX:
		if (m_Gfx == NULL || m_Gfx->DllConfig == NULL) { break; }
		if (!m_Gfx->Initialized())
		{
			if (!m_Gfx->Initiate(NULL, NULL))
			{
				break;
			}
		}
		m_Gfx->DllConfig(hParent);
		break;
	case PLUGIN_TYPE_AUDIO:
		if (m_Audio == NULL || m_Audio->DllConfig == NULL) { break; }
		if (!m_Audio->Initialized())
		{
			if (!m_Audio->Initiate(NULL, NULL))
			{
				break;
			}
		}
		m_Audio->DllConfig(hParent);
		break;
	case PLUGIN_TYPE_CONTROLLER:
		if (m_Control == NULL || m_Control->DllConfig == NULL) { break; }
		if (!m_Control->Initialized())
		{
			if (!m_Control->Initiate(NULL, NULL))
			{
				break;
			}
		}
		m_Control->DllConfig(hParent);
		break;
	}
}

void DummyCheckInterrupts(void)
{
}

void DummyFunction(void)
{
}

bool CPlugins::CopyPlugins(const stdstr & DstDir) const
{
	//Copy GFX Plugin
	CPath srcGfxPlugin(m_PluginDir.c_str(), g_Settings->LoadStringVal(Game_Plugin_Gfx).c_str());
	CPath dstGfxPlugin(DstDir.c_str(), g_Settings->LoadStringVal(Game_Plugin_Gfx).c_str());

	if (!dstGfxPlugin.DirectoryExists())
	{
		dstGfxPlugin.DirectoryCreate();
	}
	if (!srcGfxPlugin.CopyTo(dstGfxPlugin))
	{
		return false;
	}

	//Copy m_Audio Plugin
	CPath srcAudioPlugin(m_PluginDir.c_str(), g_Settings->LoadStringVal(Game_Plugin_Audio).c_str());
	CPath dstAudioPlugin(DstDir.c_str(), g_Settings->LoadStringVal(Game_Plugin_Audio).c_str());
	if (!dstAudioPlugin.DirectoryExists())
	{
		dstAudioPlugin.DirectoryCreate();
	}
	if (!srcAudioPlugin.CopyTo(dstAudioPlugin))
	{
		return false;
	}

	//Copy RSP Plugin
	CPath srcRSPPlugin(m_PluginDir.c_str(), g_Settings->LoadStringVal(Game_Plugin_RSP).c_str());
	CPath dstRSPPlugin(DstDir.c_str(), g_Settings->LoadStringVal(Game_Plugin_RSP).c_str());
	if (!dstRSPPlugin.DirectoryExists())
	{
		dstRSPPlugin.DirectoryCreate();
	}
	if (!srcRSPPlugin.CopyTo(dstRSPPlugin))
	{
		return false;
	}

	//Copy Controller Plugin
	CPath srcContPlugin(m_PluginDir.c_str(), g_Settings->LoadStringVal(Game_Plugin_Controller).c_str());
	CPath dstContPlugin(DstDir.c_str(), g_Settings->LoadStringVal(Game_Plugin_Controller).c_str());
	if (!dstContPlugin.DirectoryExists())
	{
		dstContPlugin.DirectoryCreate();
	}
	if (!srcContPlugin.CopyTo(dstContPlugin))
	{
		return false;
	}
	return true;
}