#include "audio.h"

AudioManager::AudioManager() { }
AudioManager::~AudioManager() { }

VOID AudioManager::Release()
{
	// Release all tracks.
	for(int i = 0; i < Tracks.size(); i++)
		Tracks[i]->Release();

	// Clear and release other objects.
	Tracks.clear();
	ActiveTrack = NULL;
	Audio->close();
	Audio->release();
}

BOOL AudioManager::Init()
{
	FMOD_RESULT hr;
	unsigned int version;

	// Create the FMOD system.
	hr = FMOD::System_Create(&Audio);

	if (!Validate(hr))
		return FALSE;

	// Check the version.
	hr = Audio->getVersion(&version);

    if (!Validate(hr))
		return FALSE;

    if (version < FMOD_VERSION)
    {
		Output->Write(OUTPUT_ERROR, "<FMOD> Old FMOD Version Detected [%08x], Version [%08x] Required", 
			version, 
			FMOD_VERSION);
        return FALSE;
    }

	// Initialise FMOD.
	hr = Audio->init(100, FMOD_INIT_NORMAL, 0);

	if (!Validate(hr))
		return FALSE;

	// Output success.
	Output->Write(OUTPUT_GOOD, "<FMOD> Initialisation Successful!");
	return TRUE;
}

BOOL AudioManager::AddTrack(std::string path, bool stream)
{
	FMOD_RESULT hr;
	Track* newTrack = new Track();

	// If set to stream file, create stream, else load the track from path.
	if (stream)
		hr = Audio->createStream(path.c_str(), FMOD_SOFTWARE, 0, &newTrack->Data);
	else
		hr = Audio->createSound(path.c_str(), FMOD_SOFTWARE, 0, &newTrack->Data);

	if (!Validate(hr))
		return FALSE;

	// Set the track properties, such as title, artist and path.
	newTrack->GetTags();
	newTrack->Path = path;

	// Add track to the list.
	Tracks.push_back(newTrack);

	// Output success.
	Output->Write(OUTPUT_NORMAL, "<AUDIO> Created Track [%s - %s] from [%s]", 
		newTrack->Artist.c_str(), 
		newTrack->Title.c_str(), 
		newTrack->Path.c_str());

	return TRUE;
}

BOOL AudioManager::Play()
{
	// Play the active track.
	return Play(NULL);
}

BOOL AudioManager::Play(Track* playTrack)
{
	FMOD_RESULT hr;
	bool startPaused = false;

	// If no tracks are in the list.
	if (Tracks.size() <= 0)
	{
		Output->Write(OUTPUT_ERROR, "<AUDIO> Unable to Play; No Tracks Found");
		return FALSE;
	}

	if (playTrack != NULL)
	{
		// Check if track is already playing.
		if (ActiveTrack == playTrack)
		{
			Output->Write(OUTPUT_NORMAL, "<AUDIO> Track [%s - %s] is Already Playing!", ActiveTrack->Artist.c_str(), ActiveTrack->Title.c_str());
			return TRUE;
		}
		else
		{
			// Set the active track as the requested track.
			ActiveTrack = playTrack;
		}
	}

	// If the active track is NULL, pick a random track.
	if (ActiveTrack == NULL)	
		ActiveTrack = Tracks[(int)Random(Tracks.size() - 1)];

	// Play the active track.
	hr = Audio->playSound(FMOD_CHANNEL_FREE, ActiveTrack->Data, startPaused, &Channel);

	if (!Validate(hr))
		return FALSE;

	// Output to console and HUD.
	Output->Write(OUTPUT_NORMAL, "<AUDIO> Now Playing Track [%s - %s]", 
		ActiveTrack->Artist.c_str(), 
		ActiveTrack->Title.c_str());
	Output->ToHUD(ToUpper(ActiveTrack->Artist + " - " + ActiveTrack->Title));
	
	// Set playing switch.
	ActiveTrack->IsPlaying = !startPaused;
	return TRUE;
}

BOOL AudioManager::Next()
{
	if (Tracks.size() <= 1)
		return FALSE;

	// Pause the active track.
	Pause();

	// Find the next track in the list.
	for(int i = 0; i < Tracks.size();i++)
	{
		if (Tracks[i] == ActiveTrack)
		{
			if (i < Tracks.size() - 1)
			{
				ActiveTrack = Tracks[i + 1];
				break;
			}
			else
			{
				ActiveTrack = Tracks[0];
				break;
			}
		}
	}
	
	// Play the active track.
	Play();
	return TRUE;
}

