#include "stdafx.h"
#include "StaticSceneNode.h"

#include "SceneManager.h"
#include "ResourceManager.h"

#include "NifAnimationInfo.h"

cStaticSceneNode::cStaticSceneNode( eSceneNodeType type )
: cSceneNode( type )
, mNumVerts( 0 )
, mVisibleLevel( 0 )
, mOccludeLevel( 1 )
, mFogApplied( false )
, mMaterialApplied( false )
, mPickedByCamera( false )
{
	mNifAniInfo = new cNifAnimationInfo;
}

cStaticSceneNode::~cStaticSceneNode()
{
	delete mNifAniInfo;
}

void cStaticSceneNode::Process( float deltaTime, float accumTime )
{
	ProcessVisibleLevel( deltaTime );
	if( mCheckFrustum == false )
		return;

	/// 반복 동작을 가진 경우 엔진 노드를 처리
	if( mNifAniInfo )
	{
		mNeedUpdateTransform = true;
		mNifAniInfo->Update( accumTime );
	}

	cSceneNode::Process( deltaTime, accumTime );

#ifdef REGEN_TOOL
	mTargetAlpha = 0.4f;

	/// 알파를 처리
	ProcessAlpha( deltaTime );
#endif
}

void cStaticSceneNode::ProcessVisibleLevel( float deltaTime )
{
	/// 가시 레벨을 처리
#ifdef MAP_EDITOR
	if( mVisibleLevel < 3 )
	{
		/// 정해진 거리만큼 거르는 체크
		if( mVisibleLevel <  SCENEMAN->CalcVisibleLevel( this ) )
		{
			if( mVisibleLevel == 2 )
			{
				int a = 0;
				a = 1;
			}

			mTargetAlpha = 0.0f;
			if( mAlpha == mTargetAlpha )
				/// no frustum check
				mCheckFrustum = false;
			else
				/// frustum check
				mCheckFrustum = true;
		}
		else
		{
			if( mOccludeLevel ==1 && mPickedByCamera )
			{
				mPickedByCamera = false;
				mTargetAlpha = 0.4f;
			}
			else
			{
				mTargetAlpha = 1.0f;
			}

			/// frustum check
			mCheckFrustum = true;
		}

		/// 알파를 처리
		ProcessAlpha( deltaTime );
	}

	/*
//	if( mOccludeLevel == 1 )
	{
		if( culler.mPlaying )
		{
			if( mVisibleLevel < 3 && mVisibleLevel < culler.GetVisibleLevel( GetCenter() ) )
			{
				mTargetAlpha = 0.0f;
			}
			else
			{
				if( mPickedByCamera )
				{
					mPickedByCamera = false;
					mTargetAlpha = 0.4f;
				}
				else
				{
					mTargetAlpha = 1.0f;
				}
			}
		}
		else
		{
			mTargetAlpha = 1.0f;
		}

		/// 알파를 처리
		ProcessAlpha( deltaTime );
	}
//	else
//	{
//	}
*/
#else
deltaTime;
#endif
}

bool cStaticSceneNode::Save( cFileSaver& saver )
{
	/// 상위 정보를 저장
	if( cSceneNode::Save( saver ) == false ) 
		return false;

	/// 안개 적용 여부를 저장
	saver.WriteUnsignedInt( mFogApplied ? 1 : 0 );

	/// 재질 적용 여부를 저장
	saver.WriteUnsignedInt( mMaterialApplied ? 1 : 0 );

	/// 재질 속성을 저장
	saver.Write( &mMaterial, sizeof(cMaterialData_Ver9) );

	/// 기타 속성을 저장
	saver.WriteUnsignedInt( mVisibleLevel );
	saver.WriteUnsignedInt( mOccludeLevel );
	return true;
}

