#pragma once

#include "style.h"
#include "audio.h"
#include "audloc.h"
#include "audlvl.h"
#include "audwgt.h"
#include "audtime.h"

//##########################################################################
//#####################    AudioSourceStartState    ########################
//##########################################################################

class AudioSourceStartState SIGNATURED
{
public:
	AudioSourceStartState();
	~AudioSourceStartState();

	Logical
		TestInstance() const;

	void
		CalculateDuration(AudioControlValue control_value);
	Logical
		IsFinishedPlaying();
	AudioTime
		GetCurrentRunningTime();

	void
		SetNoteValue(AudioControlValue note_value)
			{noteValue = note_value;}
	AudioControlValue
		GetNoteValue()
			{return noteValue;}
	void
		SetDurationValue(AudioControlValue duration_value);
	void
		Start()
			{startTime = AudioTime::Now();}

private:
	AudioControlValue
		noteValue;

	Logical
		isDurationSet;
	Logical
		hasDuration;
	Scalar
		duration;
	AudioTime
		startTime;
	AudioTime
		endTime;
};

//~~~~~~~~~~~~~~~~~~~~~ AudioSourceStartState inlines ~~~~~~~~~~~~~~~~~~~~~~

inline Logical
	AudioSourceStartState::IsFinishedPlaying()
{
	Check(this);
	if (hasDuration)
	{
		return (AudioTime::Now() > endTime);
	}
	return False;
}

inline void
	AudioSourceStartState::SetDurationValue(AudioControlValue duration_value)
{
	Check(this);
	isDurationSet = True; 
	duration = duration_value;
}

inline AudioTime
	AudioSourceStartState::GetCurrentRunningTime()
{
	Check(this);
	return (AudioTime::Now() - startTime);
}

//##########################################################################
//#########################    AudioSource    ##############################
//##########################################################################

class AudioSource__RequestMessage;
class AudioSource__StartMessage;
class AudioSource__StopMessage;

class AudioSource:
	public AudioComponent
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction, and Testing
//
public:
	AudioSource(
		PlugStream *stream,
		Entity *entity
	);
	void
		AudioSource::AudioSourceX(
			AudioResource *audio_resource,
			AudioLocation *audio_location,
			Entity *entity,
			AudioSourcePriority priority,
			AudioSourceMixPresence mix_presence,
			AudioControlValue volume_mix_level,
			AudioPitchCents pitch_mix_offset,
			AudioControlValue brightness_mix_level,
			Logical has_compression_curve,
			const AudioTime &compression_duration,
			Logical use_brightness_scale,
			Logical use_attack_time_scale
		);
	~AudioSource();

	Logical
		TestInstance() const;

	static void
		BuildFromPage(
			PlugStream *stream,
			NameList *name_list,
			ClassID class_ID,
			ObjectID object_ID
		);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Location methods
//
public:
	AudioLocation*
		GetAudioLocation();
	void
		UpdateSpatialModel(AudioHead *audio_head);
	Scalar
		GetDistanceToSource();
	virtual Logical
		IsAudioSourceClipped(AudioHead *audio_head);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Resource methods
//
public:
	void
		SetAudioResource(AudioResource *audio_resource);
	AudioResource*
		GetAudioResource();

	AudioVoiceCount
		GetAudioVoiceCount();
	AudioRenderType
		GetAudioRenderType();

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Current State/Status Accessors
//
public:
	void
		AssignAudioSourceState(AudioSourceState audio_source_state);
	AudioSourceState
		GetAudioSourceState();

	Logical
		IsFinishedPlaying();
	AudioTime
		GetCurrentRunningTime();

	AudioControlValue
		GetCurrentNoteValue();
	AudioControlValue
		CalculateSourceAttackVolumeScale();
	Logical
		UseSourceAttackTime();
	AudioControlValue
		CalculateSourceAttackTime();

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Priority Accessors
//
public:
	void
		SetAudioSourcePriority(AudioSourcePriority audio_source_priority);
	AudioSourcePriority
		GetAudioSourcePriority();
		
	AudioWeighting
		CalculateAudioWeighting();	

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Mixing & Logical compression methods
//
public:
	AudioSourceMixPresence
		GetAudioSourceMixPresence();

	void
		SetVolumeCompressionScale(AudioControlValue compression_scale);
	AudioControlValue
		CalculateSourceCompressionEffect();

	virtual AudioControlValue
		CalculateSourceVolumeScale();
	virtual AudioPitchCents
		CalculateSourcePitchOffset();
	Logical
		UseSourceBrightnessScale();
	virtual AudioControlValue
		CalculateSourceBrightnessScale();

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Controller methods
//
public:
	void
		ReceiveControl(
			AudioControlID control_ID,
			AudioControlValue control_value
		);

	virtual void
		VolumeAudioHandler(AudioControlValue);
	virtual void
		PitchAudioHandler(AudioControlValue);
	virtual void
		BrightnessAudioHandler(AudioControlValue);
	virtual void
		AttackVolumeAudioHandler(AudioControlValue);
	virtual void
		AttackTimeAudioHandler(AudioControlValue);
	virtual void
		NoteAudioHandler(AudioControlValue);
	virtual void
		DurationAudioHandler(AudioControlValue);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// State implementations
