#include <stdio.h>
#include <direct.h>
#include <string.h>
#include <cstdlib>
#include "mungal4.h"
#pragma hdrstop

#include "l4audres.h"
#include "l4audhdw.h"
#include "l4audio.h"
#include "l4audlvl.h"
#include "l4audwtr.h"
#include "..\munga\audcmp.h"
#include "..\munga\audseq.h"
#include "..\munga\audrend.h"
#include "..\munga\namelist.h"
#include "..\munga\app.h"
#include "..\munga\notation.h"
#include "openal\al.h"
#include "openal\alc.h"

ALuint *g_buffers;
int g_numBuffers;
int g_curAudioWatcherClass = 0;

//
// Minimal canonical-PCM WAV reader.  The repo's libsndfile-1.dll is a STUB
// (exports 15 funcs by ordinal only, no names -- sf_open always returns NULL
// and sf_strerror(NULL) faults), so the original sf_open path loaded nothing.
// Our soundbank samples (AUDIO1/2.RES, cracked by scratchpad/sf2extract.py)
// are written as plain 16-bit mono PCM WAVs, so a tiny RIFF/WAVE fmt+data
// parser loads the real sample data directly into the AL buffer -- no
// external dependency, no stub.  Handles 8/16-bit mono/stereo PCM.
//
static bool LoadWavPCM(const char *path, char **outData, int *outBytes,
                       int *outChannels, int *outBits, int *outRate)
{
	FILE *fp = fopen(path, "rb");
	if (!fp) return false;
	fseek(fp, 0, SEEK_END); long flen = ftell(fp); fseek(fp, 0, SEEK_SET);
	if (flen < 44) { fclose(fp); return false; }
	unsigned char *buf = new unsigned char[flen];
	size_t got = fread(buf, 1, flen, fp);
	fclose(fp);
	if ((long)got != flen) { delete [] buf; return false; }
	if (memcmp(buf, "RIFF", 4) != 0 || memcmp(buf + 8, "WAVE", 4) != 0) { delete [] buf; return false; }
	int channels = 0, bits = 0, rate = 0;
	char *pcm = 0; int pcmBytes = 0;
	bool haveFmt = false;
	long p = 12;
	while (p + 8 <= flen)
	{
		const unsigned char *id = buf + p;
		unsigned int csz = buf[p+4] | (buf[p+5]<<8) | (buf[p+6]<<16) | ((unsigned)buf[p+7]<<24);
		long body = p + 8;
		if (memcmp(id, "fmt ", 4) == 0 && csz >= 16)
		{
			channels = buf[body+2] | (buf[body+3]<<8);
			rate     = buf[body+4] | (buf[body+5]<<8) | (buf[body+6]<<16) | ((unsigned)buf[body+7]<<24);
			bits     = buf[body+14] | (buf[body+15]<<8);
			haveFmt = true;
		}
		else if (memcmp(id, "data", 4) == 0)
		{
			long avail = flen - body;
			pcmBytes = ((long)csz <= avail) ? (int)csz : (int)avail;
			if (pcmBytes < 0) pcmBytes = 0;
			pcm = new char[pcmBytes > 0 ? pcmBytes : 1];
			memcpy(pcm, buf + body, pcmBytes);
		}
		p = body + csz + (csz & 1);   // RIFF chunks are word-aligned
	}
	delete [] buf;
	if (!haveFmt || pcm == 0) { delete [] pcm; return false; }
	*outData = pcm; *outBytes = pcmBytes;
	*outChannels = channels; *outBits = bits; *outRate = rate;
	return true;
}

//#############################################################################
//#######################    AudioObjectStream    #############################
//#############################################################################

//
//#############################################################################
// AudioObjectStream
//#############################################################################
//
AudioObjectStream::AudioObjectStream(
	ResourceDescription *resource_description,
   Entity *the_entity
):
	PlugStream(resource_description)
{
	entity = the_entity;
}

//
//#############################################################################
// AudioObjectStream
//#############################################################################
//
AudioObjectStream::AudioObjectStream()
{
	entity = NULL;
}