BOOL AudioManager::Previous()
{
	if (Tracks.size() <= 1)
		return FALSE;

	// Pause the active track.
	Pause();

	// Find the previous track in the list.
	for(int i = 0; i < Tracks.size();i++)
	{
		if (Tracks[i] == ActiveTrack)
		{
			if (i > 0)
			{
				ActiveTrack = Tracks[i - 1];
				break;
			}
			else
			{
				ActiveTrack = Tracks[Tracks.size() - 1];
				break;
			}
		}
	}
	
	// Play the active track.
	Play();
	return TRUE;
}

VOID AudioManager::Volume(float value)
{
	float volume;
	// Get the current volume.
	Channel->getVolume(&volume);

	// Increment it.
	volume += value;

	// Control minimum and maximum values.
	if (volume < 0.0f)
		volume = 0.0f;

	if (volume > 1.0f)
		volume = 1.0f;

	// Set the volume as the new volume.
	Channel->setVolume(volume);
}

VOID AudioManager::Pan(float value)
{
	float pan;
	// Get the current pan.
	Channel->getPan(&pan);

	// Increment it.
	pan += value;

	// Control minimum and maximum values.
	if (pan < -1.0f)
		pan = -1.0f;

	if (pan > 1.0f)
		pan = 1.0f;

	// Set the pan as the new pan.
	Channel->setPan(pan);
}

BOOL AudioManager::IsFinished()
{
	// Check if active track has finished.
	return IsFinished(NULL);
}

BOOL AudioManager::IsFinished(Track* track)
{
	FMOD_RESULT hr;

	// Check if a perticular track has finished.
	bool isPlaying;
	hr = Channel->isPlaying(&isPlaying);

	return !isPlaying;
}

BOOL AudioManager::Pause()
{
	FMOD_RESULT hr;

	// Bail out if there are no tracks in the list.
	if (Tracks.size() <= 0)
	{
		Output->Write(OUTPUT_ERROR, "<AUDIO> Unable to Pause; No Tracks Found");
		return FALSE;
	}

	// If no track is playing, bail out.
	if (ActiveTrack == NULL)
	{
		Output->Write(OUTPUT_NORMAL, "<AUDIO> No Active Track to Pause");
		return TRUE;
	}

	// Toggle playing switch.
	ActiveTrack->IsPlaying = !ActiveTrack->IsPlaying;

	// Set the track state.
	hr = Channel->setPaused(!ActiveTrack->IsPlaying);

	if (!Validate(hr))
		return FALSE;

	// Output to console.
	if (ActiveTrack->IsPlaying)
		Output->Write(OUTPUT_NORMAL, "<AUDIO> Resuming Track [%s - %s]", ActiveTrack->Artist.c_str(), ActiveTrack->Title.c_str());
	else
		Output->Write(OUTPUT_NORMAL, "<AUDIO> Pausing Track [%s - %s]", ActiveTrack->Artist.c_str(), ActiveTrack->Title.c_str());

	return TRUE;
}

VOID AudioManager::Update()
{
	// Don't update if there is no audio.
	if (Tracks.size() <= 0)
		return;

	// Go to next track if finished.
	if (IsFinished())
		Next();
}


AudioData AudioManager::GetData()
{
	// If the data cannot be obtained, return the last saved chunk of data.
	if (ActiveTrack == NULL || ActiveTrack->Data == NULL || !ActiveTrack->IsPlaying)
		return Data;

	// Get the spectrum data from the current track.
	FMOD_RESULT hr;
	hr = Channel->getSpectrum(Data.Spectrum, SPECTRUMSIZE, 0, FMOD_DSP_FFT_WINDOW_TRIANGLE);

	if (!Validate(hr))
		return Data;

	Data.Amplitude = 0.0f;
	Data.Average = 0.0f;

	// Calculate audio peak amplitude and average values.
	for(int i = 0; i < SPECTRUMSIZE; i++)
	{
		float audioVolume;
		Channel->getVolume(&audioVolume);
		Data.Spectrum[i] *= audioVolume;

		if (Data.Spectrum[i] > Data.Amplitude)
			Data.Amplitude = Data.Spectrum[i];

		Data.Average += Data.Spectrum[i];
	}

	Data.Average /= SPECTRUMSIZE;

	return Data;
}

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

BOOL AudioManager::Validate(FMOD_RESULT result)
{
	// If the operation completed successfully.
	if (result == FMOD_OK)
		return TRUE;

	// If it failed.
	Output->Write(OUTPUT_ERROR, "<FMOD> %s", FMOD_ErrorString(result));
	return FALSE;
}