//
public:
	virtual void
		StartImplementation();
	virtual void
		StopImplementation();

	virtual void
		SuspendImplementation();
	virtual void
		ResumeImplementation();
		
protected:
	virtual void
		StartMessageImplementation() {}
	virtual void
		StopMessageImplementation() {}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Execution
//
public:
	Logical
		RequiresMaintenance(AudioHead *audio_head);
	Logical
		CanResume(AudioHead *audio_head);
	void
		Execute();
		
protected:
	virtual void
		ExecuteModel(Logical force_update) {}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Message Support
//
public:
	enum {
		StartMessageID = AudioComponent::NextMessageID,
		StopMessageID,
		NextMessageID
	};

	typedef AudioSource__RequestMessage
		RequestMessage;
	typedef AudioSource__StartMessage
		StartMessage;
	typedef AudioSource__StopMessage
		StopMessage;

	static const HandlerEntry
		MessageHandlerEntries[];
	//static MessageHandlerSet MessageHandlers;
	static MessageHandlerSet& GetMessageHandlers();

	void
		StartMessageHandler(StartMessage *message);
	void
		StopMessageHandler(StopMessage *message);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Shared Data Support
//
public:
	static Derivation *GetClassDerivations();
	static SharedData
		DefaultData;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
	//
	// Location data
	//
	AudioLocation
		*audioLocation;

	//
	// Resource data
	//
	AudioResource
		*audioResource;

	//
	// State/Status data
	//
	AudioSourceState
		audioSourceState;

	AudioSourceStartState
		nextStartState,
		currentStartState;

	AudioControlValue
		attackVolumeScale;
	Logical
		useAttackTimeScale;
	AudioControlValue
		attackTimeScale;

	//
	// Priority data
	//
	AudioSourcePriority
		audioSourcePriority;

	//
	// Mixing and compression
	//
	AudioSourceMixPresence
		audioSourceMixPresence;
	AudioControlValue
		volumeCompressionScale;

	Logical
		hasCompressionCurve;
	AudioTime
		compressionDuration;

	AudioControlValue
		volumeMixScale;
	AudioPitchCents
		pitchMixOffset;
	Logical
		useBrightnessScale;
	AudioControlValue
		brightnessMixScale;

	AudioControlValue
		volumeScale;
	AudioPitchCents
		pitchOffset;
	AudioControlValue
		brightnessScale;

	//
	// Execution
	//
	AudioFrameCount
		nextExecuteFrame;			
	AudioFrameCount
		suspendFinishedFrame;
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioSource inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~

//
// Location methods
//

inline AudioLocation*
	AudioSource::GetAudioLocation()
{
	Check(this);
	return audioLocation;
}

inline void
	AudioSource::UpdateSpatialModel(AudioHead *audio_head)
{
	Check(this);
	Check(audioLocation);
	audioLocation->UpdateSpatialModel(audio_head);
}

inline Scalar
	AudioSource::GetDistanceToSource()
{
	Check(this);
	Check(audioLocation);
	return audioLocation->GetDistanceToSource();
}

inline Logical
	AudioSource::IsAudioSourceClipped(AudioHead *audio_head)
{
	Check(this);
	Check(audioLocation);
	return audioLocation->IsAudioLocationClipped(audio_head);
}

//
// Resource methods
//

inline AudioResource*
	AudioSource::GetAudioResource()
{
	Check(this);
	return audioResource;
}

inline AudioVoiceCount
	AudioSource::GetAudioVoiceCount()
{
	Check(this);
	Check(audioResource);
	return audioResource->GetVoiceCount();
}

inline AudioRenderType
	AudioSource::GetAudioRenderType()
{
	Check(this);
	Check(audioResource);
	return audioResource->GetAudioRenderType();
}

//
// Current State/Status Accessors
//

inline AudioSourceState
	AudioSource::GetAudioSourceState()
{
	Check(this);
	return audioSourceState;
}

