#include "munga.h"
#pragma hdrstop

#include "audio.h"
#include "audrend.h"
#include "app.h"
#include "..\munga_l4\openal\al.h"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Audio Constants ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

const Scalar AudioDoubleTriggerCutoff = 0.2f;

const AudioControlValue MaxAudioVolume = 1.0f;
const AudioControlValue MinAudioVolume = 0.0f;
const AudioControlValue LowAudioVolumeThreshold = 0.3f;

const AudioControlValue MaxAudioBrightness = 1.0f;
const AudioControlValue MinAudioBrightness = 0.0f;

const AudioControlValue MaxAudioAttack = 1.0f;
const AudioControlValue MinAudioAttack = 0.0f;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioHead ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//
AudioHead::AudioHead():
	headEntitySocket(NULL)
{
	//
	// Start frame counter at 0, other counts should be at
	// NullAudioFrameCount
	//
   audioFrameCount = 0;

   //
   // Other audio parameters
   //
	clippingRadius = 100.0f;
   distanceBetweenEars = 1.0f;
	amplitudeRollOffExponent = 1.0f;
	amplitudeRollOffKnee = 10.0f,
	amplitudeRollOffDistanceScale = 0.05f;
	highFrequencyRollOffExponent = 0.5f;
	highFrequencyRollOffKnee = 10.0f;
	highFrequencyRollOffDistanceScale = 0.05f;
	reverbToDryRatio = 0.1f;
	audioSoundSpeed = 345.0f;
	audioDopplerConstant = 20.0f;
	sourceCompressionExponent = 0.5f;
	sourceCompressionScale = 0.5f; 
	Check_Fpu();
}

//
//#############################################################################
//#############################################################################
//
AudioHead::~AudioHead()
{
	Check(this);
	Check_Fpu();
}

//
//#############################################################################
//#############################################################################
//
Logical
	AudioHead::TestInstance() const
{
	Check(&headEntitySocket);
	return True;
}

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

	//
	// Remove existing entity, add new one
	//
	if (headEntitySocket.GetCurrent() != NULL)
	{
		headEntitySocket.Remove();
	}
	headEntitySocket.Add(entity);

   alDistanceModel(AL_LINEAR_DISTANCE);
   alDopplerFactor(0.3f);

	#if 0
	//
	// Make the new clipping sphere
	//
	clippingSphere.SetCurrentOrigin(entity->localOrigin.linearPosition);
	#endif
}

//
//#############################################################################
//#############################################################################
//
LinearMatrix
	AudioHead::GetEarToWorld()
{
	Check(this);

	Entity *entity = headEntitySocket.GetCurrent();
	Check(entity);
	return entity->localToWorld;
}

//
//#############################################################################
//#############################################################################
//
void
	AudioHead::Execute()
{
	Check(this);

	//
	// Increment frame counter
	//
	audioFrameCount = Now().ticks;
	Verify(audioFrameCount < LONG_MAX);

   //set current listener orientation
   Vector3D headVelocity;
   
   headVelocity.MultiplyByInverse(this->GetHeadEntity()->GetWorldLinearVelocity(), this->GetHeadEntity()->localToWorld);
   alListener3f(AL_VELOCITY, -headVelocity.x, -headVelocity.y, -headVelocity.z);

	#if 0
	//
	// Get the entity
	//
	Entity *entity = headEntitySocket.GetCurrent();
	Check(entity);

	//
	// Make the new clipping sphere
	//
	clippingSphere.SetCurrentOrigin(entity->localOrigin.linearPosition);
	#endif
}

//
//#############################################################################
//#############################################################################
//
void
	AudioHead::DefineClippingSphere(Scalar radius)
{
	Check(this);

	clippingRadius = radius;

	#if 0
	//
	// Get entity position
	//
	Entity *entity;
	Point3D position(0.0f, 0.0f, 0.0f);

	if ((entity = headEntitySocket.GetCurrent()) != NULL)
	{
		Check(entity);
		position = entity->localOrigin.linearPosition;
	}

	//
	// Make the new clipping sphere
	//
	clippingSphere.SetCurrentOrigin(position);
	clippingSphere.SetReferenceRadius(radius);
	#endif
}

//
//#############################################################################
//#############################################################################
//
Logical
	AudioHead::IsPointClipped(const Point3D &point)
{
	Check(this);
	
	Entity 
		*entity;
	Vector3D
		difference;
	
	entity = headEntitySocket.GetCurrent();
	Check(entity);
	difference.Subtract(entity->localOrigin.linearPosition, point);		
	return difference.LengthSquared() > clippingRadius*clippingRadius;
}

//
//#############################################################################
//#############################################################################
//
void
	AudioHead::ControlDopplerEffect(
		Scalar doppler_constant,
		Scalar sound_speed
	)
{
	Check(this);
	audioDopplerConstant = doppler_constant;
	audioSoundSpeed = sound_speed;
	Check_Fpu();
}

