#pragma once

#include "style.h"
#include "audio.h"

//##########################################################################
//########################    AudioLocation    #############################
//##########################################################################

class AudioLocation:
	public AudioComponent
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction, and Testing
//
public:
	AudioLocation(
  	PlugStream *stream,
     Entity *entity
  );
  void
  	AudioLocationX(
  		Entity *entity,
			AudioFrameCount next_update_delay,
			Scalar clipping_scale
  	);
	~AudioLocation();

	Logical
		TestInstance() const;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Resource Building
//
public:
static void
  	BuildFromPage(
     	PlugStream *stream,
     	NameList *name_list,
        ClassID class_ID,
        ObjectID object_ID
     );

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Audio Component Support
//
public:
	void
		ReceiveControl(
			AudioControlID control_ID,
			AudioControlValue control_value
		);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Audio Location Support
//
public:
	//
	//-----------------------------------------------------------------------
	// GetLinkedEntity
	//-----------------------------------------------------------------------
	//
	Entity*
		GetLinkedEntity();

	//
	//-----------------------------------------------------------------------
	// IsAudioLocationClipped
	//-----------------------------------------------------------------------
	//
	Logical
		IsAudioLocationClipped(AudioHead *audio_head);

	//
	//-----------------------------------------------------------------------
	// UpdateSpatialModel
	//-----------------------------------------------------------------------
	//
	void
		UpdateSpatialModel(AudioHead *audio_head);

	//
	//-----------------------------------------------------------------------
	// Access to localization results
	//-----------------------------------------------------------------------
	//
	Logical
		IsHeadSource()
			{return isHeadSource;}
	Scalar
		GetDistanceToSource()
			{return distanceToSource;}
	Scalar
		GetDistanceToSourceListenerPlane();
	Radian
		GetAzimuthOfSource()
			{return azimuthOfSource;}
	Scalar
		GetDistanceVolumeScale()
			{return distanceVolumeScale;}
	Scalar
		GetHighFreqCutoffScale()
			{return highFreqCutoffScale;}
	AudioPitchCents
		GetDopplerCents()
			{return dopplerCents;}

	Scalar getMaxDistance(AudioHead *audio_head) {return audio_head->GetClippingRadius() * clippingScale;}
	Vector3D GetVectorToSource() { return this->vectorToSource; }

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Protected Implementations
//
protected:
	virtual void
		UpdateSpatialModelImplementation(AudioHead *audio_head);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private Methods
//
private:
	Logical
		CalculateDoppler(
			AudioHead *audio_head,
			Scalar speed,
			AudioPitchCents *result
		);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private Data
//
private:
	Entity
		*linkedEntity;

	Point3D
		locationOffset;
	Scalar
		clippingScale;

	Logical
		isHeadSource;
	Vector3D
		vectorToSource;
	Scalar
		distanceToSource;
	#if 0
	Radian
		angleOffOrientation;
	#endif
	Radian
		azimuthOfSource;
	Scalar
		distanceVolumeScale;
	Scalar
		highFreqCutoffScale;
	#if 0
	Scalar
		reverbVolumeScale;
	#endif
	AudioPitchCents
		dopplerCents;

	AudioFrameCount
		nextUpdateDelay;
	AudioFrameCount
		nextUpdateFrame;
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioLocation inlines ~~~~~~~~~~~~~~~~~~~~~~~~

inline Entity*
	AudioLocation::GetLinkedEntity()
{
	Check(this);
	return linkedEntity;
}

inline Scalar
	AudioLocation::GetDistanceToSourceListenerPlane()
{
	Check(this);
	Vector3D
		vector_to_source_listener_plane(
			vectorToSource.x,
			0.0f,
			vectorToSource.z
		);
	return vector_to_source_listener_plane.Length();
}

inline void
	AudioLocation::UpdateSpatialModel(AudioHead *audio_head)
{
	Check(this);
	Check(audio_head);
	if (nextUpdateFrame <= audio_head->GetAudioFrameCount())
	{
		nextUpdateFrame = audio_head->GetAudioFrameCount() + nextUpdateDelay;
		UpdateSpatialModelImplementation(audio_head);
	}
}