bool cStaticSceneNode::Init( cStaticSceneNodeParam& param )
{
	/// 파일을 로딩
	if( RESOURCEMAN->LoadNIF( param.mPathName ) == false )
		return false;

	/// 리소스 관리자로부터 엔진 노드를 복제
	NiNode* n = RESOURCEMAN->CloneNiNode( param.mPathName );

	if( n == 0 )
	{
		assert( 0 && "failed to clone node from resource manager" );
		return false;
	}

	/// 상위 초기화
	param.mNiObject = n;
	if( cSceneNode::Init( param ) == false )
	{
		return false;
	}

	/// 가시 레벨을 설정
	mVisibleLevel = param.mVisibleLevel;

	/// 차폐 레벨을 설정
	mOccludeLevel = param.mOccludeLevel;

	/// 알파 속성을 설정
	NiAlphaProperty* alphaProp = (NiAlphaProperty*)n->GetProperty( NiProperty::ALPHA );
	mAlphaForced = (alphaProp != 0);

	if( mAlphaForced )
	{
		mAlphaBlended = true;

#ifdef REGEN_TOOL
		bool blendEnabled = alphaProp->GetAlphaBlending();
		bool testEnabled = alphaProp->GetAlphaTesting();
		NiAlphaProperty::TestFunction testFunc = alphaProp->GetTestMode();
		unsigned char testRef = alphaProp->GetTestRef();
		mAlphaDataList.PushBack( cAlphaData( blendEnabled, testEnabled, testFunc, testRef, alphaProp ) );
#endif
	}
	else
	{
		mAlphaBlended = false;

#ifdef MAP_EDITOR

		mWireProp = NiNew NiWireframeProperty;
		mWireProp->SetWireframe( false );

		n->AttachProperty( mWireProp );

		if( mOccludeLevel == 1 )
#endif
		{
			/// 알파 속성을 생성
			alphaProp = NiNew NiAlphaProperty;
			alphaProp->SetAlphaBlending( false );
			alphaProp->SetAlphaTesting( false );
			n->AttachProperty( alphaProp );

			bool blendEnabled = alphaProp->GetAlphaBlending();
			bool testEnabled = alphaProp->GetAlphaTesting();
			NiAlphaProperty::TestFunction testFunc = alphaProp->GetTestMode();
			unsigned char testRef = alphaProp->GetTestRef();
			mAlphaDataList.PushBack( cAlphaData( blendEnabled, testEnabled, testFunc, testRef, alphaProp ) );
		}
	}

	/// 속성들을 수집
	CollectProperties( n );

	/// 재질을 설정
	mMaterialApplied = param.mMaterialApplied;
	if( mMaterialApplied )
		SetMaterial( param.mMaterial );

	/// 안개 속성을 부착
	mFogApplied = param.mFogApplied;
	if( mFogApplied )
	{
		n->AttachProperty( SCENEMAN->GetFogProperty() );
	}

	/// 조명을 부착
	n->AttachEffect( SCENEMAN->GetAmbientLight( 0 ) );
	n->AttachEffect( SCENEMAN->GetDirLight( 0 ) );

	/// 애니메이션 컨트롤러 정보 수집
	mNifAniInfo->CollectData( n );

	if( mNifAniInfo->GetNumberOfControllers() )
	{
		mNifAniInfo->SetAnimType( NiTimeController::APP_INIT );
		mNifAniInfo->SetLooping( true );
	}
	else
	{
		/// 애니 정보가 없다면 해제
		delete mNifAniInfo;
		mNifAniInfo = 0;
	}

	/// 갱신
	mNeedUpdateProperties = true;
	n->SetSelectiveUpdate( false );
	n->UpdateProperties();
	n->UpdateEffects();
	n->Update( 0.0f );

	/// 삼각형 수를 계산
	cGeomList::cIterator i = mGeomList.Begin();
	cGeomList::cIterator iend = mGeomList.End();

	for( ; i != iend; ++i )
	{
		mNumVerts += i->mFirst->GetActiveVertexCount();
	}
	return true;
}

void cStaticSceneNode::SetMaterial( const cMaterialData& material )
{
	mMaterial = material;

	if( mMaterialApplied )
	{
		cMaterialDataList::cIterator i = mMatDataList.Begin();
		cMaterialDataList::cIterator iend = mMatDataList.End();

		for( ; i != iend; ++i )
		{
			NiMaterialProperty* matProp = i->mProp;

			matProp->SetAmbientColor( material.mAmbient );
			matProp->SetDiffuseColor( material.mDiffuse );
			matProp->SetSpecularColor( material.mSpecular );
			matProp->SetEmittance( material.mEmissive );
			matProp->SetShineness( material.mShininess );
			matProp->SetAlpha( material.mAlpha );
		}
	}
}

