
#include "application.h"

Application::Application() { }
Application::~Application() { Release(); }

VOID Application::Release()
{
	// Stop rendering.
	BlockRendering = TRUE;

	// Release all of the main components.
	Audio.Release();
	Components.Release();
	Output.Release();
	Graphics.Release();
}

VOID Application::SetKey(WPARAM key)
{
	switch(key)
	{
		case ' ':
			// Pause the audio.
			Audio.Pause();
			break;
		case 'Z':
			// Show the developer console.
			Output.Show = !Output.Show;
			break;
		case 'T':
			// Transition the current visualisation.
			Transition();
			break;
		case VK_LEFT:
			// Play previous track.
			Audio.Previous();
			break;
		case VK_RIGHT:
			// Play next track.
			Audio.Next();
			break;
		case VK_UP:
			// Turn up the volume.
			Audio.Volume(0.1f);
			break;
		case VK_DOWN:
			// Turn down the volume.
			Audio.Volume(-0.1f);
			break;
		case VK_HOME:
			// Push pan to the left.
			Audio.Pan(0.1f);
			break;
		case VK_END:
			// Push pan to the right.
			Audio.Pan(-0.1f);
			break;
	}
}

VOID Application::Init(LPSTR lpCmdLine)
{
	// Get the argument list.
	Arguments = parseCmdLine(lpCmdLine);

	// Find the current working directory.
	char szAppPath[MAX_PATH] = "";
	::GetModuleFileName(0, szAppPath, sizeof(szAppPath) - 1);
	Directory = szAppPath;
	Directory = Directory.substr(0, Directory.rfind("\\"));

	// Set the current working directory.
	SetCurrentDirectory(Directory.c_str());

	bool foundConfig = false;
	// Show welcome message.
	Output.Write(OUTPUT_NORMAL, "Welcome to Mosaic Audio Visualizer [%s]", Directory.c_str());

	// Format and display arguments.
	for (int i = 0; i < Arguments.size(); ++i)
	{
		if (Arguments[i][0] == '\"')
			Arguments[i].erase(0, 1);

		if (Arguments[i][Arguments[i].size()-1] == '\"')
		Arguments[i].erase(Arguments[i].size()-1, 1);

		Output.Write(OUTPUT_INFO, "<ARGS> %s", Arguments[i].c_str());
	}

	// If one of the arguments is a path to a config file, parse it and use that.
	for (int i = 0; i < Arguments.size(); ++i)
	{
		if (IsFileType(Arguments[i], "ini"))
		{
			ParseConfig(Arguments[i]);
			foundConfig = true;
			break;
		}
	}

	// Else, use the default config file.
	if (!foundConfig)
		ParseConfig(Directory + "\\config.ini");

	// Set initial states.
	Properties.Ticks = 0;
	Properties.HasShownStartScreen = FALSE;
	BlockRendering = FALSE;
}

BOOL Application::Start(HWND hWnd, HINSTANCE hInstance)
{
	// Stop rendering.
	BlockRendering = TRUE;

	std::string argFile;
	bool inFile = false;

	// If the argument is playable type, set it to be played.
	for (int i = 0; i < Arguments.size(); ++i)
	{
		if (!IsFileType(Arguments[i], "ini"))
		{
			argFile = Arguments[i];
			inFile = true;
			break;
		}
	}

	// Initialise graphics.
	if (!Graphics.Init(hWnd, Properties.Width, Properties.Height, Properties.Fullscreen))
		return FALSE;

	// Pass output manager to components.
	Graphics.SetOutput(&Output);
	Audio.SetOutput(&Output);
	Components.SetOutput(&Output);

	// Setup graphics.
	if (!Graphics.Setup())
		return FALSE;

	// Load external plugins.
	Components.GetPlugins(Properties.PluginsDirectory);

	// Load external visualisations.
	Components.GetVisualisations(Properties.VisualisationDirectory);

	// Initialise components.
	Components.Init(Graphics.GetDevice(), Properties.StartScreenEnabled);
	
	// Set the front buffer to the first visualisation.
	Graphics.SetVisualisation(FRONT_BUFFER, Components.FrontVisualisation);

	// Initialise audio.
	if (Audio.Init())
	{
		if (inFile)
		{
			// Add the file passed by argument.
			Audio.AddTrack(argFile, true);
		}
		else
		{
			// Find tracks in the default audio directory and add.
			std::vector<std::string> foundTracks = Components.GetFiles(GetFullPath(Properties.AudioDirectory), "mp3");

			for (int i = 0; i < foundTracks.size(); i++)
			{
				// Set the correct path.
				foundTracks[i] = Directory + "\\" + Properties.AudioDirectory + foundTracks[i];
				Audio.AddTrack(foundTracks[i].c_str(), true);
			}
		}

		// Post message if no tracks are found, else, play the first track.
		if (Audio.Tracks.size() > 0)
			Audio.Play();
		else
			Output.ToHUD("No Audio Tracks Loaded!", true);
	}

	// Set initial properties.
	if (Properties.StartScreenEnabled)
		Graphics.Transition.Interval = 500;
	else
		Graphics.Transition.Interval = Properties.TransitionInterval;

	Graphics.Transition.Speed = 2000;

	// Begin the first transition.
	Graphics.BeginTransition(&D3DXVECTOR4(0.5f,0.5f,0.0f,0.0f));

	BlockRendering = FALSE;
	return TRUE;
}

