#include "StdAfx.h"
#include "effectscenenode.h"
#include "NifAnimationInfo.h"

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

cEffectSceneNode::cEffectSceneNode( eType type )
: cSceneNode( type )
, mpFollowSceneNode(0)
, mRemoved(false)
{
//	mFollowSceneNodeIdx = (unsigned long)-1;
	mDummyIdx = (unsigned int)-1;

	mNifAniInfo = new cNifAnimationInfo;
	assert(mNifAniInfo);

	mIsDamage = false;
	mIsDistCheck = true;
}

cEffectSceneNode::~cEffectSceneNode()
{
	if( mSceneNiNode )
	{
		NiNode* parent = mSceneNiNode->GetParent();
		if( parent != 0 )
		{
			parent->DetachChild( mSceneNiNode );
		}
	}

	try {
		RemoveSelf();
		SAFE_DELETE(mNifAniInfo);
	} catch ( ... ) {
		GameErrorLog( "cEffectSceneNode::~cEffectSceneNode(Delete NifAniInfo)" );
		throw;
	}
}

bool cEffectSceneNode::InitSelfNode( const cString& pathName, bool distCheck, NiTransform& trans, bool bLoop )
{
	if( RESOURCEMAN->LoadNIF( pathName ) == false )
	{
		assert( 0 && "failed to load nif file" );
		return false;
	}
	NiNode* n = RESOURCEMAN->CloneObjectByName( pathName.Cstr() );
	if( n == 0 )
	{
		assert( 0 && "failed to clone node from resource manager" );
		return false;
	}

	mIsDistCheck = distCheck;

	NiTransform kWorld = trans * n->GetLocalTransform();
	n->SetTranslate( kWorld.m_Translate );
	n->SetRotate( kWorld.m_Rotate );
	n->SetScale( kWorld.m_fScale );

	mSceneNiNode = n;
	n->SetName( "EffectSceneNode" );

	/// 애니메이션 컨트롤러 정보 수집
	mNifAniInfo->CollectData( n );
	if( mNifAniInfo->GetNumberOfControllers() )
	{
		mNifAniInfo->SetAnimType( NiTimeController::APP_INIT );
		mNifAniInfo->SetLooping( bLoop );
	}
	else
	{
		/// 애니 정보가 없다면 메모리 해제
		SAFE_DELETE(mNifAniInfo);
	}

	NiFogProperty* pFog = NiNew NiFogProperty;
	pFog->SetFog( false );
	n->AttachProperty( pFog );

	/// 빌보드 노드들을 수집
	CollectBillboards( GetNiObj() );

	/// 엔진 노드를 설정
	n->SetSelectiveUpdate( true );
	n->SetSelectiveUpdateTransforms( true );
	n->SetSelectiveUpdatePropertyControllers( true );
	n->SetSelectiveUpdateRigid( false );

	/// 엔진 노드를 처리
	n->Update( 0.0f );
	n->UpdateProperties();
	n->UpdateEffects();
	n->UpdateNodeBound();

	return true;
}