//
//#############################################################################
//#############################################################################
//
void
	AudioHead::ControlAmplitudeRollOff(
		Scalar exponent,
		Scalar amplitude_rolloff_knee,
		Scalar distance_scale
	)
{
	Check(this);
	amplitudeRollOffExponent = exponent;
	amplitudeRollOffKnee = amplitude_rolloff_knee;
	amplitudeRollOffDistanceScale = distance_scale;
	Check_Fpu();
}

//
//#############################################################################
//#############################################################################
//
void
   AudioHead::ControlHighFrequencyRollOff(
		Scalar exponent,
      Scalar high_frequency_rolloff_knee,
      Scalar distance_scale
   )
{
   Check(this);
   highFrequencyRollOffExponent = exponent;
   highFrequencyRollOffKnee = high_frequency_rolloff_knee;
   highFrequencyRollOffDistanceScale = distance_scale;
	Check_Fpu();
}

//
//#############################################################################
//#############################################################################
//
void
	AudioHead::ControlSourceCompression(
		Scalar exponent,
		Scalar scale
	)
{
	Check(this);
	sourceCompressionExponent = exponent;
	sourceCompressionScale = scale;
	Check_Fpu();
}

//
//#############################################################################
//#############################################################################
//
void
	AudioHead::SetPositionalCulling(Logical)
{
	Check(this);
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioComponent ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//
const Receiver::HandlerEntry
	AudioComponent::MessageHandlerEntries[]=
{
	{
		AudioComponent::ReceiveControlMessageID,
		"ReceiveControl",
		(AudioComponent::Handler)&AudioComponent::ReceiveControlMessageHandler
	}
};

AudioComponent::MessageHandlerSet& AudioComponent::GetMessageHandlers()
{
	static AudioComponent::MessageHandlerSet messageHandlers(ELEMENTS(AudioComponent::MessageHandlerEntries), AudioComponent::MessageHandlerEntries, Component::GetMessageHandlers());
	return messageHandlers;
}

//
//#############################################################################
//#############################################################################
//
Derivation* AudioComponent::GetClassDerivations()
{
	static Derivation classDerivations(Component::GetClassDerivations(), "AudioComponent");
	return &classDerivations;
}

AudioComponent::SharedData
	AudioComponent::DefaultData(
		AudioComponent::GetClassDerivations(),
		AudioComponent::GetMessageHandlers()
	);

//
//#############################################################################
//#############################################################################
//
AudioComponent::AudioComponent(
	PlugStream *stream,
	SharedData &shared_data
):
	Component(stream, shared_data),
	audioWatcherSocket(NULL)
{
	nextExecuteWatcherFrame = NullAudioFrameCount;
}

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

//
//#############################################################################
//#############################################################################
//
Logical
	AudioComponent::TestInstance() const
{
	if (!IsDerivedFrom(*GetClassDerivations()))
	{
		return False;
	}
	Check(&audioWatcherSocket);
   return True;
}

//
//#############################################################################
//#############################################################################
//
void
	AudioComponent::ExecuteWatchers()
{
	Check(this);

	//
	// Execute watchers if this is the next watcher frame
	//
	AudioFrameCount 
		audio_frame_count;
	
	Check(application);	
	Check(application->GetAudioRenderer());
	audio_frame_count = application->GetAudioRenderer()->GetAudioFrameCount();
	if (nextExecuteWatcherFrame <= audio_frame_count)
	{
		nextExecuteWatcherFrame = audio_frame_count + DefaultAudioFrameDelay;
		
		//
		// Execute watchers
		//
		ChainIteratorOf<Component*> 
			iterator(&audioWatcherSocket);
		Component 
			*component;

		while ((component = iterator.ReadAndNext()) != NULL)
		{
			Check(component);
			component->Execute();
		}
	}
}

//
//#############################################################################
//#############################################################################
//
void
	AudioComponent::Execute()
{
	Check(this);
	ExecuteWatchers();
	Check_Fpu();
}

//
//#############################################################################
//#############################################################################
//
void
	AudioComponent::ReceiveControl(
		AudioControlID,
		AudioControlValue
	)
{
	Fail("AudioComponent::ReceiveControl - Should never reach here");
}

//
//#############################################################################
//#############################################################################
//
void
	AudioComponent::PostReceiveControl(
		AudioControlID control_ID,
		AudioControlValue control_value
	)
{
	Check(this);

	ReceiveControlMessage
		message(control_ID, control_value);

	Check(application);
	// ECH 1/26/96 - application->Post(HighEventPriority, this, &message);
	application->Post(DefaultEventPriority, this, &message);
	Check_Fpu();
}

//
//#############################################################################
//#############################################################################
//
void
	AudioComponent::ReceiveControlMessageHandler(ReceiveControlMessage *message)
{
	Check(this);
   Check(message);
	ReceiveControl(message->controlID, message->controlValue);
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~ AudioComponent__ReceiveControlMessage ~~~~~~~~~~~~~~~~~~~~


AudioComponent__ReceiveControlMessage::AudioComponent__ReceiveControlMessage(
	AudioControlID control_ID,
	AudioControlValue control_value
):
	Receiver::Message(
		AudioComponent::ReceiveControlMessageID,
		sizeof(AudioComponent__ReceiveControlMessage)
	)
{
	controlID = control_ID;
	controlValue = control_value;
}
