#include "munga.h"
#pragma hdrstop

#include "audrend.h"
#include "audent.h"
#include "jmover.h"
#include "app.h"

//#############################################################################
//###########################    AudioRenderer    #############################
//#############################################################################

//
//#############################################################################
// AudioRenderer
//#############################################################################
//
AudioRenderer::AudioRenderer(RendererRate render_rate):
	Renderer(
		render_rate,
		MaxRendererComplexity,
		DefaultRendererPriority,
		AudioInterestType,
		DefaultInterestDepth,
		AudioRendererClassID
	),
	audioEventSocket(NULL, False)
{
	audioHead = NULL;

	#ifdef LAB_ONLY
		sourceClippedCount = 0;
		sourceStartCount = 0;
		sourceSuspendCount = 0;
		sourceResumeCount = 0;
		doubleTriggerCount = 0;
		resourceStealCount = 0;
		resourceStealPriorityCount = 0;
		resourceStealVolumeCount = 0;
		stealShortDurationCount = 0;
	#endif
}

//
//#############################################################################
// ~AudioRenderer
//#############################################################################
//
AudioRenderer::~AudioRenderer()
{
	Unregister_Object(audioHead);
	delete audioHead;

	#ifdef LAB_ONLY
		cout << "AudioRenderer::~AudioRenderer - sourceClippedCount=" 
			<< sourceClippedCount << "\n";
		cout << "AudioRenderer::~AudioRenderer - sourceStartCount=" 
			<< sourceStartCount << "\n";
		cout << "AudioRenderer::~AudioRenderer - sourceSuspendCount=" 
			<< sourceSuspendCount << "\n";
		cout << "AudioRenderer::~AudioRenderer - sourceResumeCount=" 
			<< sourceResumeCount << "\n";
		cout << "AudioRenderer::~AudioRenderer - doubleTriggerCount=" 
			<< doubleTriggerCount << "\n";
		cout << "AudioRenderer::~AudioRenderer - resourceStealCount=" 
			<< resourceStealCount << "\n";
		cout << "AudioRenderer::~AudioRenderer - resourceStealPriorityCount=" 
			<< resourceStealPriorityCount << "\n";
		cout << "AudioRenderer::~AudioRenderer - resourceStealVolumeCount=" 
			<< resourceStealVolumeCount << "\n";
		cout << "AudioRenderer::~AudioRenderer - stealShortDurationCount=" 
			<< stealShortDurationCount << "\n";
	#endif
}

//
//#############################################################################
// Initialize
//#############################################################################
//
void
	AudioRenderer::Initialize()
{
	audioHead = MakeAudioHead();
   Register_Object(audioHead);
}

//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
	AudioRenderer::TestInstance() const
{
	Renderer::TestInstance();
	if (audioHead != NULL)
	{
		Check(audioHead);
	}
	Check(&audioEventSocket);
	return True;
}

//
//#############################################################################
// LinkToEntity
//#############################################################################
//
void
	AudioRenderer::LinkToEntity(Entity *entity)
{
	Check(this);
	Check(entity);

	//
	//--------------------------------------------------------------------------
	// Link the head to the entity
	//--------------------------------------------------------------------------
	//
	Check(audioHead);
	audioHead->LinkToEntity(entity);

	//
	//--------------------------------------------------------------------------
	// Call inherited method
	//--------------------------------------------------------------------------
	//
	Renderer::LinkToEntity(entity);
}

//
//#############################################################################
// ExecuteImplementation
//#############################################################################
//
void
	AudioRenderer::ExecuteImplementation(
		RendererComplexity,
		RendererOrigin::InterestingEntityIterator*
	)
{
	Check(this);
	Check(audioHead);
	audioHead->Execute();
}