/// param을 생성하는 경우 dummy를 포함해야 한다.
bool cEffectSceneNode::Init( const cEffectSceneNodeParam& param )
{
	/// 파일을 로딩
	if( RESOURCEMAN->LoadNIF( param.mPathName ) == false )
	{
//		assert( 0 && "failed to load nif file" );
		return false;
	}

	if( param.mpFollowSceneNode == 0 )
	{
		assert(0);
		return false;
	}

	/// 리소스 관리자로부터 엔진 노드를 복제
	NiNode* n = RESOURCEMAN->CloneObjectByName( param.mPathName.Cstr() );
	if( n == 0 )
	{
		assert( 0 && "failed to clone node from resource manager" );
		return false;
	}

	n->SetTranslate( param.mTranslate );
	n->SetRotate( param.mRotate );
	n->SetScale( param.mScale );

	mIsDistCheck = param.mIsDistCheck;

	NiNode* pDummy = param.mpFollowSceneNode->GetDummyObject(param.mDummyIdx);
	if( pDummy == 0 )
	{
		if( param.mDummyIdx == 10 ) /// right weapon dummy
		{
			/// right hand link
			pDummy = param.mpFollowSceneNode->GetDummyObject( 2 );
		}
		else if( param.mDummyIdx == 11 ) /// left weapon dummy
		{
			/// left hand link
			pDummy = param.mpFollowSceneNode->GetDummyObject( 3 );
		}
		else
		{
			assert(0);
			return false;
		}
	}

	if( param.mFollowing == false )
	{
		NiTransform kWorld =  pDummy->GetWorldTransform() * n->GetLocalTransform();
		n->SetWorldScale( kWorld.m_fScale );

		NiPoint3 trans = pDummy->GetWorldTranslate();
		n->SetTranslate( n->GetTranslate() + trans );
	}
	else
	{
		mpFollowSceneNode = param.mpFollowSceneNode;
//		mFollowSceneNodeIdx = mpFollowSceneNode->GetIndexByManger();
		mDummyIdx = param.mDummyIdx;
	}

	mSceneNiNode = n;
	n->SetName("EffectSceneNode");

	NiFogProperty* pFog = NiNew NiFogProperty;
	pFog->SetFog( false );
	n->AttachProperty( pFog );

	/// 빌보드 노드들을 수집
	CollectBillboards( GetNiObj() );

	/// 애니메이션 컨트롤러 정보 수집
	mNifAniInfo->CollectData( n );
	if( mNifAniInfo->GetNumberOfControllers() )
	{
		mNifAniInfo->SetAnimType( NiTimeController::APP_INIT );
		mNifAniInfo->SetLooping( param.mLooping );
	}
	else
	{
		/// 애니 정보가 없다면 메모리 해제
		SAFE_DELETE(mNifAniInfo);
	}

	if( param.mFollowing == true && pDummy )
	{
		pDummy->AttachChild( n );

//		char tempmsg[255] = {0,};
//		sprintf( tempmsg, "\ncEffectSceneNode::Init(AttachChild) : %s[%d]", pDummy->GetName(), GetIndexByManger() );
//		OutputDebugStringA( tempmsg );
	}
    
	/// 엔진 노드를 설정
	bool selectiveUpdate = true;
	bool rigid = false;
	n->SetSelectiveUpdateFlags( selectiveUpdate, true, rigid );

	/// 엔진 노드를 처리
	n->Update( 0.0f );
	n->UpdateProperties();
	n->UpdateEffects();
	n->UpdateNodeBound();

	return true;
}

bool cEffectSceneNode::InitDamageEffect( const cEffectSceneNodeParam& param, bool IsMiss, bool zFalse )
{
	/// 파일을 로딩
	if( RESOURCEMAN->LoadNIF( param.mPathName ) == false )
	{
		assert( 0 );
		return false;
	}

	if( param.mpFollowSceneNode == 0 )
	{
		assert(0);
		return false;
	}
	NiNode* pDummy = param.mpFollowSceneNode->GetDummyObject(param.mDummyIdx);
	if( pDummy == 0 )
	{
		assert(0);
		return false;
	}

	///
	NiNode* des = 0;
	if( IsMiss == true )
	{
		/// 리소스 관리자로부터 엔진 노드를 복제
		des = RESOURCEMAN->CloneObjectByName( param.mPathName.Cstr() );
		if( des == 0 )
		{
			assert( 0 && "failed to clone node from resource manager" );
			return false;
		}
	}
	else
	{
		//////////////////////////////////////////////////////////////////////////
		// 공유 GeomData를 버리고 새로 갱신한다.
		NiNode* src = RESOURCEMAN->GetObjectByName( param.mPathName.Cstr() );
		des = RESOURCEMAN->CloneObjectByName( param.mPathName.Cstr() );
		if( src == 0 || des == 0 )
			return false ;

		NiGeometry* sGeom[4] = {0,};
		if( FindDamageGeom( src, sGeom ) == false )
			return false;
		NiGeometry* dGeom[4] = {0,};
		if( FindDamageGeom( des, dGeom ) == false )
			return false;

		for( unsigned int i=0; i<4; i++ )
		{
			assert( sGeom[i] != 0 );
			assert( dGeom[i] != 0 );

			NiGeometryDataPtr g = NiSmartPointerCast( NiGeometryData, sGeom[i]->GetModelData()->CreateDeepCopy() );
			g->SetConsistency( NiGeometryData::VOLATILE );

			dGeom[i]->SetModelData( g );
		}
		//////////////////////////////////////////////////////////////////////////
	}

	des->SetTranslate( param.mTranslate );
	des->SetRotate( param.mRotate );
	des->SetScale( param.mScale );

	mIsDistCheck = param.mIsDistCheck;

	if( param.mFollowing == false )
	{
		NiPoint3 trans = pDummy->GetWorldTranslate();
		des->SetTranslate( des->GetTranslate() + trans );
	}
	else
	{
		mpFollowSceneNode = param.mpFollowSceneNode;
//		mFollowSceneNodeIdx = mpFollowSceneNode->GetIndexByManger();
		mDummyIdx = param.mDummyIdx;
	}

	mSceneNiNode = des;
	des->SetName("EffectSceneNode");

	/// 애니메이션 컨트롤러 정보 수집
	mNifAniInfo->CollectData( des );
	if( mNifAniInfo->GetNumberOfControllers() )
	{
		mNifAniInfo->SetAnimType( NiTimeController::APP_INIT );
		mNifAniInfo->SetLooping( param.mLooping );
	}
	else
	{
		/// 애니 정보가 없다면 메모리 해제
		SAFE_DELETE(mNifAniInfo);
	}

	NiFogProperty* pFog = NiNew NiFogProperty;
	pFog->SetFog( false );
	des->AttachProperty( pFog );

	if( zFalse )
	{
		NiZBufferProperty* zProp = (NiZBufferProperty*)des->GetProperty( NiProperty::ZBUFFER );
		if( zProp == 0 )
		{
			/// 알파 속성이 없으면 생성
			zProp = NiNew NiZBufferProperty;
			des->AttachProperty( zProp );
		}

		DisableZBuffer( des );
		mIsDamage = true;
	}

	if( param.mFollowing == true && pDummy )
		pDummy->AttachChild( des );

	/// 엔진 노드를 설정
	bool selectiveUpdate = true;
	bool rigid = false;
	des->SetSelectiveUpdateFlags( selectiveUpdate, true, rigid );

	/// 엔진 노드를 처리
	des->Update( 0.0f );
	des->UpdateProperties();
	des->UpdateEffects();
	des->UpdateNodeBound();

	return true;
}

