#include "mungal4.h"
#pragma hdrstop

#include "l4audlvl.h"
#include "l4audres.h"
#include "..\munga\audrend.h"
#include "..\munga\objstrm.h"
#include "..\munga\namelist.h"
#include "openal/al.h"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PatchLevelOfDetail ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#if DEBUG_LEVEL>0
	TableOf<PatchLevelOfDetail*, unsigned int>
		PatchLevelOfDetail::patchTableSocket(NULL, True);
#endif

//
//#############################################################################
//#############################################################################
//
PatchLevelOfDetail::PatchLevelOfDetail(PlugStream *stream):
	AudioLevelOfDetail(stream)
{
	MemoryStream_Read(stream, &bankID);
	MemoryStream_Read(stream, &patchID);
	MemoryStream_Read(stream, &maxMIDIFilterCutoff);

	Warn(GetVoiceCount() > 4); // HACK - AWE appears to only play 1st 4 voices

	#ifdef LAB_ONLY
		setupCount = 0;
	#endif

	//
	// Keep table of created patchs to verify that duplicates
	// are not created, could move this to tool time
	//
	#if DEBUG_LEVEL>0
		unsigned int index_value = (bankID * 1000) + patchID;
		if (patchTableSocket.Find(index_value) != NULL)
		{
			Dump((int)bankID);
			Dump((int)patchID);
		}
		Verify(patchTableSocket.Find(index_value) == NULL);
		patchTableSocket.AddValue(this, index_value);
	#endif
}

//
//#############################################################################
//#############################################################################
//
void
	PatchLevelOfDetail::BuildFromPage(
		PlugStream *stream,
		NameList *name_list,
		ClassID class_ID,
		ObjectID object_ID
	)
{
	AudioLevelOfDetail::BuildFromPage(stream, name_list, class_ID, object_ID);

	//
	// Store fields
	//
	MEM_STRM_WRITE_ENTRY(*stream, name_list, SBKBankID, bank_ID);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, SBKPatchID, patch_ID);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, MIDINRPNValue, max_filter_cutoff);
}

//
//#############################################################################
//#############################################################################
//
PatchLevelOfDetail::~PatchLevelOfDetail()
{
	#ifdef LAB_ONLY
		cout << "PatchLevelOfDetail::~PatchLevelOfDetail\t";
		cout << (int)bankID << ":" << (int)patchID << "\t";
		cout << "setupCount=\t" << setupCount << "\n";
	#endif

	/*if (m_numSources > 0 && m_sources != NULL)
	{
		for (int i=0; i < m_numSources; i++)
		{
			//Detach buffer
			alSourcei(m_sources[i],AL_BUFFER,0);
			//Stop source
			ALenum state;
			alGetSourcei(m_sources[i],AL_SOURCE_STATE,&state);
			if (state == AL_PLAYING)
			{
				alSourceStop(m_sources[i]);
			}
		}

		//Destroy all sources
		alDeleteSources(m_numSources,m_sources);
	}*/
}

//
//#############################################################################
//#############################################################################
//
Logical
	PatchLevelOfDetail::TestInstance() const
{
	AudioLevelOfDetail::TestInstance();
	return True;
}

//
//#############################################################################
//#############################################################################
//
void
	PatchLevelOfDetail::SetupPatch(SourceSet sourceSet)
{
//	Check(this);
////	Check(channel);
//
//	#ifdef LAB_ONLY
//		setupCount++;
//	#endif
   SAMPLEINFO info;

	//Attach buffers
	for (int i=0; i < sourceSet.count; i++)
	{
		info = PRESET_getSampleInfo(bankID,patchID,i);
		if (info.bufferIndex >= 0)
		{
			ALenum sourceState;
			alGetSourcei(sourceSet.sources[i], AL_SOURCE_STATE, &sourceState);

			if (sourceState == AL_INITIAL)
			{
				alSourcei(sourceSet.sources[i],AL_BUFFER,AL_getBuffer(info.bufferIndex));
				alSourcei(sourceSet.sources[i],AL_SOURCE_RELATIVE,AL_TRUE);

				AudioRenderer *render = application->GetAudioRenderer();
				AudioHead *head = render->GetAudioHead();

				alSource3f(sourceSet.sources[i],AL_POSITION,0,0,0);
				alSource3f(sourceSet.sources[i],AL_VELOCITY,0,0,0);

				if (info.loop == ForceStatic)
				{
					alSourcei(sourceSet.sources[i],AL_LOOPING,0);
				} else
				{
					alSourcei(sourceSet.sources[i],AL_LOOPING,1);
				}
			}
		}
	}

	//
	// Set the channel to the bank and program, reset patch controls
	//
/*	channel->SelectBank(bankID);
	channel->SendProgramChange(patchID);
	channel->SendController(MIDI_CONTROL_RESET, MIDI_MAX_CONTROL_VALUE);*/

	//TODO:  Sync up OpenAL source info appropriately
	
	/*#if 0
		Tell((int)bankID << ":" << (int)patchID << "\n");
	#endif*/
}