//
//#############################################################################
// PostAudioRequestMessage
//#############################################################################
//
void
	AudioRenderer::PostAudioRequestMessage(
		AudioSource *audio_source,
		AudioSource::RequestMessage *message
	)
{
	Check(this);
	Check(audio_source);
	Check(message);
	
	// 
	//--------------------------------------------------------------------------
	// If audio source is clipped and the source is transient 
	// Then ignore
	// Else set priority and volume
	//--------------------------------------------------------------------------
	// 
	AudioSourcePriority
		audio_source_priority;
	AudioControlValue 
		audio_source_volume_scale;
		
	if (audio_source->IsAudioSourceClipped(GetAudioHead()))
	{
		//
		// If it is a transient source then ignore request
		//
		if (audio_source->GetAudioRenderType() == TransientAudioRenderType)
		{
			#ifdef LAB_ONLY
				sourceClippedCount++;
			#endif		
			return;
		}
		audio_source_priority = audio_source->GetAudioSourcePriority();
		audio_source_volume_scale = 0.0f;
	}
	else
	{
		audio_source_priority = audio_source->GetAudioSourcePriority();
		audio_source_volume_scale = audio_source->CalculateSourceVolumeScale();
	}
	
	//
	//--------------------------------------------------------------------------
	// If this a transient source and volume is lower than threshold
	// Then return
	//--------------------------------------------------------------------------
	//
	if (
		message->controlID == StartAudioControlID &&
		audio_source->GetAudioRenderType() == TransientAudioRenderType &&
		audio_source_volume_scale < LowAudioVolumeThreshold
	)
	{
		#ifdef LAB_ONLY
			sourceClippedCount++;
		#endif		
		return;
	}
	
	//
	//--------------------------------------------------------------------------
	// Create audio weight based on priority and volume
	// Add audio event
	//--------------------------------------------------------------------------
	//
	AudioWeighting 
		audio_weight(audio_source_priority, audio_source_volume_scale);
	AudioEvent	
		*audio_event;

	audio_event = new AudioEvent(audio_source, message);
	Register_Object(audio_event);
	audioEventSocket.AddValue(audio_event, audio_weight);
}

//
//#############################################################################
// ProcessAudioRequestMessage
//#############################################################################
//
Logical
	AudioRenderer::ProcessAudioRequestMessage()
{
	//
	// Process high priority, high volume event
	//
	AudioEventIterator
		iterator(&audioEventSocket);
	AudioEvent 
		*audio_event;

	Logical stuff = False;
	while ((audio_event = iterator.GetCurrent()) != NULL)
	{
		stuff = True;
		Check(audio_event);
		audio_event->Process();
		//return True;
	}
	return stuff;
}	

//
//#############################################################################
// FlushAudioMessages
//#############################################################################
//
void
	AudioRenderer::FlushAudioMessages(AudioSource *audio_source)
{
	Check(this);
	Check(audio_source);
	
	//
	// Process matching events
	//
	AudioEventIterator
		iterator(&audioEventSocket);
	AudioEvent 
		*audio_event;

	while ((audio_event = iterator.ReadAndNext()) != NULL)
	{
		Check(audio_event);
		if (audio_event->targetReceiver.GetCurrent() == audio_source)
		{
			if (audio_event->messageToSend->controlID == StopAudioControlID)
			{
				audio_event->Process();							
			}
			else
			{
				Unregister_Object(audio_event);
				delete audio_event;
			}
		}
	}
}

//
//#############################################################################
// MakeAudioHead
//#############################################################################
//
AudioHead*
	AudioRenderer::MakeAudioHead()
{
	return new AudioHead;
}