void cEffectSceneNode::RemoveSelf()
{
	if( mRemoved == true )
		return;

	mRemoved = true;
	SCENEMAN->AddDeleteEffectNode( this );

	if( mpFollowSceneNode )
	{
		mpFollowSceneNode->UnLinkEffect( this );
		mpFollowSceneNode = 0;
	}
//	if( mFollowSceneNodeIdx != (unsigned long)-1 )
//	{
//		cDynamicSceneNode* node = SCENEMAN->GetDynamicSceneNode( mFollowSceneNodeIdx );
//		if( node )
//		{
//			assert( node == mpFollowSceneNode );
//			if( node != mpFollowSceneNode )
//				GameErrorLog( "cEffectSceneNode::RemoveSelf : node == mpFollowSceneNode" );

//			node->UnLinkEffect( this );
//		}
//	}

//	mpFollowSceneNode = 0;
//	mFollowSceneNodeIdx = (unsigned long)-1;
	mDummyIdx = (unsigned int)-1;

	if( mNifAniInfo )
		mNifAniInfo->Stop();
}

void cEffectSceneNode::Remove()
{
	if( mRemoved == true )
		return;

	if( mpFollowSceneNode )
	{
		mpFollowSceneNode->UnLinkEffect( this );
		mpFollowSceneNode = 0;
	}
/*
	if( mFollowSceneNodeIdx != (unsigned long)-1 )
	{
		cDynamicSceneNode* node = SCENEMAN->GetDynamicSceneNode( mFollowSceneNodeIdx );
		if( node )
		{
			assert( node == mpFollowSceneNode );
			if( node != mpFollowSceneNode )
				GameErrorLog( "cEffectSceneNode::Remove : node == mpFollowSceneNode" );

			node->UnLinkEffect( this );
		}
	}
*/
	mRemoved = true;
//	mpFollowSceneNode = 0;
//	mFollowSceneNodeIdx = (unsigned long)-1;
	mDummyIdx = (unsigned int)-1;

	SCENEMAN->AddDeleteEffectNode( this );

	if( mNifAniInfo )
		mNifAniInfo->Stop();
}

void cEffectSceneNode::ParentNodeRemove()
{
	if( mRemoved == true )
		return;

	mRemoved = true;
	mpFollowSceneNode = 0;
//	mFollowSceneNodeIdx = (unsigned long)-1;
	mDummyIdx = (unsigned int)-1;

	SCENEMAN->AddDeleteEffectNode( this );

	if( mNifAniInfo )
		mNifAniInfo->Stop();
}

