#include "Mix/Private/Sound/StreamingEmitter.h"

#include "Mix/Private/Sound/Manager.h"
#include "Mix/Private/Sound/Listener.h"
#include "Mix/Private/Sound/EmitterMediator.h"
#include "Mix/IO/IReader.h"

namespace Mix{ namespace Sound{

StreamingEmitter* StreamingEmitter::CreateInstance(	Mix::Sound::Listener* pListener,
												Mix::IO::IReader* pReader,
												Mix::Sound::EmitterMediator* pMediator,
												const Mix::Vector3& localFront,
												const Mix::Vector3& localUp,
												UInt32 channels,
												UInt32 masterChannels )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_SOUND, StreamingEmitter, pListener, pReader, pMediator, localFront, localUp, channels, masterChannels );
}

StreamingEmitter::StreamingEmitter(	Mix::Sound::Listener* pListener,
								Mix::IO::IReader* pReader,
								Mix::Sound::EmitterMediator* pMediator,
								const Mix::Vector3& localFront,
								const Mix::Vector3& localUp,
								UInt32 channels,
								UInt32 masterChannels ) :
m_pListener( NULL ),
m_pReader( NULL ),
m_pMediator( NULL ),
m_LocalFront( localFront ),
m_LocalUp( localUp ),
m_WorldFront( localFront ),
m_WorldUp( localUp )
{
	MIX_ASSERT( pListener != NULL );
	MIX_ASSERT( pReader != NULL );
	MIX_ASSERT( pMediator != NULL );

	MIX_ADD_REF( pListener );
	m_pListener = pListener;

	MIX_ADD_REF( pReader );
	m_pReader = pReader;

	MIX_ADD_REF( pMediator );
	m_pMediator = pMediator;

	m_ChannelAzimuths.resize( channels, 1.0f );

	m_Param.pCone = NULL;
	m_Param.OrientFront.x = m_LocalFront.x;
	m_Param.OrientFront.y = m_LocalFront.y;
	m_Param.OrientFront.z = m_LocalFront.z;
	m_Param.OrientTop.x = m_LocalUp.x;
	m_Param.OrientTop.y = m_LocalUp.y;
	m_Param.OrientTop.z = m_LocalUp.z;
	m_Param.Position.x = 0.0f;
	m_Param.Position.y = 0.0f;
	m_Param.Position.z = 0.0f;
	m_Param.Velocity.x = 0.0f;
	m_Param.Velocity.y = 0.0f;
	m_Param.Velocity.z = 0.0f;
	m_Param.InnerRadius = 0.0f;
	m_Param.InnerRadiusAngle = ( X3DAUDIO_PI / 4.0f );
	m_Param.ChannelCount = channels;
	m_Param.ChannelRadius = 1.0f;
	m_Param.pChannelAzimuths = &( m_ChannelAzimuths[0] );
	m_Param.pVolumeCurve = NULL;
	m_Param.pLFECurve = NULL;
	m_Param.pLPFDirectCurve = NULL;
	m_Param.pLPFReverbCurve = NULL;
	m_Param.pReverbCurve = NULL;
	m_Param.CurveDistanceScaler = 1.0f;
	m_Param.DopplerScaler = 1.0f;
}

