#include "components.h"

ComponentManager::ComponentManager() { }
ComponentManager::~ComponentManager() { }

VOID ComponentManager::SetOutput(OutputManager* output)
{
	// Set the output manager.
	Output = output;
}

BOOL ComponentManager::LoadPlugin(std::string path) 
{
	HINSTANCE hinstDLL;
	GetManifest PluginManifest = NULL;

	// Load the DLL plugin extension.
	hinstDLL = LoadLibrary((LPCSTR)path.c_str());
	
	// Find the main method to retrieve the plugin manifest.
	if(hinstDLL != 0)
		PluginManifest = (GetManifest)GetProcAddress(hinstDLL, "Plugin");

	// Bail out if the main method was not found.
	if (PluginManifest == NULL)
		return FALSE;

	// Create a new plugin from the manifest.
	Plugin* newPlugin = new Plugin(PluginManifest());

	// Add the plugin to the list.
	Plugins.push_back(newPlugin);

	// Output success.
	Output->Write(OUTPUT_GOOD, "<PLUGIN> Successfully Loaded [%s]", newPlugin->Name.c_str());
	return TRUE;
}

void ComponentManager::Release()
{
	// Release all of the visualisations.
	for(int i = 0; i < Visualisations.size();i++)
		Visualisations[i]->Release();

	// Clear the lists.
	Plugins.clear();
	Visualisations.clear();

	// Release the front visualisation.
	if (FrontVisualisation != NULL)
	{
		FrontVisualisation->Release();
		FrontVisualisation = NULL;
	}

	// Release the back visualisation.
	if (BackVisualisation != NULL)
	{
		BackVisualisation->Release();
		BackVisualisation = NULL;
	}
}

VOID ComponentManager::GetPlugins(std::string path) 
{
	// Get all of the DLL files in the plugin directory.
	std::vector<std::string> pluginFiles = GetFiles(GetFullPath(path), "dll");

	// Load the plugins found.
	for(int i = 0; i < pluginFiles.size(); i++)
		LoadPlugin(GetFullPath(path) + "\\" + pluginFiles[i]);
}

std::vector<std::string> ComponentManager::GetFiles(std::string dir, std::string type)
{
	// The list of discovered files.
	std::vector<std::string> foundFiles;

	HANDLE FindHandle = INVALID_HANDLE_VALUE;
	WIN32_FIND_DATA FindData;

	// Sort out correct path.
	std::string Lookup = dir + "*." + type;
	Output->Write(OUTPUT_INFO, "<APP> Dir [%s]", Lookup.c_str());

	// Get the first handle.
	FindHandle = FindFirstFile(Lookup.c_str(), &FindData);

	if (INVALID_HANDLE_VALUE != FindHandle)
	{
		do
		{
			// Add each found file to the list.
			foundFiles.push_back(FindData.cFileName);
		} while (FindNextFile(FindHandle, &FindData) != 0);

		FindClose(FindHandle);
	}

	return foundFiles;
}

VOID ComponentManager::GetVisualisations(std::string path)
{
	// Get all of the manifest files in the visualisation directory.
	std::vector<std::string> visFiles = GetFiles(GetFullPath(path), "vis");

	for(int i = 0; i < visFiles.size(); i++)
	{
		// Parse each manifest.
		Visualisation* newVisualisation = ParseManifest(GetFullPath(path) + visFiles[i]);
		
		// If the parsing was successful, add the visualisation to the list.
		if (newVisualisation != NULL)
			Visualisations.push_back(newVisualisation);
	}
}