void cEffectSceneNode::OnProcess( unsigned long deltaTime, unsigned long accumTime )
{
	if( mRemoved == true )
		return;

//	if( mFollowSceneNodeIdx != (unsigned int)-1 )
//	{
//		if( SCENEMAN->IsValidDynamicNode( mFollowSceneNodeIdx, mpFollowSceneNode ) == false )
//		{
//			GameErrorLog( "cEffectSceneNode::OnProcess Invalid follow scene node (%d)", mFollowSceneNodeIdx );
//		}
//	}

	if( mpFollowSceneNode )
	{
		mCheckFrustum = mpFollowSceneNode->IsEnableFrustumFlag();
	}
	else
	{
		if( SCENEMAN->CalcDynamicVisibleLevel( this ) == true )
			SetAlphaBlended( 1.0f );
		else
			SetAlphaBlended( 0.0f );
	}

	if( mDummyIdx == (unsigned int)-1 )
		GetNiObj()->Update( accumTime * 0.001f );


	/// 반복 동작을 가진 경우 엔진 노드를 처리
	if( mNifAniInfo )
	{
		unsigned char state = mNifAniInfo->Update( deltaTime, accumTime );
		if( state == cNifAnimationInfo::eEvent_End )
		{
			RemoveSelf();
			return;
		}
	}

	if( mAlpha < 0.000001f )
		mCheckFrustum = false;
	else
		mCheckFrustum = true;
}

bool cEffectSceneNode::FindDamageGeom( NiNode* pNode, NiGeometry** pArray )
{
	if( pNode == 0 || pArray == 0 )
		return false;

	cString str;
	for( unsigned int i = 0; i<4; i++ )
	{
		str.Format("Dam%d", i+1);
		NiAVObject* pobj = pNode->GetObjectByName( str.Cstr() );
		if( pobj == 0 )
		{
			assert(0);
			return false;
		}

		NiGeometry* pgeom = cSceneNode::GetGeom( pobj );
		if( pgeom == 0 )
		{
			assert(0);
			return false;
		}

		if( pgeom->GetVertexCount() != 4 )
			return false;
		if( pgeom->GetTextureSets() != 1 )
			return false;

		pArray[i] = pgeom;
	}
	return true;
}

void cEffectSceneNode::SetLoopFlag( bool flag )
{
	if( mNifAniInfo )
	{
		if( mNifAniInfo->IsEnable() )
			mNifAniInfo->SetLooping( flag );
		else
			RemoveSelf();
	}
	else
	{
		RemoveSelf();
	}
}

bool cEffectSceneNode::GetLoopFlag()
{
	if( mNifAniInfo )
		return mNifAniInfo->IsLooping();

	return false;
}

bool cEffectSceneNode::IsViewNode()
{
	if( mIsDistCheck )
	{
		NiPoint3 camPos = CAMERAMAN->GetCurrentCameraPos();
		if( (camPos - GetWorldTranslate()).Length() > 5000.0f )
			return false;
	}
	
	return cSceneNode::IsViewNode();
}

void cEffectSceneNode::DisableZBuffer( NiAVObject* obj )
{
	NiZBufferProperty* prop = (NiZBufferProperty*)obj->GetProperty( NiProperty::ZBUFFER );
	if( prop )
	{
		prop->SetZBufferTest( false );
		prop->SetZBufferWrite( false );
	}

	//NiStencilProperty* sten = (NiStencilProperty*)obj->GetProperty( NiProperty::STENCIL );
	//if( sten )
	//{
	//	sten->SetStencilOn( true );
	//	sten->SetStencilFailAction( NiStencilProperty::ACTION_KEEP );
	//	sten->SetStencilPassAction( NiStencilProperty::ACTION_REPLACE );
	//	sten->SetStencilPassZFailAction( NiStencilProperty::ACTION_REPLACE );
	//	sten->SetStencilFunction( NiStencilProperty::TEST_GREATEREQUAL );
	//	sten->SetStencilReference( 10 );
	//}

	if( NiIsKindOf(NiNode, obj) )
	{
		NiAVObject* child = 0;
		NiNode* node = (NiNode*)obj;
		for( unsigned int i = 0; i < node->GetArrayCount(); ++i )
		{
			child = node->GetAt( i );
			if( child )
				DisableZBuffer( child );
		}
	}
}
