//===========================================================================//
// File:	AudioRenderer.cpp                                                //
// Project: MUNGA           Brick: Video Renderer 	                         //
// Contents: Abstracted Base Video Renderer								     //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 03/04/97  JTR  Initial coding.                                            //
//---------------------------------------------------------------------------//
// Copyright (C) 1997, Virtual World Entertainment, Inc.                     //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "AdeptHeaders.hpp"
#include "AudioChannel.hpp"
#include "AudioSample.hpp"
#include "AudioRenderer.hpp"
#include "AudioCommand.hpp"

//##########################################################################
//############################    AudioChannel    ##########################
//##########################################################################

AudioChannel::ClassData* AudioChannel::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioChannel::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			AudioChannelClassID,
			"Adept::AudioChannel",
			BaseClass::DefaultData
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioChannel::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AudioChannel::AudioChannel(ClassData *class_data, int channel, int type, DWORD properties):
	Plug(class_data),
	m_channel(channel),
	m_type(type),
	m_sample(NULL),
	m_playMode(gosAudio_Stop),
	m_properties(properties),
	m_startTime(0.0),
	m_command(NULL)
{
	Check_Pointer(this);

	//
	//-------------------------------
	// Initialize the various sliders
	//-------------------------------
	//
	gosAudio_AssignResourceToChannel(m_channel, NULL);
	gosAudio_AllocateChannelSliders(m_channel, m_properties);
	SetVolume();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AudioChannel::~AudioChannel()
{
	Check_Object(this);
	Stop();
	gosAudio_AssignResourceToChannel(m_channel, NULL);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioChannel::Play(AudioCommand *command)
{
	Check_Object(this);
	Check_Object(command);

	Verify(m_playMode == gosAudio_Stop);
	m_startTime = gos_GetElapsedTime();
	m_playMode = command->GetPlayMode();
	m_command = command;
	SetVolume();
	gosAudio_SetChannelPlayMode(m_channel, m_playMode);
	m_command->SetChannel(this);
	m_energy = command->m_energy;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioChannel::Stop()
{
	Check_Object(this);

	if (m_playMode != gosAudio_Stop)
	{
		gosAudio_SetChannelPlayMode(m_channel, gosAudio_Stop);
		Deactivate();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioChannel::SetVolume()
{
	Check_Object(this);

	//
	//---------------------------------------------------
	// If there is no command attached, we have no volume
	//---------------------------------------------------
	//
	if (!m_command)
		m_volume = 0.0f;

	//
	//----------------------------------------------------------
	// Otherwise, multiply the command volume by the type volume
	//----------------------------------------------------------
	//
	else
	{
		ChannelType &type = AudioRenderer::Instance->m_channelTypes[m_type];
		Check_Object(&type);
		m_volume = m_command->m_volume * type.GetVolume();
	}

	//
	//----------------------------
	// Now tell gos the new volume
	//----------------------------
	//
	gosAudio_SetChannelSlider(m_channel, gosAudio_Volume, m_volume);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioChannel::Activate(AudioCommand *command)
{
	Check_Object(this);
	Check_Object(command);
	Verify(m_playMode == gosAudio_Stop);

	//
	//-------------------
	// Start the sound up
	//-------------------
	//
	Play(command);

	//
	//----------------------------
	// Attach to the active chains
	//----------------------------
	//
	Check_Object(m_sample);
	m_sample->SetChannelActive(this);
	Check_Object(AudioRenderer::Instance);
	AudioRenderer::Instance->SetChannelActive(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioChannel::Deactivate()
{
	Check_Object(this);
	Verify(m_playMode != gosAudio_Stop);

	//
	//-------------------------------------------------------------------------------
	// Kill the command, and delete the object from any chains/sockets it might be on
	//-------------------------------------------------------------------------------
	//
	if (m_command)
	{
		Check_Object(m_command);

		//
		//-----------------------------------------------------------------------------------
		// If this is a loop, we don't destroy the command.  Rather, we put it on the pending
		// loop list in the audio renderer
		//-----------------------------------------------------------------------------------
		//
		if (m_playMode == gosAudio_PlayOnce)
			delete m_command;
		else
		{
			Verify(m_playMode == gosAudio_Loop);
			m_command->m_channel = NULL;
			Check_Object(AudioRenderer::Instance);
			AudioRenderer::Instance->m_executingCommands.RemovePlug(m_command);
		}
		m_command = NULL;
	}

	//
	//----------------------------------------------------------------------------------
	// Attach to the inactive chain in the sample.  If the sample is a stream and is not
	// play once, delete it
	//----------------------------------------------------------------------------------
	//
	m_playMode = gosAudio_Stop;
	Check_Object(m_sample);
	m_sample->SetChannelInactive(this);
	AudioRenderer::Instance->SetChannelInactive(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioChannel::SetSample(AudioSample *sample)
{
	Check_Object(this);
	Verify(m_sample != sample);
	Verify(m_playMode == gosAudio_Stop);

#if defined(LAB_ONLY)
	if (((m_properties&(gosAudio_Position|gosAudio_MinMaxDistance))!=0) != sample->m_3D)
	{
		if (sample->m_3D)
			PAUSE(("3-D sound \"%s\" is being used as a 2-D sound!", sample->m_sampleResource.GetName()));
		else
			PAUSE(("2-D sound \"%s\" is being used as a 3-D sound!", sample->m_sampleResource.GetName()));
	}
#endif

	if (m_sample)
	{
		if (m_command)
		{
			Verify(m_sample->m_activeChannels.IsPlugMember(this));
			m_sample->m_activeChannels.RemovePlug(this);
		}
		else
		{
			Verify(m_sample->m_inactiveChannels.IsPlugMember(this));
			m_sample->m_inactiveChannels.RemovePlug(this);
		}
		gosAudio_AssignResourceToChannel(m_channel, NULL);
	}
	//
	// Causes a load, which needs a heap.
	//
	Verify(AudioRenderer::s_Heap);
	gos_PushCurrentHeap(AudioRenderer::s_Heap);
	sample->Load();
	gosAudio_AssignResourceToChannel(m_channel, sample->m_gosSample);
	gos_PopCurrentHeap();
	m_sample = sample;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioChannel::Execute(Stuff::Time till)
{
	Check_Object(this);
	Verify(m_playMode != gosAudio_Stop);
	Verify(!m_sample->m_3D);
	AUDIO_RENDER("Execute::AudioChannel");

	//
	//-----------------------------------------
	// If we have stopped, deactivate the sound
	//-----------------------------------------
	//
	gosAudio_PlayMode play_mode = gosAudio_GetChannelPlayMode(m_channel);
	switch (play_mode)
	{
	case gosAudio_Pause:
		return;

	case gosAudio_Stop:
		{
			//
			//-----------------------------------------------------------
			// If we lost focus, I guess we gotta start up our loop again
			//-----------------------------------------------------------
			//
			if (m_playMode == gosAudio_Loop)
			{
				gosAudio_SetChannelPlayMode(m_channel, m_playMode);
				goto Still_Playing;
			}
			Deactivate();

			//
			//-------------------------------------------------------------------
			// But if this is a queued channel type, play the next queued command
			//-------------------------------------------------------------------
			//
			Check_Object(AudioRenderer::Instance);
			ChannelType &type = AudioRenderer::Instance->m_channelTypes[m_type];
			if (type.IsQueued())
			{
				ChainIteratorOf<AudioCommand*> commands(&type.m_queuedCommands);
				AudioCommand *command = commands.GetCurrent();
				if (command)
				{
					Check_Object(command);
					commands.Remove();
					command->Play();
				}
			}
		}
		break;

	//
	//-----------------------------------------------------------------
	// Otherwise, update the energy of the channel for culling purposes
	//-----------------------------------------------------------------
	//
	default:
		Verify(m_playMode == play_mode);
Still_Playing:
		m_energy = m_command->ComputeEnergy(this);
		break;
	}
}