Visualisation* ComponentManager::ParseManifest(std::string path)
{
	// Create new visualisation.
	Visualisation* newVisualisation = new Visualisation();
	newVisualisation->Path = path;

	try
	{
		// Key types.
		std::string ATTRIB_KEY = "attributes";
		std::string CONTENT_KEY = "content";
		std::string NAME_KEY = "name";
		std::string COLOR_KEY = "color";
		std::string POSITION_KEY = "position";
		std::string ROTATION_KEY = "rotation";
		std::string SPIN_KEY = "spin";
		std::string CYCLE_KEY = "cycle";
		std::string RESOLUTION_KEY = "resolution";

		// Load the manifest file.
		CSimpleIniA ini;
		ini.LoadFile(path.c_str());

		// Get the number of content sections.
		const int contentSize = ini.GetSectionSize(CONTENT_KEY.c_str());
		std::vector<std::string> contentItems(contentSize);

		// Create a plugin object.
		Plugin* thisPlugin = NULL;
		PluginAttributes Properties;

		// Get all of the sections in the manifest.
		CSimpleIni::TNamesDepend sections;
		ini.GetAllSections(sections);

		// Loop through each section.
		bool bHasMulti;
		CSimpleIni::TNamesDepend::const_iterator section = sections.begin();
		for (; section != sections.end(); ++section) 
		{
			std::string sectionName = section->pItem;

			// Get the keys in each section.
			CSimpleIni::TNamesDepend keys;
			ini.GetAllKeys(sectionName.c_str(), keys);

			thisPlugin = NULL;
		
			// Loop through keys in section.
			CSimpleIni::TNamesDepend::const_iterator key = keys.begin();
			for (; key != keys.end(); ++key) 
			{
				std::string keyName = key->pItem;
				std::string keyValue = ini.GetValue(sectionName.c_str(), keyName.c_str(), 0, &bHasMulti);

				// Manifest global attributes.
				if (sectionName == ATTRIB_KEY)
				{
					// Parse visualisation name.
					if (keyName == NAME_KEY)
						newVisualisation->Name = keyValue;
					// Parse visualisation background color.
					if(keyName == COLOR_KEY)
					{
						D3DXVECTOR4 color = ParseVector4(keyValue);
						newVisualisation->Color = D3DXCOLOR(color.x, 
							color.y, 
							color.z, 
							color.w);
					}
				}
				// Manifest content items.
				else if (sectionName == CONTENT_KEY)
				{
					// Apply the name of the plugin to the content list.
					int contentItem = atoi(keyValue.c_str());
					contentItems[contentItem] = keyName.c_str();
				}
				else
				{
					// If the content has not yet been found.
					if (thisPlugin == NULL)
					{
						// Get the content ID.
						std::string contentID = ini.GetValue(sectionName.c_str(), CONTENT_KEY.c_str(), 0, &bHasMulti);

						int contentItem = atoi(contentID.c_str());
						std::string pluginName = "<UnknownPlugin>";

						if (contentItem < contentItems.size() && contentItem >= 0)
						{
							// Find the plugin the the main plugin list.
							pluginName = contentItems[atoi(contentID.c_str())];
							thisPlugin = FindPlugin(pluginName);
						}

						// If the plugin was found.
						if (thisPlugin != NULL)
						{
							newVisualisation->Plugins.push_back(thisPlugin);
							newVisualisation->Properties.push_back(Properties);
						}
						else
						{
							// Output reference error.
							Output->Write(OUTPUT_ERROR, "<PLUGIN> Missing Plugin [%s] Referenced in [%s]", pluginName.c_str(), newVisualisation->Name.c_str());
							break;
						}
					}

					// Parse plugin color.
					if (keyName == COLOR_KEY)
					{
						D3DXVECTOR4 color = ParseVector4(keyValue);
						newVisualisation->Properties.back().Color.R = color.x;
						newVisualisation->Properties.back().Color.G = color.y;
						newVisualisation->Properties.back().Color.B = color.z;
						newVisualisation->Properties.back().Color.A = color.w;
					}
					// Parse plugin position.
					else if (keyName == POSITION_KEY)
					{
						D3DXVECTOR4 position = ParseVector4(keyValue);
						newVisualisation->Properties.back().Position.X = position.x;
						newVisualisation->Properties.back().Position.Y = position.y;
						newVisualisation->Properties.back().Position.Z = position.z;
					}
					// Parse plugin rotation.
					else if (keyName == ROTATION_KEY)
					{
						D3DXVECTOR4 rotation = ParseVector4(keyValue);
						newVisualisation->Properties.back().Rotation.X = rotation.x;
						newVisualisation->Properties.back().Rotation.Y = rotation.y;
						newVisualisation->Properties.back().Rotation.Z = rotation.z;
					}
					// Parse plugin spin value.
					else if (keyName == SPIN_KEY)
					{
						D3DXVECTOR4 spin = ParseVector4(keyValue);
						newVisualisation->Properties.back().Spin.X = spin.x;
						newVisualisation->Properties.back().Spin.Y = spin.y;
						newVisualisation->Properties.back().Spin.Z = spin.z;
					}
					// Parse plugin color cycle toggle.
					else if (keyName == CYCLE_KEY)
						newVisualisation->Properties.back().Cycle = atof(keyValue.c_str());
					// Parse plugin resolution.
					else if (keyName == RESOLUTION_KEY)
						newVisualisation->Properties.back().Resolution = ParseResolution(keyValue);
					// If the key is not understood.
					else if (keyName != CONTENT_KEY)
						Output->Write(OUTPUT_ERROR, "<PLUGIN> Unknown Key [%s] Referenced in [%s]", keyName.c_str(), newVisualisation->Name.c_str());
						
				}
			}
		}
	} 
	catch (...)
	{
		// Output any caught execeptions.
		Output->Write(OUTPUT_ERROR, "<PLUGIN> Visualisation [%s] is Invalid", path.c_str());
		return NULL;
	}

	// Warn if there visualisation contains no content.
	if (newVisualisation->Plugins.size() == 0)
	{
		Output->Write(OUTPUT_ERROR, "<PLUGIN> Visualisation [%s] has no Plugins!", newVisualisation->Name.c_str());
		return NULL;
	}
	else
	{
		// Output success.
		Output->Write(OUTPUT_GOOD, "<PLUGIN> Visualisation [%s] Successfully Loaded", newVisualisation->Name.c_str());
		return newVisualisation;
	}
}