void cStaticSceneNode::SetAmbient( const NiColor& color )
{
	mMaterial.mAmbient = color;

	if( mMaterialApplied )
	{
		cMaterialDataList::cIterator i = mMatDataList.Begin();
		cMaterialDataList::cIterator iend = mMatDataList.End();

		for( ; i != iend; ++i )
		{
			i->mProp->SetAmbientColor( color );
		}
	}
}

void cStaticSceneNode::SetDiffuse( const NiColor& color )
{
	mMaterial.mDiffuse = color;

	if( mMaterialApplied )
	{
		cMaterialDataList::cIterator i = mMatDataList.Begin();
		cMaterialDataList::cIterator iend = mMatDataList.End();

		for( ; i != iend; ++i )
		{
			i->mProp->SetDiffuseColor( color );
		}
	}
}

void cStaticSceneNode::SetEmissive( const NiColor& color )
{
	mMaterial.mEmissive = color;

	if( mMaterialApplied )
	{
		cMaterialDataList::cIterator i = mMatDataList.Begin();
		cMaterialDataList::cIterator iend = mMatDataList.End();

		for( ; i != iend; ++i )
		{
			i->mProp->SetEmittance( color );
		}
	}
}

void cStaticSceneNode::SetMaterialApplied( bool matApplied )
{
//	if( mMaterialApplied == matApplied )
//	{
//		NiMessageBox( _T("Bug Check!!"), _T("Error") );
//	}

	mMaterialApplied = matApplied;

	if( mMaterialApplied )
		SetMaterial( mMaterial );
	else
		RestoreMaterial();
}

void cStaticSceneNode::SetVisibleLevel( unsigned int level )
{
	mVisibleLevel = level;
}

void cStaticSceneNode::SetOccludeLevel( unsigned int level )
{
	mOccludeLevel = level;
}

void cStaticSceneNode::SetFogApplied( bool fogApplied )
{
	if( mFogApplied == fogApplied )
		return;

	if( fogApplied )
	{
		mFogApplied = true;
		mNiObject->AttachProperty( SCENEMAN->GetFogProperty() );
	}
	else
	{
		mFogApplied = false;
		mNiObject->DetachProperty( SCENEMAN->GetFogProperty() );
	}
	mNiObject->UpdateProperties();
}

void cStaticSceneNode::RestoreMaterial()
{
	cMaterialDataList::cIterator i = mMatDataList.Begin();
	cMaterialDataList::cIterator iend = mMatDataList.End();

	for( ; i != iend; ++i )
	{
		cMaterialData& matData = *i;
		NiMaterialProperty* matProp = matData.mProp;

		if( matProp )
		{
			matProp->SetAmbientColor( matData.mAmbient );
			matProp->SetDiffuseColor( matData.mDiffuse );
			matProp->SetSpecularColor( matData.mSpecular );
			matProp->SetEmittance( matData.mEmissive );
			matProp->SetShineness( matData.mShininess );
			matProp->SetAlpha( matData.mAlpha );
		}
	}
}

NiTimeController* cStaticSceneNode::GetTimeController( NiAVObject* obj )
{
	NiTimeController* tc = NiGetController( NiTimeController, obj );

	if( tc )
		return tc;

	NiNode* node = NiDynamicCast( NiNode, obj );

	if( node )
	{
		NiAVObject* child = 0;

		for( unsigned int i = 0, iend = node->GetArrayCount(); i < iend; ++i )
		{
			child = node->GetAt( i );

			if( child )
			{
				tc = GetTimeController( child );
				if( tc )
					return tc;
			}
		}
	}
	return 0;
}

bool cStaticSceneNode::IsAnimated() const
{
	return mNifAniInfo->GetNumberOfControllers() > 0;
}

/*
unsigned int cStaticSceneNode::CalcVisibleLevel()
{
	float sqrDist = (mHeroPos - GetCenter()).SqrLength();

	if( sqrDist > mSqaredLevelDistance2 )
		return 3;
	if( sqrDist > mSqaredLevelDistance1 )
		return 2;
	if( sqrDist > mSqaredLevelDistance0 )
		return 1;
	return 0;
}
*/