StreamingEmitter::~StreamingEmitter( void )
{
	if( m_pMediator != NULL )
	{
		m_pMediator->PushEvent( Mix::Sound::BaseMediator::EVENT_TYPE_TERMINATE, 0 );
	}

	MIX_RELEASE( m_pReader );
	MIX_RELEASE( m_pMediator );
	MIX_RELEASE( m_pListener );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Sound::IEmitter
////////////////////////////////////////////////////////////////////////////////////////////////////

const Mix::Vector3& StreamingEmitter::GetLocalFront( void ) const
{
	return m_LocalFront;
}

const Mix::Vector3& StreamingEmitter::GetLocalUp( void ) const
{
	return m_LocalUp;
}

const Mix::Matrix4x4& StreamingEmitter::GetWorldMatrix( void ) const
{
	return m_WorldMatrix;
}

void StreamingEmitter::SetWorldMatrix( const Mix::Matrix4x4& mat )
{
	m_WorldMatrix = mat;

	m_WorldFront = m_WorldMatrix.TransformSR( m_LocalFront );
	m_WorldUp = m_WorldMatrix.TransformSR( m_LocalUp );

	m_Param.OrientFront.x = m_WorldFront.x;
	m_Param.OrientFront.y = m_WorldFront.y;
	m_Param.OrientFront.z = m_WorldFront.z;
	m_Param.OrientTop.x = m_WorldUp.x;
	m_Param.OrientTop.y = m_WorldUp.y;
	m_Param.OrientTop.z = m_WorldUp.z;
	m_Param.Position.x = m_WorldMatrix.m30;
	m_Param.Position.y = m_WorldMatrix.m31;
	m_Param.Position.z = m_WorldMatrix.m32;
}

const Mix::Vector3& StreamingEmitter::GetWorldFront( void ) const
{
	return m_WorldFront;
}

const Mix::Vector3& StreamingEmitter::GetWorldUp( void ) const
{
	return m_WorldUp;
}

const Mix::Vector3& StreamingEmitter::GetVelocity( void ) const
{
	return m_Velocity;
}

void StreamingEmitter::SetVelocity( const Mix::Vector3& velocity )
{
	m_Velocity = velocity;

	m_Param.Velocity.x = m_Velocity.x;
	m_Param.Velocity.y = m_Velocity.y;
	m_Param.Velocity.z = m_Velocity.z;
}

UInt32 StreamingEmitter::GetChannelCount( void ) const
{
	return m_Param.ChannelCount;
}

Float32 StreamingEmitter::GetChannelRadius( void ) const
{
	return m_Param.ChannelRadius;
}

void StreamingEmitter::SetChannelRadius( Float32 radius )
{
	m_Param.ChannelRadius = max( 0.0f, radius );
}

Float32 StreamingEmitter::GetChannelAzimuth( UInt32 channel ) const
{
	if( channel >= m_Param.ChannelCount )
	{
		return 0.0f;
	}

	return m_ChannelAzimuths[channel];
}

void StreamingEmitter::SetChannelAzimuth( UInt32 channel, Float32 azimuth )
{
	if( channel >= m_Param.ChannelCount )
	{
		return;
	}

	m_ChannelAzimuths[channel] = MIX_CLAMP( azimuth, 0.0f, MIX_2PI );
}

Float32 StreamingEmitter::GetDopplerScaler( void ) const
{
	return m_Param.DopplerScaler;
}

void StreamingEmitter::SetDopplerScaler( Float32 scaler )
{
	m_Param.DopplerScaler = MIX_CLAMP( scaler, 0.0f, MIX_FLOAT_MAX );
}

void StreamingEmitter::Update( void )
{
	m_pMediator->UpdateDSPSettings( m_pListener->GetParam(), this->m_Param );
}

Boolean StreamingEmitter::IsCloneable( void ) const
{
	return ( m_pReader->GetSourceType() == Mix::IO::IStream::S_BUFFER );
}

Boolean StreamingEmitter::Clone( Mix::Sound::IEmitter** ppEmitter )
{
	MIX_ASSERT( ppEmitter != NULL );

	if( m_pMediator == NULL )
	{
		return False;
	}

	Mix::Sound::IEmitter* pEmitter = NULL;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// インターフェースを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	MIX_ASSERT( Mix::Sound::GetInternalManagerPtr() != NULL );

	if( Mix::Sound::GetInternalManagerPtr()->CloneStreamingEmitter( m_pListener, m_pReader, m_LocalFront, m_LocalUp, &pEmitter ) == False )
	{
		return False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// パラメータを設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pEmitter->SetChannelRadius( GetChannelRadius() );

	for( UInt32 i = 0; i < GetChannelCount(); i++ )
	{
		pEmitter->SetChannelAzimuth( i, GetChannelAzimuth( i ) );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 譲渡
	////////////////////////////////////////////////////////////////////////////////////////////////////

	( *ppEmitter ) = pEmitter;

	return True;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Sound::IDevice
////////////////////////////////////////////////////////////////////////////////////////////////////

Boolean StreamingEmitter::IsPlaying( void )
{
	if( m_pMediator == NULL )
	{
		return False;
	}

	return m_pMediator->IsPlaying();
}

void StreamingEmitter::Play( Boolean bLoop )
{
	if( m_pMediator != NULL )
	{
		m_pMediator->PushEvent( Mix::Sound::BaseMediator::EVENT_TYPE_STOP, 0 );
		m_pMediator->PushEvent( Mix::Sound::BaseMediator::EVENT_TYPE_PLAY, ( bLoop == True )? 1 : 0 );
	}
}

void StreamingEmitter::Stop( void )
{
	if( m_pMediator != NULL )
	{
		m_pMediator->PushEvent( Mix::Sound::BaseMediator::EVENT_TYPE_STOP, 0 );
	}
}

void StreamingEmitter::Suspend( void )
{
	if( m_pMediator != NULL )
	{
		m_pMediator->PushEvent( Mix::Sound::BaseMediator::EVENT_TYPE_SUSPEND, 0 );
	}
}

void StreamingEmitter::Resume( void )
{
	if( m_pMediator != NULL )
	{
		m_pMediator->PushEvent( Mix::Sound::BaseMediator::EVENT_TYPE_RESUME, 0 );
	}
}

Boolean StreamingEmitter::IsDisposed( void )
{
	return ( m_pMediator == NULL );
}

void StreamingEmitter::Dispose( void )
{
	if( m_pMediator != NULL )
	{
		m_pMediator->PushEvent( Mix::Sound::BaseMediator::EVENT_TYPE_TERMINATE, 0 );
		m_pMediator->Join( INFINITE );
	}

	MIX_RELEASE( m_pReader );
	MIX_RELEASE( m_pMediator );
	MIX_RELEASE( m_pListener );
}

const wchar_t* StreamingEmitter::GetFilePath( void ) const
{
	if( m_pReader == NULL )
	{
		return Mix::STR_NULL;
	}

	return m_pReader->GetFilePath();
}

}}