//
//#############################################################################
// ~AudioObjectStream
//#############################################################################
//
AudioObjectStream::~AudioObjectStream()
{
}

//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
	AudioObjectStream::TestInstance() const
{
	PlugStream::TestInstance();
   if (entity != NULL)
   {
	   Check(entity);
   }
   return True;
}

//
//#############################################################################
// MakeObjectImplementation
//#############################################################################
//
RegisteredClass*
   AudioObjectStream::MakeObjectImplementation(Enumeration class_ID)
{
	Check(this);
   Verify(class_ID != RegisteredClass::NullClassID);

   // DIAG (BT_ATTRBIND_LOG): record which audio-object ClassID is being built so the
   // AttributeWatcher trace can print the watcher TYPE alongside the bound attribute.
   extern int g_curAudioWatcherClass;
   g_curAudioWatcherClass = (int)class_ID;

   //
   //-----------------------------------------------------------------------
   // HACK - Constructors should be referenced through registration, for
   // now just use a switch statement
   //-----------------------------------------------------------------------
   //
	RegisteredClass *object;

   switch (class_ID)
   {
   //
   // Static resources ...
   //
	case RegisteredClass::PatchLevelOfDetailClassID:
		object = new PatchLevelOfDetail(this);
   	break;

	case RegisteredClass::PatchResourceClassID:
   	object = new PatchResource(this);
   	break;

	case RegisteredClass::AudioResourceIndexClassID:
   	object = new AudioResourceIndex(this);
      break;

	//
   // Location and sources ...
   //
	case RegisteredClass::L4AudioLocationClassID:
   	object = new L4AudioLocation(this, entity);
      break;

	case RegisteredClass::DirectPatchSourceClassID:
   	object = new DirectPatchSource(this, entity);
   	break;

	case RegisteredClass::Dynamic3DPatchSourceClassID:
   	object = new Dynamic3DPatchSource(this, entity);
      break;

	case RegisteredClass::Static3DPatchSourceClassID:
   	object = new Static3DPatchSource(this, entity);
      break;

   //
   // Audio components ...
   //
	case RegisteredClass::AudioControlMixerClassID:
   	object = new AudioControlMixer(this, entity);
      break;

	case RegisteredClass::AudioControlMultiplierClassID:
   	object = new AudioControlMultiplier(this, entity);
      break;

	case RegisteredClass::AudioResourceSelectorClassID:
   	object = new AudioResourceSelector(this, entity);
      break;

	case RegisteredClass::AudioControlSmootherClassID:
   	object = new AudioControlSmoother(this, entity);
      break;

	case RegisteredClass::AudioSampleAndHoldClassID:
   	object = new AudioSampleAndHold(this, entity);
      break;

	case RegisteredClass::AudioControlSendClassID:
   	object = new AudioControlSend(this, entity);
      break;

	case RegisteredClass::AudioControlSequenceClassID:
		object = new AudioControlSequence(this, entity);
      break;

	case RegisteredClass::AudioControlSplitterClassID:
		object = new AudioControlSplitter(this, entity);
      break;

	case RegisteredClass::AudioLFOClassID:
		object = new AudioLFO(this, entity);
      break;

	//
   // Audio watchers ...
   //
	case RegisteredClass::AudioMotionTriggerClassID:
   	object = new AudioMotionTrigger(this, entity);
      break;

	case RegisteredClass::AudioMotionScaleClassID:
   	object = new AudioMotionScale(this, entity);
      break;

	case RegisteredClass::AudioHingeScaleClassID:
   	object = new AudioHingeScale(this, entity);
      break;

	case RegisteredClass::AudioScalarTriggerClassID:
   	object = new AudioScalarTrigger(this, entity);
      break;

	case RegisteredClass::AudioScalarScaleClassID:
   	object = new AudioScalarScale(this, entity);
      break;

	case RegisteredClass::AudioLogicalTriggerClassID:
		object = new AudioLogicalTrigger(this, entity);
		break;

	case RegisteredClass::AudioEnumerationTriggerClassID:
   	object = new AudioEnumerationTrigger(this, entity);
      break;

	case RegisteredClass::AudioIntegerTriggerClassID:
   	object = new AudioIntegerTrigger(this, entity);
      break;

	case RegisteredClass::AudioControlsButtonTriggerClassID:
		object = new AudioControlsButtonTrigger(this, entity);
		break;

	case RegisteredClass::AudioStateTriggerClassID:
		object = new AudioStateTrigger(this, entity);
		break;

	case RegisteredClass::AudioIdleWatcherClassID:
		object = new AudioIdleWatcher(this, entity);
      break;

	case RegisteredClass::AudioEnumerationDeltaTriggerClassID:
		object = new AudioEnumerationDeltaTrigger(this, entity);
		break;

	case RegisteredClass::AudioScalarDeltaTriggerClassID:
		object = new AudioScalarDeltaTrigger(this, entity);
		break;

	case RegisteredClass::L4AudioCollisionTriggerClassID:
		object = new L4AudioCollisionTrigger(this, entity);
		break;

	case RegisteredClass::AudioMessageWatcherClassID:
		object = new AudioMessageWatcher(this, entity);
		break;

	case RegisteredClass::AudioControlsButtonMessageWatcherClassID:
		object = new AudioControlsButtonMessageWatcher(this, entity);
		break;

	//
   // Inherited behavior
   //
   default:
      object = PlugStream::MakeObjectImplementation(class_ID);
      break;
   }
   return object;
}

