#include "stdafx.h"
#include "SoundSceneNode.h"

#include "Ray.h"
#include "Sphere.h"

#include "SoundSystem.h"
#include "ResourceManager.h"
#include "DynamicSceneNode.h"
#include "SceneManager.h"

const float FADE_SPEED = 2.2f;

cSoundSceneNode::cSoundSceneNode( eType type )
: cSceneNode( type )
, mListenerEntered( false )
, mOldListenerEntered( false )
, mRadius( 1000.0f )
, mAttenDistance( 100.0f )
, mLoopCount( 0 )
, mTempCount( 0 )
, mLoopInterval( 0.0f )
, mTempInterval( 0.0f )
, mVolumeRatio( 1.0f )
, mGain( 0.0f )
, mTargetGain( 0.0f )
, mRemoved(false)
, mFadeOut(false)
, mpOwnerSceneNode(0)
{
}

cSoundSceneNode::~cSoundSceneNode()
{
}

void cSoundSceneNode::OnProcess( float deltaTime, float accumTime )
{
	if( mFadeOut )
	{
		Remove();
	}

	///
	if( mpOwnerSceneNode )
	{
		SetTranslate( mpOwnerSceneNode->GetWorldTranslate() );
	}

	///
	float dt = deltaTime;
	cSceneNode::OnProcess( deltaTime, accumTime );

	/// 음량
	if( mListenerEntered == true )
	{
		if( mOldListenerEntered == false )
		{
			mTempCount = 0;
			mTempInterval = mLoopInterval;
		}

		float dist = (mBoundSphere.GetCenter() - mListenerSphere.GetCenter()).Length();

		if( dist <= mAttenDistance )
			mTargetGain = mVolumeRatio;
		else
			mTargetGain = mVolumeRatio * (1.0f - (dist - mAttenDistance) / (mRadius + mListenerSphere.GetRadius() - mAttenDistance));
	}
	else
	{
		mTargetGain = 0.0f;
	}

	if( mGain != mTargetGain )
	{
		mGain += (mTargetGain - mGain) * min(deltaTime * FADE_SPEED, 1.0f);
		mSound->SetGain( mGain );
	}

	/// 루프 카운트
	NiAudioSource::Status status = mSound->GetStatus();

	if( status != NiAudioSource::PLAYING )
	{
		if( mLoopCount == 0 )
		{
			mTempInterval += dt;

			if( mTempInterval >= mLoopInterval )
			{
				mTempInterval = 0.0f;
				mSound->Stop();
				mSound->Play();
			}
		}
		else
		{
			if( mTempCount < mLoopCount )
			{
				mTempInterval += dt;

				if( mTempInterval >= mLoopInterval )
				{
					mTempInterval = 0.0f;
					mTempCount += 1;
					mSound->Stop();
					mSound->Play();
				}
			}
			else
			{
				/// 제거 시점
				Remove();
			}
		}
	}

	mOldListenerEntered = mListenerEntered;
	mListenerEntered = false;
}

void cSoundSceneNode::OnListenerEntered( const cSphere& sphere )
{
	mListenerEntered = true;
	mListenerSphere = sphere;
}

bool cSoundSceneNode::Init( const cSoundSceneNodeParam& param )
{
	NiAudioSource* src = 0;
	if( param.mManaged == false )
	{
		/// 엔진 노드를 생성
		NiAudioSystem* as = NiAudioSystem::GetAudioSystem();
		src = as->CreateSource( NiAudioSource::TYPE_AMBIENT );
		mSound = src;

		src->SetStreamed( false );
		src->SetFilename( param.mPathName.Cstr() );
		src->SetAllowSharing( true );

		/// 사운드를 로딩
		if( src->Load() == false )
		{
			assert( 0 );
			return false;
		}
	}
	else
	{
		/// 이펙트용 사운드는 ResourceManager에서 획득한다.
		src = RESOURCEMAN->CloneSoundByName( param.mPathName.Cstr() );
		if( src == 0 )
		{
			assert( 0 );
			return false;
		}

		mSound = src;
	}

	if( mSound == 0 )
		return false;

	mpOwnerSceneNode = param.mpOwnerSceneNode;

	/// 멤버 초기화
	mRadius = param.mRadius;
	mAttenDistance = param.mAttenDistance;
	mLoopCount = param.mLoopCount;
	mLoopInterval = param.mLoopInterval;
	mVolumeRatio = param.mVolumeRatio;

	/// 상위 초기화
	GetFileName( &param.mPathName, param.mPathName );
	mNiObject = src;
	if( cSceneNode::Init( param ) == false )
	{
		return false;
	}

	/// 속성을 설정
	src->SetLoopCount( 1 );

	//if( param.mManaged )
	{
		src->SetGain( 0.0f );
		src->Stop();
	}
	//else
	//{
	//	if( SOUNDSYS->IsSoundActive() )
	//	{
	//		src->SetGain( mVolumeRatio );
	//	}
	//	else
	//	{
	//		mGain = mVolumeRatio;
	//		src->SetGain( 0.0f );
	//	}
	//}
	return true;
}

bool cSoundSceneNode::Pick( const NiPoint3& origin, const NiPoint3& dir )
{
	return mBoundSphere.IntersectRay( &mPickPos, &mPickDistance, cRay(origin, dir) );
}

void cSoundSceneNode::ActiveSound( bool active )
{
	if( active )
	{
		if( mSound )
			mSound->SetGain( mGain );
	}
	else
	{
		if( mSound )
			mSound->SetGain( 0.0f );
	}
}

void cSoundSceneNode::Remove()
{
	if( mRemoved == false )
	{
		if( mpOwnerSceneNode )
		{
			mpOwnerSceneNode->UnLinkSound( this );
		}
		else
			SCENEMAN->AddDeleteSoundNode( this );
		mRemoved = true;
	}
}