//
//#############################################################################
// StartEntityEffectImplementation
//#############################################################################
//
void
	AudioRenderer::StartEntityEffectImplementation(
		Entity *parent_entity,
		DamageZone *damage_zone,
		ResourceDescription::ResourceID resource_ID
	)
{
	SET_AUDIO_RENDERER();

	Check(this);
	Check(parent_entity);
	Check(damage_zone);
	Verify(resource_ID != ResourceDescription::NullResourceID);

	//
	//--------------------------------------------------------------------------
	// Get the audio resource for this effect
	//--------------------------------------------------------------------------
	//
	ResourceDescription *audio_resource_description;

	Check(application);
	Check(application->GetResourceFile());
	audio_resource_description =
		application->GetResourceFile()->SearchList(
			resource_ID,
			ResourceDescription::AudioStreamListResourceType
		);
	if (audio_resource_description == NULL)
	{
		CLEAR_AUDIO_RENDERER();
		return;
	}
	Check(audio_resource_description);

	//
	//--------------------------------------------------------------------------
	// Get the resource ID for the model resource for the audio effect entity
	//--------------------------------------------------------------------------
	//
	ResourceDescription *model_resource_description;

	Check(application);
	Check(application->GetResourceFile());
	Check(audio_resource_description);
	model_resource_description =
		application->GetResourceFile()->SearchList(
			audio_resource_description->resourceID,
			ResourceDescription::ModelListResourceType
		);
	if (model_resource_description == NULL)
	{
		CLEAR_AUDIO_RENDERER();
		return;
	}
	Check(model_resource_description);
	model_resource_description->Lock();
	audio_resource_description->Lock();

	//
	//--------------------------------------------------------------------------
	// Get the entity segment
	//--------------------------------------------------------------------------
	//
	Entity *linked_entity;
	AudioRepresentation audio_representation;
	EntitySegment *parent_entity_segment;

	linked_entity = GetLinkedEntity();
	Check(linked_entity);
	audio_representation =
		(AudioRepresentation)parent_entity->GetAudioRepresentation(
			linked_entity
		);

	parent_entity_segment = damage_zone->GetCurrentEffectSite(
		(audio_representation == InternalAudioRepresentation) ?
			DamageZone::InternalAudioEffectSite :
			DamageZone::ExternalAudioEffectSite
	);
	Check(parent_entity_segment);

	//
	//--------------------------------------------------------------------------
	// Create the audio effect entity with this resource
	//--------------------------------------------------------------------------
	//
	JointedMover *jointed_mover;

	Verify(parent_entity->IsDerivedFrom(JointedMover::GetClassDerivations()));
	jointed_mover = Cast_Object(JointedMover*, parent_entity);
	Check(jointed_mover);

	AudioEntity::MakeMessage
		make_message(
			model_resource_description->resourceID,
			jointed_mover,
			parent_entity_segment
		);
	#if DEBUG_LEVEL>0
		AudioEntity *audio_entity =
	#endif
	AudioEntity::Make(&make_message);
	Register_Object(audio_entity);

	model_resource_description->Unlock();
	audio_resource_description->Unlock();

	CLEAR_AUDIO_RENDERER();
}

//~~~~~~~~~~~~~~~~~~~~~~~~ AudioRenderer profile bits ~~~~~~~~~~~~~~~~~~~~~~~~~

#if defined(TRACE_AUDIO_RENDERER)
	BitTrace Audio_Renderer("Audio Renderer");
#endif

#if defined(TRACE_AUDIO_RENDERER_CREATE_OBJECTS)
	BitTrace Audio_Renderer_Create_Objects("Audio Renderer Create Objects");
#endif

#if defined(TRACE_AUDIO_RENDERER_DESTROY_OBJECTS)
	BitTrace Audio_Renderer_Destroy_Objects("Audio Renderer Destroy Objects");
#endif

#if defined(TRACE_AUDIO_RENDERER_START_HANDLER)
	BitTrace Audio_Renderer_Start_Handler("Audio Renderer Start Handler");
#endif

#if defined(TRACE_AUDIO_RENDERER_STOP_HANDLER)
	BitTrace Audio_Renderer_Stop_Handler("Audio Renderer Stop Handler");
#endif

#if defined(TRACE_AUDIO_RENDERER_EXECUTE)
	BitTrace Audio_Renderer_Execute("Audio Renderer Execute");
#endif

//#############################################################################
//##############################    AudioEvent    #############################
//#############################################################################

MemoryBlock *AudioEvent::GetAllocatedMemory()
{
	static MemoryBlock allocatedMemory(sizeof(Event), AUDIOEVENT_MEMORYBLOCK_ALLOCATION, AUDIOEVENT_MEMORYBLOCK_ALLOCATION, "AudioEvents");
	return &allocatedMemory;
}

//
//#############################################################################
//#############################################################################
//
AudioEvent::AudioEvent(
	AudioSource *target,
	AudioSource::RequestMessage *message
):
	targetReceiver(this)
{
	Check(target);
	Check(message);

	//
	// Store the message
	//
	size_t long_size = (message->messageLength+3)>>2;
	messageToSend = (AudioSource::RequestMessage*)new long[long_size];
	Check_Pointer(messageToSend);
	Register_Pointer(messageToSend);

	Mem_Copy(
		messageToSend,
		message,
		message->messageLength,
		long_size*sizeof(long)
	);

	//
	// Store the target receiver 
	//
	targetReceiver.Add(target);
}

//
//#############################################################################
//#############################################################################
//
AudioEvent::~AudioEvent()
{
	Unregister_Pointer(messageToSend);
	delete messageToSend;
}

//
//#############################################################################
//#############################################################################
//
void
	AudioEvent::ReleaseLinkHandler(
		Socket*,
		Plug*
	)
{
	Unregister_Object(this);
	delete this;
}