//
//#############################################################################
// CreatedObjectImplementation
//#############################################################################
//
void
   AudioObjectStream::CreatedObjectImplementation(
      RegisteredClass *object,
      ObjectID object_ID
   )
{
	Check(this);
   Check(object);
   Verify(object_ID != NullObjectID);

   //
	// std::Decide which index to add object to
   //
	switch (object->GetClassID())
   {
	case RegisteredClass::PatchLevelOfDetailClassID:
	case RegisteredClass::PatchResourceClassID:
	case RegisteredClass::AudioResourceIndexClassID:
		AddGlobalPlug(Cast_Object(Plug*, object), object_ID);
      break;

	default:
		AddLocalPlug(Cast_Object(Plug*, object), object_ID);
      break;
   }
}

//
//#############################################################################
// BuildFromPageImplementation
//#############################################################################
//
void
   AudioObjectStream::BuildFromPageImplementation(
      NameList *name_list,
      Enumeration class_ID_enumeration,
      ObjectID object_ID
   )
{
	Check(this);
   Verify(class_ID_enumeration != RegisteredClass::NullClassID);
   Verify(object_ID != NullObjectID);

   //
   //-----------------------------------------------------------------------
   // HACK - Page interpreters should be referenced through registration,
   // for now just use a switch statement
   //-----------------------------------------------------------------------
   //
   RegisteredClass::ClassID
		class_ID = (RegisteredClass::ClassID)class_ID_enumeration;

	switch (class_ID)
   {
   //
	// Static resources ...
   //
	case RegisteredClass::PatchLevelOfDetailClassID:
		PatchLevelOfDetail::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::PatchResourceClassID:
		PatchResource::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::AudioResourceIndexClassID:
      AudioResourceIndex::BuildFromPage(this, name_list, class_ID, object_ID);
      break;

	//
   // Location and sources ...
   //
	case RegisteredClass::L4AudioLocationClassID:
      L4AudioLocation::BuildFromPage(this, name_list, class_ID, object_ID);
      break;

	case RegisteredClass::DirectPatchSourceClassID:
      DirectPatchSource::BuildFromPage(this, name_list, class_ID, object_ID);
   	break;

	case RegisteredClass::Dynamic3DPatchSourceClassID:
      Dynamic3DPatchSource::BuildFromPage(this, name_list, class_ID, object_ID);
      break;

	case RegisteredClass::Static3DPatchSourceClassID:
      Static3DPatchSource::BuildFromPage(this, name_list, class_ID, object_ID);
      break;

   //
   // Audio components ...
	//
	case RegisteredClass::AudioControlMixerClassID:
      AudioControlMixer::BuildFromPage(this, name_list, class_ID, object_ID);
      break;

	case RegisteredClass::AudioControlMultiplierClassID:
		AudioControlMultiplier::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::AudioResourceSelectorClassID:
      AudioResourceSelector::BuildFromPage(this, name_list, class_ID, object_ID);
      break;

	case RegisteredClass::AudioControlSmootherClassID:
      AudioControlSmoother::BuildFromPage(this, name_list, class_ID, object_ID);
      break;

	case RegisteredClass::AudioSampleAndHoldClassID:
      AudioSampleAndHold::BuildFromPage(this, name_list, class_ID, object_ID);
      break;

	case RegisteredClass::AudioControlSendClassID:
		AudioControlSend::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::AudioControlSequenceClassID:
		AudioControlSequence::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::AudioControlSplitterClassID:
		AudioControlSplitter::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::AudioLFOClassID:
		AudioLFO::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	//
   // Audio watchers ...
   //
	case RegisteredClass::AudioMotionTriggerClassID:
      AudioMotionTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
      break;

	case RegisteredClass::AudioMotionScaleClassID:
		AudioMotionScale::BuildFromPage(this, name_list, class_ID, object_ID);
      break;

	case RegisteredClass::AudioHingeScaleClassID:
      AudioHingeScale::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::AudioScalarTriggerClassID:
      AudioScalarTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::AudioScalarScaleClassID:
		AudioScalarScale::BuildFromPage(this, name_list, class_ID, object_ID);
      break;

	case RegisteredClass::AudioLogicalTriggerClassID:
      AudioLogicalTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
      break;

	case RegisteredClass::AudioEnumerationTriggerClassID:
		AudioEnumerationTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
      break;

	case RegisteredClass::AudioIntegerTriggerClassID:
		AudioIntegerTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::AudioControlsButtonTriggerClassID:
		AudioControlsButtonTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::AudioStateTriggerClassID:
		AudioStateTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::AudioIdleWatcherClassID:
		AudioIdleWatcher::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::AudioEnumerationDeltaTriggerClassID:
		AudioEnumerationDeltaTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::AudioScalarDeltaTriggerClassID:
		AudioScalarDeltaTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::L4AudioCollisionTriggerClassID:
		L4AudioCollisionTrigger::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::AudioMessageWatcherClassID:
		AudioMessageWatcher::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	case RegisteredClass::AudioControlsButtonMessageWatcherClassID:
		AudioControlsButtonMessageWatcher::BuildFromPage(this, name_list, class_ID, object_ID);
		break;

	//
   // Inherited behavior
   //
   default:
   	PlugStream::BuildFromPageImplementation(name_list, class_ID, object_ID);
      break;
   }
}