RESOLUTION ComponentManager::ParseResolution(std::string resolution)
{
	// Get the resolution value from the manifest int value.
	int intRes = atoi(resolution.c_str());

	switch (intRes)
	{
		case 0:
			return RES_SIMPLE;
			break;
		case 2:
			return RES_DETAILED;
			break;
		default:
			return RES_MEDIUM;
			break;
	}
}

D3DXVECTOR4 ComponentManager::ParseVector4(std::string vector)
{
	// Get the vector4 value from the manifest vector4 value.
	D3DXVECTOR4 final;
	char split = '|';
	int i = 0;

	while(vector.find(split, 0) != std::string::npos)
	{
		size_t pos = vector.find(split, 0);
		float temp = (float)atof(vector.substr(0, pos).c_str());  
		vector.erase(0, pos + 1);

		switch(i)
		{
			case 0:
				final.x = temp;
				break;
			case 1:
				final.y = temp;
				break;
			case 2:
				final.z = temp;
				break;
			case 3:
				final.w = temp;
				break;
		}
		
		i++;
	}
	
	return final;
}

Plugin* ComponentManager::FindPlugin(std::string name)
{
	// Find the plugin in the plugin list.
	for(int i = 0; i < Plugins.size();i++)
		if (Plugins[i]->Name == name)
			return Plugins[i];

	// If the plugin was not found.
	return NULL;
}

Visualisation* ComponentManager::GetNext(Visualisation* previous)
{
	int firstVisualisation = 0;

	// Ignore splash screen.
	if (SplashEnabled)
		firstVisualisation = 1;

	// Find next visualisation in list.
	for(int i = 0; i < Visualisations.size();i++)
	{
		if (Visualisations[i] == previous)
		{
			if (i < Visualisations.size() - 1)
				return Visualisations[i + 1];
			else
				return Visualisations[firstVisualisation];
		}
	}
}

int ComponentManager::GetCount()
{
	// Ignore splash screen in count.
	if (SplashEnabled)
		return Visualisations.size() - 1;
	else
		return Visualisations.size();
}

BOOL ComponentManager::Init(IDirect3DDevice9* device, BOOL showSplash)
{
	SplashEnabled = showSplash;

	// Warn if no visualisations loaded and show splash screen.
	if (Visualisations.size() <= 0)
	{
		SplashEnabled = TRUE;
		Output->Write(OUTPUT_ERROR, "<PLUGIN> No Visualisations Loaded!");
		Output->ToHUD("No Visualisations Loaded!", true);
	}

	if (SplashEnabled)
	{
		// Create fake visualisation that will serve as the splash screen.
		Visualisation* splashVis = new Visualisation("Mosaic Boot Screen");
		splashVis->Color = D3DXCOLOR(0.0f);
		splashVis->IsSilent = TRUE;
		splashVis->HasOverlay = TRUE;

		// Set the logo as the overlay.
		splashVis->Overlay.Init(device);
		splashVis->Overlay.LoadFromResources(ID_LOGOB);
		splashVis->Overlay.SetCenter();
		splashVis->Overlay.Color.a = 0.6f;
		
		// Place the splash screen at the beginning so it is shown first.
		Visualisations.insert(Visualisations.begin(), splashVis);
	}

	// Set the front visualisation as the first in the list.
	if (Visualisations.size() > 0)
		FrontVisualisation = Visualisations[0];

	// Initialise all visualisation.
	for(int i = 0; i < Visualisations.size();i++)
		Visualisations[i]->Init(device);

	return TRUE;
}