inline Logical
	AudioSource::IsFinishedPlaying()
{
	Check(this);
	return currentStartState.IsFinishedPlaying();
}

inline AudioTime
	AudioSource::GetCurrentRunningTime()
{
	Check(this);
	Verify(audioSourceState == RunningAudioSourceState);
	return currentStartState.GetCurrentRunningTime();
}

inline AudioControlValue
	AudioSource::GetCurrentNoteValue()
{
	Check(this);
	return currentStartState.GetNoteValue();
}

inline AudioControlValue
	AudioSource::CalculateSourceAttackVolumeScale()
{
	Check(this);
	return attackVolumeScale;
}

inline Logical
	AudioSource::UseSourceAttackTime()
{
	Check(this);
	return useAttackTimeScale;
}

inline AudioControlValue
	AudioSource::CalculateSourceAttackTime()
{
	Check(this);
	return attackTimeScale;
}

//
// Priority Accessors
//

inline void
	AudioSource::SetAudioSourcePriority(
		AudioSourcePriority audio_source_priority
	)
{
	Check(this);
	audioSourcePriority = audio_source_priority;
}

inline AudioSourcePriority
	AudioSource::GetAudioSourcePriority()
{
	Check(this);
	return audioSourcePriority;
}

inline AudioWeighting
	AudioSource::CalculateAudioWeighting()
{
	Check(this);
	AudioWeighting
		audio_weight(
			GetAudioSourcePriority(), 
			CalculateSourceVolumeScale()
		);		
	return audio_weight;
}

//
// Mixing & Logical compression methods
//

inline AudioSourceMixPresence
	AudioSource::GetAudioSourceMixPresence()
{
	Check(this);
	return audioSourceMixPresence;
}

inline void
	AudioSource::SetVolumeCompressionScale(
		AudioControlValue compression_level
	)
{
	Check(this);
	volumeCompressionScale = compression_level;
}

inline AudioPitchCents
	AudioSource::CalculateSourcePitchOffset()
{
	Check(this);
	return pitchMixOffset + pitchOffset;
}

inline Logical
	AudioSource::UseSourceBrightnessScale()
{
	Check(this);
	return useBrightnessScale;
}

//
// Execute
//

inline Logical
	AudioSource::RequiresMaintenance(AudioHead *audio_head)
{
	Check(this);
	Check(audio_head);
	return (nextExecuteFrame <= audio_head->GetAudioFrameCount());
}

inline Logical
	AudioSource::CanResume(AudioHead *audio_head)
{
	Check(this);
	Check(audio_head);
	return (suspendFinishedFrame <= audio_head->GetAudioFrameCount());
}		

//~~~~~~~~~~~~~~~~~~~~~~ AudioSource__RequestMessage ~~~~~~~~~~~~~~~~~~~~~~~

class AudioSource__RequestMessage:
	public Receiver::Message
{
public:
	AudioSource__RequestMessage(
		Receiver::MessageID id,
		size_t message_length,
		AudioControlID control_ID,
		AudioControlValue control_value
	);

	AudioControlID
		controlID;
	AudioControlValue
		controlValue;
};

inline AudioSource__RequestMessage::AudioSource__RequestMessage(
	Receiver::MessageID message_ID,
	size_t message_length,
	AudioControlID control_ID,
	AudioControlValue control_value
):
	Receiver::Message(
		message_ID,
		message_length
	)
{
	controlID = control_ID;
	controlValue = control_value;
}

//~~~~~~~~~~~~~~~~~~~~~~ AudioSource__StartMessage ~~~~~~~~~~~~~~~~~~~~~~~~~

class AudioSource__StartMessage:
	public AudioSource::RequestMessage
{
public:
	AudioSource__StartMessage(AudioControlValue control_value);
};

inline AudioSource__StartMessage::AudioSource__StartMessage(
	AudioControlValue control_value
):
	AudioSource::RequestMessage(
		AudioSource::StartMessageID,
		sizeof(AudioSource__StartMessage),
		StartAudioControlID,
		control_value
	)
{
}

//~~~~~~~~~~~~~~~~~~~~~~ AudioSource__StopMessage ~~~~~~~~~~~~~~~~~~~~~~~~~~

class AudioSource__StopMessage:
	public AudioSource::RequestMessage
{
public:
	AudioSource__StopMessage();
};

inline AudioSource__StopMessage::AudioSource__StopMessage():
	AudioSource::RequestMessage(
		AudioSource::StopMessageID,
		sizeof(AudioSource__StopMessage),
		StopAudioControlID,
		0.0f
	)
{
}