//
//#############################################################################
// BuiltFromPageImplementation
//#############################################################################
//
void
   AudioObjectStream::BuiltFromPageImplementation(
      Enumeration class_ID,
      ObjectID object_ID,
      const CString &object_name
   )
{
	Check(this);
   Verify(class_ID != RegisteredClass::NullClassID);
   Verify(object_ID != NullObjectID);
   Check(&object_name);

	//
   // std::Decide which index to add object to
   //
   if (object_name.Length() == 0)
		return;

   switch (class_ID)
   {
   case RegisteredClass::PatchLevelOfDetailClassID:
	case RegisteredClass::PatchResourceClassID:
	case RegisteredClass::AudioResourceIndexClassID:
   	AddGlobalObjectID(object_ID, object_name);
      break;

   default:
   	AddLocalObjectID(object_ID, object_name);
      break;
   }
}

//#############################################################################
//######################    L4AudioResourceManager    #########################
//#############################################################################

//
//#############################################################################
// L4AudioResourceManager
//#############################################################################
//
L4AudioResourceManager::L4AudioResourceManager()
{
	g_buffers = NULL;
	g_numBuffers = 0;
}

//
//#############################################################################
// ~L4AudioResourceManager
//#############################################################################
//
L4AudioResourceManager::~L4AudioResourceManager()
{
}

//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
	L4AudioResourceManager::TestInstance() const
{
	Node::TestInstance();
	return True;
}