void PatchLevelOfDetail::PlayNote(SourceSet sourceSet)
{
	if (sourceSet.count > 0 && sourceSet.sources != NULL)
	{
		alGetError();
		//DEBUG_STREAM << "Playing bank " << (int)bankID << ", patch " << (int)patchID << std::endl;

		for (int i = 0; i < sourceSet.count; i++)
		{
			ALenum state;
			alGetSourcei(sourceSet.sources[i], AL_SOURCE_STATE, &state);

			if (state != AL_PLAYING)
			{
				alSourcePlay(sourceSet.sources[i]);
			}

			ALenum error = alGetError();
			if (error != AL_NO_ERROR)
			{
				DEBUG_STREAM << "Playing hit an error: " << error << std::endl;
				return;
			}
		}
	}
}

void PatchLevelOfDetail::StopNote(SourceSet sourceSet)
{
	if (sourceSet.count >0 && sourceSet.sources != NULL)
	{
		alSourceStopv(sourceSet.count, sourceSet.sources);
		alSourceRewindv(sourceSet.count, sourceSet.sources);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PatchResource ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//
PatchResource::PatchResource(PlugStream *stream):
	AudioResource(stream)
{
}

//
//#############################################################################
//#############################################################################
//
void
	PatchResource::BuildFromPage(
		PlugStream *stream,
		NameList *name_list,
		ClassID class_ID,
		ObjectID object_ID
	)
{
	AudioResource::BuildFromPage(stream, name_list, class_ID, object_ID);
}

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

//
//#############################################################################
//#############################################################################
//
Logical
	PatchResource::TestInstance() const
{
	AudioResource::TestInstance();
	return True;
}

//
//#############################################################################
//#############################################################################
//
void
	PatchResource::SetupPatch(SourceSet sourceSet)
{
	Check(this);
//	Check(channel);

	PatchLevelOfDetail *patch_level_of_detail =
		Cast_Object(PatchLevelOfDetail*, GetAudioLevelOfDetail());

	Check(patch_level_of_detail);
	patch_level_of_detail->SetupPatch(sourceSet);
}

void PatchResource::PlayNote(SourceSet sourceSet)
{
	Check(this);
//	Check(channel);

	PatchLevelOfDetail *patch_level_of_detail =
		Cast_Object(PatchLevelOfDetail*, GetAudioLevelOfDetail());

	Check(patch_level_of_detail);
	patch_level_of_detail->PlayNote(sourceSet);
}

void PatchResource::StopNote(SourceSet sourceSet)
{
	Check(this);
//	Check(channel);

	PatchLevelOfDetail *patch_level_of_detail =
		Cast_Object(PatchLevelOfDetail*, GetAudioLevelOfDetail());

	Check(patch_level_of_detail);
	patch_level_of_detail->StopNote(sourceSet);
}

//
//#############################################################################
//#############################################################################
//
MIDINRPNValue
	PatchResource::GetMaxMIDIFilterCutoff()
{
	Check(this);

	PatchLevelOfDetail *patch_level_of_detail =
		Cast_Object(PatchLevelOfDetail*, GetAudioLevelOfDetail());

	Check(patch_level_of_detail);
	return patch_level_of_detail->GetMaxMIDIFilterCutoff();
}
