#pragma once

#include "renderer.h"
#include "audio.h"
#include "audsrc.h"
#include "audwgt.h"
#include "event.h"
#include "..\munga_l4\l4audhdw.h"

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

class AudioEvent;

class AudioRenderer:
	public Renderer
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction, Testing
//
public:
	AudioRenderer(RendererRate render_rate);
	~AudioRenderer();

	Logical
		TestInstance() const;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Renderer Interface
//
public:
	virtual void
		Initialize();

	void
		LinkToEntity(Entity *entity);

	AudioHead*
		GetAudioHead();
		
	AudioFrameCount
		GetAudioFrameCount();

	virtual void ReleaseSourceSet(SourceSet &sourceSet) = 0;

	void
		PostAudioRequestMessage(
			AudioSource *audio_source,
			AudioSource::RequestMessage *message
		);
	Logical
		ProcessAudioRequestMessage();
	void
		FlushAudioMessages(AudioSource *audio_source);

	Logical
		ExecuteBackground();

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Implementations
//
protected:
	void
		ExecuteImplementation(
			RendererComplexity complexity_update,
			RendererOrigin::InterestingEntityIterator *iterator
		);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private Methods
//
private:
	virtual AudioHead*
		MakeAudioHead();

	void
		StartEntityEffectImplementation(
			Entity *entity,
			DamageZone *damage_zone,
			ResourceDescription::ResourceID resource_ID
		);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
	typedef VChainOf<AudioEvent*, AudioWeighting>
		AudioEventSocket;
	typedef VChainIteratorOf<AudioEvent*, AudioWeighting>
		AudioEventIterator;

	AudioHead
		*audioHead;
	AudioEventSocket
		audioEventSocket;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Audio Renderer Statistics
//
#ifdef LAB_ONLY
   public:
		int
			sourceClippedCount;
		int
			sourceStartCount;
		int
			sourceSuspendCount;
		int
			sourceResumeCount;
		int 
			doubleTriggerCount;
		int 
			resourceStealCount;
		int
			resourceStealPriorityCount;
		int
			resourceStealVolumeCount;
		int
			stealShortDurationCount;
#endif
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioRenderer inlines ~~~~~~~~~~~~~~~~~~~~~~~~

inline AudioHead*
	AudioRenderer::GetAudioHead()
{
	Check(audioHead);
	return audioHead;
}

inline AudioFrameCount
	AudioRenderer::GetAudioFrameCount()
{
	Check(audioHead);
	return audioHead->GetAudioFrameCount();
}

inline Logical
	AudioRenderer::ExecuteBackground()
{
	Check(this);
	return ProcessAudioRequestMessage();
}

//~~~~~~~~~~~~~~~~~~~~~~~~ AudioRenderer profile macros ~~~~~~~~~~~~~~~~~~~~

#if defined(TRACE_AUDIO_RENDERER)
	extern BitTrace Audio_Renderer;
	#define SET_AUDIO_RENDERER() Audio_Renderer.Set()
	#define CLEAR_AUDIO_RENDERER() Audio_Renderer.Clear()
#else
	#define SET_AUDIO_RENDERER()
	#define CLEAR_AUDIO_RENDERER()
#endif

#if defined(TRACE_AUDIO_RENDERER_CREATE_OBJECTS)
	extern BitTrace Audio_Renderer_Create_Objects;
	#define SET_AUDIO_RENDERER_CREATE_OBJECTS() Audio_Renderer_Create_Objects.Set()
	#define CLEAR_AUDIO_RENDERER_CREATE_OBJECTS() Audio_Renderer_Create_Objects.Clear()
#else
	#define SET_AUDIO_RENDERER_CREATE_OBJECTS()
	#define CLEAR_AUDIO_RENDERER_CREATE_OBJECTS()
#endif

#if defined(TRACE_AUDIO_RENDERER_DESTROY_OBJECTS)
	extern BitTrace Audio_Renderer_Destroy_Objects;
	#define SET_AUDIO_RENDERER_DESTROY_OBJECTS() Audio_Renderer_Destroy_Objects.Set()
	#define CLEAR_AUDIO_RENDERER_DESTROY_OBJECTS() Audio_Renderer_Destroy_Objects.Clear()
#else
	#define SET_AUDIO_RENDERER_DESTROY_OBJECTS()
	#define CLEAR_AUDIO_RENDERER_DESTROY_OBJECTS()
#endif

#if defined(TRACE_AUDIO_RENDERER_START_HANDLER)
	extern BitTrace Audio_Renderer_Start_Handler;
	#define SET_AUDIO_RENDERER_START_HANDLER() Audio_Renderer_Start_Handler.Set()
	#define CLEAR_AUDIO_RENDERER_START_HANDLER() Audio_Renderer_Start_Handler.Clear()
#else
	#define SET_AUDIO_RENDERER_START_HANDLER()
	#define CLEAR_AUDIO_RENDERER_START_HANDLER()
#endif

#if defined(TRACE_AUDIO_RENDERER_STOP_HANDLER)
	extern BitTrace Audio_Renderer_Stop_Handler;
	#define SET_AUDIO_RENDERER_STOP_HANDLER() Audio_Renderer_Stop_Handler.Set()
	#define CLEAR_AUDIO_RENDERER_STOP_HANDLER() Audio_Renderer_Stop_Handler.Clear()
#else
	#define SET_AUDIO_RENDERER_STOP_HANDLER()
	#define CLEAR_AUDIO_RENDERER_STOP_HANDLER()
#endif

#if defined(TRACE_AUDIO_RENDERER_EXECUTE)
	extern BitTrace Audio_Renderer_Execute;
	#define SET_AUDIO_RENDERER_EXECUTE() Audio_Renderer_Execute.Set()
	#define CLEAR_AUDIO_RENDERER_EXECUTE() Audio_Renderer_Execute.Clear()
#else
	#define SET_AUDIO_RENDERER_EXECUTE()
	#define CLEAR_AUDIO_RENDERER_EXECUTE()
#endif

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

#define AUDIOEVENT_MEMORYBLOCK_ALLOCATION (20)

class AudioEvent:
	public Node
{
	friend class AudioRenderer;
	
private:
	AudioEvent(
		AudioSource *target,
		AudioSource::RequestMessage *message
	);
	~AudioEvent();

	static MemoryBlock* GetAllocatedMemory();

	void* operator new(size_t)			{ return GetAllocatedMemory()->New(); }
	void operator delete(void *where)	{ GetAllocatedMemory()->Delete(where); }

	void
		Process();

	void
		ReleaseLinkHandler(
			Socket *socket,
			Plug *plug
		);

	AudioSource::RequestMessage	
		*messageToSend;
	SlotOf<AudioSource*>		
		targetReceiver;
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioEvent inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

inline void
	AudioEvent::Process()
{
	Check(targetReceiver.GetCurrent());
	targetReceiver.GetCurrent()->Receive(messageToSend);
	Unregister_Object(this);
	delete this;
}