//
//#############################################################################
// PreloadResources
//#############################################################################
//
void
	L4AudioResourceManager::PreloadResources(
		NotationFile *notation_file
	)
{
	Check(this);
	Check(notation_file);
	//Check(audio_hardware);

	//Determine how many loadable samples exist
	g_numBuffers=0;

	for(int i=1; i <= 2; i++)
	{
		for (int j=0; j < 128; j++)
		{
			if (PRESET_isImplemented(i,j))
			{
				g_numBuffers += PRESET_getNumSamples(i,j);
			}
		}
	}

	//Generate the buffers
	if (g_numBuffers > 0)
	{
		//Generate buffers
		alGetError();
		g_buffers = new ALuint[g_numBuffers];
		alGenBuffers(g_numBuffers,g_buffers);
		if (getenv("BT_AUDIO_LOG")) DEBUG_STREAM << "[audio] alGenBuffers count=" << g_numBuffers << "\n" << std::flush;
		ALenum error;
		if ((error = alGetError()) != AL_NO_ERROR)
		{
			DEBUG_STREAM << "OpenAL has encountered an error while initializing the buffers." << std::endl;
			delete [] g_buffers;
			g_buffers = NULL;
			g_numBuffers = 0;
		}
	}

	int bufferInd = 0;
	//Load buffers
	for(int i=1; i<=2 && bufferInd < g_numBuffers; i++)
	{
		for(int j=0; j<128 && bufferInd < g_numBuffers; j++)
		{
			if (PRESET_isImplemented(i,j))
			{
				for (int k=0; k < PRESET_getNumSamples(i,j) && bufferInd < g_numBuffers; k++)
				{
					SAMPLEINFO info = PRESET_getSampleInfo(i,j,k);

					//Open WAV
					char fullname[50];
					strcpy_s(fullname,50,"AUDIO/");
					strcat_s(fullname,50,info.file);
					char *data = 0; int size = 0;
					int wavCh = 0, wavBits = 0, wavRate = 0;
					if (!LoadWavPCM(fullname, &data, &size, &wavCh, &wavBits, &wavRate))
					{
						if (getenv("BT_AUDIO_LOG") && bufferInd == 0) {
							char cwd[512]; cwd[0]=0; _getcwd(cwd, sizeof(cwd));
							DEBUG_STREAM << "[audio] DIAG cwd=[" << cwd << "] path=[" << fullname << "] WAV load FAILED\n" << std::flush;
						}
						DEBUG_STREAM << "[audio] Failed to open AUDIO/" << info.file << " -- skipping\n" << std::flush;
						PRESET_setBufferIndex(i,j,k,bufferInd);
						bufferInd++;
						continue;
					}

					ALenum format;
					if (wavBits == 8)
						format = (wavCh == 1) ? AL_FORMAT_MONO8 : AL_FORMAT_STEREO8;
					else
						format = (wavCh == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;

					//Feed the buffer with the real PCM sample data
					alBufferData(g_buffers[bufferInd], format, data, size, wavRate);
					if (getenv("BT_AUDIO_LOG") && bufferInd < 3)
						DEBUG_STREAM << "[audio] loaded buf" << bufferInd << " " << info.file
							<< " ch=" << wavCh << " bits=" << wavBits << " rate=" << wavRate
							<< " bytes=" << size << " alErr=" << alGetError() << "\n" << std::flush;
					PRESET_setBufferIndex(i,j,k,bufferInd);
					bufferInd++;
					delete [] data;
				}
			}
		}
	}

	if (getenv("BT_AUDIO_TEST") && g_numBuffers > 0)
	{
		// PROOF OF LIFE: play the first loaded sample so we can confirm the
		// SF2 -> WAV -> OpenAL buffer -> speakers path works end to end.
		ALCcontext *ctx = alcGetCurrentContext();
		alGetError();
		ALuint testsrc = 0; alGenSources(1, &testsrc);
		ALenum genErr = alGetError();
		alSourcei(testsrc, AL_BUFFER, g_buffers[0]);
		ALenum bufErr = alGetError();
		alSourcei(testsrc, AL_LOOPING, AL_TRUE);
		alSourcef(testsrc, AL_GAIN, 1.0f);
		alSourcePlay(testsrc);
		ALenum playErr = alGetError();
		ALint st = 0; alGetSourcei(testsrc, AL_SOURCE_STATE, &st);
		DEBUG_STREAM << "[audio] TEST-PLAY buffer0 src=" << testsrc
			<< " ctx=" << (void*)ctx
			<< " genErr=" << genErr << " bufErr=" << bufErr << " playErr=" << playErr
			<< " state=" << st << " playing=" << (int)(st==AL_PLAYING) << "\n" << std::flush;
	}

	//
	//----------------------------------------------------------------------
	// Load the sound banks
	//----------------------------------------------------------------------
	//
	/*NameList *name_list;
	NameList::Entry *entry;

	Verify(notation_file->EntryCount("AudioResources") >= 2);

	name_list = notation_file->MakeEntryList("AudioResources");
	Register_Object(name_list);
	entry = name_list->GetFirstEntry();
	Check(entry);
	while (entry != NULL)
	{
		Check(entry);
		if (entry->IsName("front_audio_resource"))
		{
			Check_Pointer(entry->GetChar());
			Check(audio_hardware->GetFrontCard());
			audio_hardware->GetFrontCard()->LoadSBK(entry->GetChar());
		}
		else
		{
			Verify(entry->IsName("rear_audio_resource"));
			Check_Pointer(entry->GetChar());
			Check(audio_hardware->GetRearCard());
			audio_hardware->GetRearCard()->LoadSBK(entry->GetChar());
		}
		entry = entry->GetNextEntry();
	}
	Unregister_Object(name_list);
   delete name_list;*/

	//
	//----------------------------------------------------------------------
	// Create static audio objects
	//----------------------------------------------------------------------
	//

	//
	// Get static audio stream resource
	//
	ResourceDescription *resource_description;

   Check(application);
   Check(application->GetResourceFile());
   resource_description =
      application->GetResourceFile()->FindResourceDescription(
         "StaticAudioStream",
         ResourceDescription::StaticAudioStreamResourceType
		);
	Check(resource_description);
	resource_description->Lock();

	//
	// Parse the audio object stream
	//
	AudioObjectStream audio_object_stream(resource_description, NULL);

	Check(&audio_object_stream);
	audio_object_stream.CreateObjects();
	resource_description->Unlock();
}

ALuint AL_getBuffer(int index)
{
	return g_buffers[index];
}

//
//#############################################################################
// UnloadResources
//#############################################################################
//
void
   L4AudioResourceManager::UnloadResources(
	)
{
	Check(this);
//   Check(audio_hardware);

	//Unload wav data
	if (g_numBuffers > 0)
	{
		alGetError();
		alDeleteBuffers(g_numBuffers,g_buffers);
		ALenum error = alGetError();
		if (error != AL_NO_ERROR)
		{
			DEBUG_STREAM << "OpenAL reached an error when attempting to delete its buffers." << std::endl;
		}
	}

	//
	//----------------------------------------------------------------------
	// Release banks
	//----------------------------------------------------------------------
	//
//   Check(audio_hardware->GetFrontCard());
//   audio_hardware->GetFrontCard()->ReleaseAllBanks();
//   Check(audio_hardware->GetRearCard());
//   audio_hardware->GetRearCard()->ReleaseAllBanks();
}

//
//#############################################################################
// CreateStaticAudioStreamResource
//#############################################################################
//
ResourceDescription::ResourceID
   L4AudioResourceManager::CreateStaticAudioStreamResource(
      ResourceFile *resource_file
   )
{
   Check(resource_file);

	//
	//----------------------------------------------------------------------
	// Open the notation file for the static stream resource
	//----------------------------------------------------------------------
	//
   Tell("Opening Audio Script audio\\static.scp\n");
   NotationFile notation_file("audio\\static.scp");
   Check(&notation_file);

	//
	//----------------------------------------------------------------------
	// Build the static audio object stream
	//----------------------------------------------------------------------
	//
	AudioObjectStream audio_object_stream;

   Check(&audio_object_stream);
   audio_object_stream.BuildFromNotationFile(&notation_file, resource_file);
   Tell("Closed Audio Script\n");

   //
	//----------------------------------------------------------------------
   // Add the resource
	//----------------------------------------------------------------------
   //
   ResourceDescription *res_description;

   res_description =
      resource_file->AddResourceMemoryStream(
         "StaticAudioStream",
         ResourceDescription::StaticAudioStreamResourceType,
         1,
         ResourceDescription::Preload,
         &audio_object_stream
      );
	Check(res_description);
	return res_description->resourceID;
}

//
//#############################################################################
// CreateEntityAudioObjects
//#############################################################################
//
void
	L4AudioResourceManager::CreateEntityAudioObjects(
      Entity *entity,
		AudioRepresentation audio_representation
   )
{
	Check(this);
	Check(entity);
	Verify(
		audio_representation == InternalAudioRepresentation ||
		audio_representation == ExternalAudioRepresentation
	);

	//
	//------------------------------------------------------------------------
	// Read the audio resource for this entity
	//------------------------------------------------------------------------
	//
	ResourceDescription *audio_resource_description;

	Check(application);
	Check(application->GetResourceFile());
	audio_resource_description =
		application->GetResourceFile()->SearchList(
			entity->GetResourceID(),
			ResourceDescription::AudioStreamListResourceType
		);
	if (audio_resource_description == NULL)
		return;

	//
	//------------------------------------------------------------------------
	// Read the appropriate resource for an internal or external
	// representation
	//------------------------------------------------------------------------
	//
	ResourceDescription *stream_resource_description;

	Check(application);
	Check(application->GetResourceFile());
	Check(audio_resource_description);
	stream_resource_description =
		application->GetResourceFile()->SearchList(
			audio_resource_description->resourceID,
			(audio_representation == InternalAudioRepresentation) ?
				ResourceDescription::InternalAudioStreamResourceType :
				ResourceDescription::ExternalAudioStreamResourceType
		);
	if (stream_resource_description == NULL)
		return;
	Check(stream_resource_description);
	stream_resource_description->Lock();
	audio_resource_description->Lock();

	//
	//------------------------------------------------------------------------
	// Make the audio object stream
	//------------------------------------------------------------------------
	//
	AudioObjectStream
		audio_object_stream(
			stream_resource_description,
			entity
		);

	Check(&audio_object_stream);
	audio_object_stream.CreateObjects();
	stream_resource_description->Unlock();
	audio_resource_description->Unlock();
}

//
//#############################################################################
// DestroyEntityAudioObjects
//#############################################################################
//
void
	L4AudioResourceManager::DestroyEntityAudioObjects(Entity *entity)
{
	Check(this);
	Check(entity);

	//
	//--------------------------------------------------------------------------
	// Stop and delete all audio objects
	//--------------------------------------------------------------------------
	//
	Entity::AudioSocketIterator 
		iterator(entity);
	Component 
		*component;
	AudioComponent 
		*audio_component;

	Check(&iterator);
	while ((component = iterator.ReadAndNext()) != NULL)
	{
		audio_component = Cast_Object(AudioComponent*, component);
		Check(audio_component);
		audio_component->ReceiveControl(StopAudioControlID, 0.0f);
		audio_component->ReceiveControl(FlushMessagesAudioControlID, 0.0f);
	}
	iterator.DeletePlugs();
}

//
//#############################################################################
// CreateModelAudioResource
//#############################################################################
//
ResourceDescription::ResourceID
	L4AudioResourceManager::CreateModelAudioStreamResource(
		ResourceFile *resource_file,
		const char *model_name,
		NotationFile *model_file,
		const ResourceDirectories*
	)
{
	Check(resource_file);
	Check_Pointer(model_name);
	Check(model_file);

	const char *script_name = NULL;
	ResourceDescription::ResourceID resource_id_list[3];
	size_t list_length = 0;

	resource_id_list[0] = ResourceDescription::NullResourceID;
	resource_id_list[1] = ResourceDescription::NullResourceID;
	resource_id_list[2] = ResourceDescription::NullResourceID;

	//
	//--------------------------------------------------------------------------
	// Check for the external audio script
	//--------------------------------------------------------------------------
	//
	if (model_file->GetEntry("audio", "external", &script_name))
	{
		//
		// See if the resource already exists
		//
		ResourceDescription *res_description =
			resource_file->FindResourceDescription(
				script_name,
				ResourceDescription::ExternalAudioStreamResourceType
			);

		//
		//-----------------------------------------------------------------------
		// If not, create it
		//-----------------------------------------------------------------------
		//
		if (res_description == NULL)
		{
			//
			// Make the file name
			//
			CString file_name(script_name);
			CString prefix("audio\\");

         file_name = prefix + file_name;

         //
			// Build the memory stream
			//
			Tell("Opening Audio Script " << file_name << "\n");
         NotationFile script_file(file_name);
			AudioObjectStream audio_object_stream;

			Check(&script_file);
         Check(&audio_object_stream);
			audio_object_stream.BuildFromNotationFile(&script_file, resource_file);
			Tell("Closed Audio Script\n");

			//
			// Add the resource
         //
			res_description =
            resource_file->AddResourceMemoryStream(
					script_name,
               ResourceDescription::ExternalAudioStreamResourceType,
					1,
               ResourceDescription::Preload,
					&audio_object_stream
            );
		}
      Check(res_description);
		resource_id_list[list_length++] = res_description->resourceID;
	}

	//
	//--------------------------------------------------------------------------
	// Check for the internal audio script
	//--------------------------------------------------------------------------
	//
	if (model_file->GetEntry("audio", "internal", &script_name))
   {
		//
      // See if the resource already exists
		//
      ResourceDescription *res_description =
			resource_file->FindResourceDescription(
            script_name,
				ResourceDescription::InternalAudioStreamResourceType
			);

		//
		// If not, create it
      //
		if (res_description == NULL)
      {
			//
			// Make the file name
			//
      	CString file_name(script_name);
			CString prefix("audio\\");

			file_name = prefix + file_name;

			//
         // Build the memory stream
			//
		   Tell("Opening Audio Script " << file_name << "\n");
			NotationFile script_file(file_name);
         AudioObjectStream audio_object_stream;

			Check(&script_file);
			Check(&audio_object_stream);
         audio_object_stream.BuildFromNotationFile(&script_file, resource_file);
			Tell("Closed Audio Script\n");

			//
			// Add the resource
			//
         res_description =
				resource_file->AddResourceMemoryStream(
               script_name,
					ResourceDescription::InternalAudioStreamResourceType,
					1,
					ResourceDescription::Preload,
               &audio_object_stream
				);
      }
		Check(res_description);
		resource_id_list[list_length++] = res_description->resourceID;
	}

	//
	//--------------------------------------------------------------------------
	// Check for the audio entity model
	//--------------------------------------------------------------------------
	//
	const char *audio_entity_model = NULL;

	if (model_file->GetEntry("audio", "model", &audio_entity_model))
	{
		//
		// The resource should already exist
		//
		ResourceDescription *res_description =
			resource_file->FindResourceDescription(
				audio_entity_model,
				ResourceDescription::ModelListResourceType
			);
		if (res_description == NULL)
		{
			std::cout << "audio_entity_model == " << audio_entity_model;
			Fail("L4AudioResourceManager::CreateModelAudioStreamResource - model not found");
		}
		Check(res_description);
		resource_id_list[list_length++] = res_description->resourceID;
	}

	//
	//--------------------------------------------------------------------------
	// If either script has been created, then create the resource list
	//--------------------------------------------------------------------------
	//
	if (list_length > 0)
	{
		ResourceDescription *res_description =
			resource_file->AddResourceList(
				model_name,
				ResourceDescription::AudioStreamListResourceType,
				1,
				ResourceDescription::Preload,
				resource_id_list,
				list_length
			);
		Check(res_description);
		return res_description->resourceID;
	}
	return ResourceDescription::NullResourceID;
}