VOID Application::ParseConfig(std::string path)
{
	// Key types.
	std::string GENERAL_KEY = "general";
	std::string DIR_KEY = "dir";
	std::string TRANSITION_KEY = "transition";

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

	// Get all of the [sections] in the config file.
	CSimpleIni::TNamesDepend sections;
    ini.GetAllSections(sections);

	// Loop through [sections].
	bool bHasMulti;
	CSimpleIni::TNamesDepend::const_iterator section = sections.begin();
    for (; section != sections.end(); ++section) 
	{
		std::string sectionName = section->pItem;
		
		// Get all of the keys in a [section].
		CSimpleIni::TNamesDepend keys;
		ini.GetAllKeys(sectionName.c_str(), keys);

			// Loop though keys.
			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);

				// Set general key properties.
				if (sectionName == GENERAL_KEY)
				{
					if (keyName == "width")
						Properties.Width = atoi(keyValue.c_str());
					else if (keyName == "height")
						Properties.Height = atoi(keyValue.c_str());
					else if (keyName == "splash")
						Properties.StartScreenEnabled = ToBool(keyValue);
					else if (keyName == "fullscreen")
						Properties.Fullscreen = ToBool(keyValue);
				} 
				// Set directory key properties.
				else if (sectionName == DIR_KEY)
				{
					if (keyName == "audio")
						Properties.AudioDirectory = keyValue;
					else if (keyName == "visualisations")
						Properties.VisualisationDirectory = keyValue;
					else if (keyName == "plugins")
						Properties.PluginsDirectory = keyValue;
				}
				// Set transition key properties.
				else if (sectionName == TRANSITION_KEY)
				{
					if (keyName == "speed")
						Properties.TransitionSpeed = atoi(keyValue.c_str());
					else if (keyName == "interval")
						Properties.TransitionInterval = atoi(keyValue.c_str());
				}
			}
	}

	// Audio directory.
	if (Properties.AudioDirectory.empty())
		Properties.AudioDirectory = ".\\audio\\";

	// Visualisation directory.
	if (Properties.VisualisationDirectory.empty())
		Properties.VisualisationDirectory = ".\\visualisations\\";

	// Plugins directory.
	if (Properties.PluginsDirectory.empty())
		Properties.PluginsDirectory = ".\\plugins\\";

	// Min and max width.
	if (Properties.Width < 320 || Properties.Width > 1920)
		Properties.Width = 800;

	// Min and max height.
	if (Properties.Height < 240 || Properties.Height > 1080)
		Properties.Height = 600;

	// Min transition speed.
	if (Properties.TransitionSpeed < 200)
		Properties.TransitionSpeed = 3000;

	// Min transition interval.
	if (Properties.TransitionInterval < 100)
		Properties.TransitionInterval = 4000;

		// Output success message.
	Output.Write(OUTPUT_INFO, "<APP> Config Settings Applied [%s]", path.c_str());
}

VOID Application::Transition()
{
	// If there is only one visualisation, don't bother.
	if (Components.GetCount() < 1)
		return;

	// Set the properties.
	Graphics.Transition.Interval = Properties.TransitionInterval;
	Graphics.Transition.Speed = Properties.TransitionSpeed;

	// Get the active buffer.
	switch(Graphics.ActiveBuffer)
	{
		case FRONT_BUFFER:
			// If front is active, set the back to NULL.
			Components.BackVisualisation = NULL;
			break;
		case BACK_BUFFER:
			// If back is active, set the front to NULL.
			Components.FrontVisualisation = NULL;
			break;
		case TRANSITION_BUFFER:
			// Bail if transitioning.
			return;
	}

	// Get front visualisation.
	if (Components.FrontVisualisation == NULL)
		Components.FrontVisualisation = Components.GetNext(Components.BackVisualisation);

	// Get back visualisation.
	if (Components.BackVisualisation == NULL)
		Components.BackVisualisation = Components.GetNext(Components.FrontVisualisation);

	// Set front visualisation.
	Graphics.SetVisualisation(FRONT_BUFFER, Components.FrontVisualisation);
	// Set back visualisation.
	Graphics.SetVisualisation(BACK_BUFFER, Components.BackVisualisation);

	// Begin the transition.
	Graphics.BeginTransition();
}

VOID Application::Update()
{
	// Update time if rendering.
	if (!BlockRendering)
	{
		Properties.Ticks++;

		// Set start screen switch.
		if (Graphics.Transition.Count > 0)
			Properties.HasShownStartScreen = TRUE;

		if (!Graphics.Transition.Active)
		{
			// If it is time to transition.
			if (Properties.Ticks % Graphics.Transition.Interval == 0)
			{
				// Begin transition.
				if (Components.GetCount() > 1)
					Transition();

				// Transition if we're currently on the splash screen.
				if (Properties.StartScreenEnabled)
					if (Graphics.Transition.Count == 1)
						Transition();
			}
		}

		// Update components.
		Audio.Update();
		Graphics.Update();

		// Get the audio data from the audio component.
		PluginData Data;
		Data.Audio = Audio.GetData();
		
		// Start the rendering process.
		Graphics.BeginRender();

			// If there is something in the front buffer.
			if (Graphics.GetBuffer(FRONT_BUFFER) != NULL)
			{
				// If the direction of transition is corrent or if a transition is active.
				if (Graphics.Transition.Direction || Graphics.Transition.Active)
				{
					// Set the back rendering surface.
					Graphics.SetSurface(FRONT_BUFFER);
					// Render the visualisation to the back surface.
					Components.FrontVisualisation->Render(&Data);
				}
			}

			// If there is something in the back buffer.
			if (Graphics.GetBuffer(BACK_BUFFER) != NULL)
			{
				// If the direction of transition is corrent or if a transition is active.
				if (!Graphics.Transition.Direction || Graphics.Transition.Active)
				{
					// Set the back rendering surface.
					Graphics.SetSurface(BACK_BUFFER);
					// Render the visualisation to the back surface.
					Components.BackVisualisation->Render(&Data);
				} 
			}

		// End the rendering process.
		Graphics.EndRender();
	}
}


