#include "AdeptHeaders.hpp"

#include "ComponentHeaders.hpp"
#include "Interface.hpp"
#include "AudioFXComponent.hpp"
#include "AudioRenderer.hpp"
#include "Entity.hpp"
#include "AudioComponentWeb.hpp"
#include "Mover.hpp"
#include "SpatializedCommand.hpp"

//
// Shared Data Support
//
//#define HUNT_BUG "daberger"
Component::ClassData* AudioFXComponent::DefaultData = NULL;

static Scalar g_RangeMultiplier;
bool g_LowEndSound;

static bool __stdcall Check50() {return g_RangeMultiplier == 0.5f;}
static bool __stdcall Check33() {return g_RangeMultiplier == 0.33f;}
static bool __stdcall Check25() {return g_RangeMultiplier == 0.25f;}
static bool __stdcall CheckLowDetailSound() {return g_LowEndSound;}

static void __stdcall Enable50() {g_RangeMultiplier = (g_RangeMultiplier == 0.5f) ? 1.0f : 0.5f;}
static void __stdcall Enable33() {g_RangeMultiplier = (g_RangeMultiplier == 0.33f) ? 1.0f : 0.33f;}
static void	__stdcall Enable25() {g_RangeMultiplier = (g_RangeMultiplier == 0.25f) ? 1.0f : 0.25f;}
static void	__stdcall EnableLowDetailSound() {g_LowEndSound = !g_LowEndSound;}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	InitializeClass
//
void AudioFXComponent::InitializeClass(Stuff::NotationFile *startup_ini)
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			AudioFXComponentClassID,
			"Adept::AudioFXComponent",
			BaseClass::DefaultData,
			0,
			NULL
		);
	Register_Object(DefaultData);

	g_RangeMultiplier = 1.0f;
	g_LowEndSound = false;
	AddDebuggerMenuItem("Libraries\\Sound\\50% sound range", Check50, Enable50, 0 );
	AddDebuggerMenuItem("Libraries\\Sound\\33% sound range", Check33, Enable33, 0 );
	AddDebuggerMenuItem("Libraries\\Sound\\25% sound range", Check25, Enable25, 0 );
	AddDebuggerMenuItem("Libraries\\Sound\\Low Detail sounds", CheckLowDetailSound, EnableLowDetailSound, 0 );

	if (startup_ini)
	{
		Check_Object(startup_ini);
		Stuff::Page *page = startup_ini->FindPage("Sound Options");
		if (page)
		{
			Check_Object(page);
			page->GetEntry("LowEndSound", &g_LowEndSound);
			page->GetEntry("Radius", &g_RangeMultiplier);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioFXComponent::TerminateClass(Stuff::NotationFile *startup_ini)
{
	if (startup_ini)
	{
		Check_Object(startup_ini);
		Stuff::Page *page = startup_ini->SetPage("Sound Options");
		Check_Object(page);
		page->SetEntry("LowEndSound", g_LowEndSound);
		page->SetEntry("Radius", g_RangeMultiplier);
	}

	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// AudioFXComponent
//
AudioFXComponent::AudioFXComponent(
	ClassData *class_data,
	MemoryStream *stream,
	AudioComponentWeb *owning_web
):
	Component(class_data, stream, owning_web),
	m_command(NULL)
{
	Check_Pointer(this);
	Check_Object(stream);
	Check_Object(owning_web);

	//
	//-------------------------
	// Read in the command data
	//-------------------------
	//
	int play_mode;
	*stream >> m_type >> m_handleResourceID >> m_lowEndHandleResourceID;
	*stream >> play_mode >> m_priority >> m_volume;
	m_playMode = static_cast<gosAudio_PlayMode>(play_mode);
	*stream >> m_minRestartDelay >> m_minReplaceDelay;
	*stream >> m_minDeviation;
	*stream >> m_nearClip >> m_minRange >> m_maxRange >> m_farClip;

	//
	//-------------------------------------------
	// Make sure the sample is in the audio cache
	//-------------------------------------------
	//
	AudioRenderer::Instance->UseSample(m_handleResourceID);

	//
	//---------------------------
	// Read in the component data
	//---------------------------
	//
	int attribute_id;
	*stream >> attribute_id;
	m_entity = owning_web->GetEntity();
	Check_Object(m_entity);
	if (attribute_id != -1)
	{
		m_status = m_entity->GetAttributeEntry(attribute_id);
		Check_Object(m_status);
		Verify(m_status->attributeType == IntClassID);
	}
	else
		m_status = NULL;

	//
	//----------------------------------------------------------
	// If we are told to play on startup, issue the play command
	//----------------------------------------------------------
	//
	bool execute_now;
	*stream >> execute_now >> m_executeDelay;
	if (execute_now)
	{
		PlaySound();
		if (m_status)
		{
			bool result = true;
			m_status->SetValue(m_entity, &result);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AudioFXComponent* AudioFXComponent::Make(
	MemoryStream *stream,
	AudioComponentWeb *owning_web
)
{
	AudioFXComponent *ear = new AudioFXComponent(DefaultData, stream, owning_web);
	return ear;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AudioFXComponent::~AudioFXComponent()
{
	Check_Object(this);

	AudioCommand *command = m_command.GetCurrent();
	if (command)
	{
		Check_Object(command);
		command->Stop();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioFXComponent::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioFXComponent::Execute()
{
	Check_Object(this);

	//
	//------------------------------------------------------------------------
	// Set the status for the entity.  If we are no longer executing, or if it
	// isn't our turn yet, don't do anything
	//------------------------------------------------------------------------
	//
	SpatializedCommand *command = m_command.GetCurrent();
	int result = command != NULL;
	if (m_status)
		m_status->SetValue(m_entity, &result);
	Time now = gos_GetElapsedTime();
	if (!result || now < m_nextExecute)
		return;

	//
	//-----------------------------------------------
	// Update the position and velocity of the entity
	//-----------------------------------------------
	//
	Check_Object(m_entity);
	Point3D position(m_entity->GetLocalToWorld());
	command->SetPosition(position);
	m_nextExecute = now + m_executeDelay;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioFXComponent::ChannelChanged(Channel *channel)
{
	Check_Object(this);
	Check_Object(channel);

	switch (channel->GetCommand())
	{
	case AudioRenderer::StartEffectCommandID:
		if (!m_command.GetCurrent())
			PlaySound();
		break;
	case AudioRenderer::KillEffectCommandID:
	case AudioRenderer::StopEffectCommandID:
		if (m_command.GetCurrent())
			m_command.GetCurrent()->Stop();
		break;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioFXComponent::PlaySound()
{
	Check_Object(this);

	//
	//------------------------------------------------------
	// Find where the sound is, then launch the play command
	//------------------------------------------------------
	//
	Check_Object(m_entity);
	Point3D position(m_entity->GetLocalToWorld());
	Verify(m_executeDelay >= 0.0f);
	m_nextExecute = gos_GetElapsedTime() + m_executeDelay;
	SpatializedCommand *command = SpatializedCommand::Create(
		m_type,
		(g_LowEndSound) ? m_lowEndHandleResourceID : m_handleResourceID,
		m_priority,
		m_volume,
		m_minRestartDelay,
		m_minReplaceDelay,
		position,
		Vector3D(0.0f, 0.0f, 0.0f),
		m_minDeviation,
		#if defined(LAB_ONLY)
			m_nearClip * g_RangeMultiplier,
			m_minRange * g_RangeMultiplier,
			m_maxRange * g_RangeMultiplier,
			m_farClip * g_RangeMultiplier
		#else
			m_nearClip,
			m_minRange,
			m_maxRange,
			m_farClip
		#endif
	);
	Check_Object(command);
	m_command.Add(command);
	command->Play(m_playMode);
}

