#include "stdafx.h"
#include "Hero.h"

#include "MapEditorApp.h"
#include "NaviFieldEditor.h"

#include "Engine/Box.h"
#include "Engine/LookAtCamera.h"
#include "Engine/NaviMesh.h"
#include "Engine/SoundSystem.h"
#include "Engine/ResourceManager.h"
#include "Engine/SceneManager.h"
#include "Engine/TrailGeometry.h"
#include "Engine/PathFinder.h"

cHero* cHero::mSingleton = 0;

cHero::cHero( cLookAtCamera* cam )
: mPos( NiPoint3::ZERO )
, mLevel0Lines( NiColorA(1.0f, 1.0f, 1.0f), NiColorA::BLACK )
, mLevel1Lines( NiColorA(1.0f, 1.0f, 0.0f), NiColorA::BLACK )
, mLevel2Lines( NiColorA(1.0f, 0.0f, 0.0f), NiColorA::BLACK )
, mCamera( cam )
, mNode( 0 )
, mBoundSphere( NiPoint3::ZERO, 0.0f )
//, mRibbonTexture( 0 )
//, mRibbonGeom( 0 )
, mNeedUpdate( false )
{
	assert( mSingleton == 0 && "bad singleton!" );
	mSingleton = this;

	assert( cam && "null camera" );

	mPathCount = 0;
	mPathIndex = 0;

	/// 길찾기 이동 경로 선을 생성
	mPosCoords = NiNew NiPoint3[MAX_PATH_COUNT];
	for( unsigned int i = 0; i < MAX_PATH_COUNT; ++i )
	{
		mPosCoords[i] = NiPoint3::ZERO;
	}

	mColors = NiNew NiColorA[MAX_PATH_COUNT];
	for( unsigned int i = 0; i < MAX_PATH_COUNT; ++i )
	{
		if( i % 2 == 0 )
			mColors[i] = NiColorA( 1.0f, 0.0f, 0.0f );
		else
			mColors[i] = NiColorA( 0.0f, 0.0f, 1.0f );
	}

	mFlags = NiAlloc( NiBool, MAX_PATH_COUNT );
	for( unsigned int i = 0; i < MAX_PATH_COUNT; ++i )
	{
		mFlags[i] = 1;
	}
	mFlags[MAX_PATH_COUNT-1] = 0;

	mPathLines = NiNew NiLines(
		MAX_PATH_COUNT,
		mPosCoords,
		mColors,
		0,
		0,
		NiGeometryData::NBT_METHOD_NONE,
		mFlags );

	mMatProp = NiNew NiMaterialProperty;
	mMatProp->SetAmbientColor( NiColor::WHITE );
	mMatProp->SetDiffuseColor( NiColor::WHITE );
	mMatProp->SetSpecularColor( NiColor::WHITE );
	mMatProp->SetEmittance( NiColor::WHITE );

	mVertColorProp = NiNew NiVertexColorProperty;
	mVertColorProp->SetSourceMode( NiVertexColorProperty::SOURCE_EMISSIVE );
	mVertColorProp->SetLightingMode( NiVertexColorProperty::LIGHTING_E );

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

	mPathLines->AttachProperty( mMatProp );
	mPathLines->AttachProperty( mVertColorProp );
	mPathLines->AttachProperty( mWireProp );
	mPathLines->UpdateProperties();
	mPathLines->Update( 0.0f );
}

cHero::~cHero()
{
	Clear();
	mSingleton = 0;
}

void cHero::Clear()
{
	if( mNode )
	{
		delete mNode;
		mNode = 0;
	}

	//if( mRibbonTexture )
	//{
	//	mRibbonTexture->Release();
	//	mRibbonTexture = 0;
	//}
}

bool cHero::Init()
{
	Clear();

	/// 현재 경로를 설정
	cString path = theApp.GetBaseDir();
	path += "MapData";
	SetCurrentDirectory( path.Cstr() );

	/// 노드를 생성
	mNode = new cPlayerSceneNode;

	/// 노드를 초기화
	cPlayerSceneNodeParam param;
	param.mPathName.Format( "%s/%s", path.Cstr(), "h_m.kfm");

	if( mNode->Init( param ) == false )
	{
		delete mNode;
		mNode = 0;

		cString msg;
		msg.Format( "Failed to init hero scene node: %s", param.mPathName.Cstr() );
		AfxMessageBox( msg.Cstr() );
		return false;
	}

	mBoundSphere = mNode->GetBoundSphere();

	/// 궤적 텍스처를 로딩
	//mRibbonTexture = RESOURCEMAN->LoadD3DTexture( "trail01.tga" );

	/// 궤적을 생성
	//mRibbonGeom = mNode->CreateRibbonGeom( 100, 0.05f, 0.02f );
	//mRibbonGeom->SetTexture( mRibbonTexture );
	return true;
}

void cHero::Reset()
{
	mNeedUpdate = true;

	NiPoint3 min = NAVIMESH->GetBoundBox().GetMin();
	NiPoint3 max = NAVIMESH->GetBoundBox().GetMax();
	mPos = (min + max) * 0.5f;

	/// 높이값을 계산
	NAVIMESH->CalcHeight( &mPos.z, mPos.x, mPos.y );

	/// 카메라의 시점을 설정
	mCamera->SetLookAt( mPos + NiPoint3(0.0f, 0.0f, 124.0f) );

	/// 노드를 갱신
	if( mNode )
	{
		mNode->SetTranslate( mPos );
		mNode->Update();

		mBoundSphere = mNode->GetBoundSphere();
	}
}

void cHero::Process( float deltaTime )
{
	if( mPathCount == 0 )
	{
		if( mNeedUpdate )
		{
			mNeedUpdate = false;

			/// 이동
			mNode->SetTranslate( mPos );

			mBoundSphere = mNode->GetBoundSphere();
		}
		return;
	}

	NiPoint2 nextPos = mPathArray[mPathIndex];

	/// 방향
	NiPoint2 dir1, dir2;
	dir1.x = nextPos.x - mPos.x;
	dir1.y = nextPos.y - mPos.y;
	dir1.Unitize();

	/// 이동
	float speed = 500.0f;
	mPos.x += dir1.x * deltaTime * speed;
	mPos.y += dir1.y * deltaTime * speed;
	mPos.z = 0.0f;

	/// 보정을 위해서 현재계산된 위치로의 거리를 계산한다.
	dir2.x = nextPos.x - mPos.x;
	dir2.y = nextPos.y - mPos.y;
	dir2.Unitize();

	if( dir2.Dot(dir1) <= 0.0f )
	{
		//mPos.x = nextPos.x;
		//mPos.y = nextPos.y;

		if( mPathIndex < mPathCount-1 )
		{
			mNeedUpdate = true;
			nextPos = mPathArray[++mPathIndex];
		}
		else
		{
			mNeedUpdate = false;
			mPathCount = 0;
			mPathIndex = 0;
		}
	}
	else
	{
		mNeedUpdate = true;
	}

	if( mNeedUpdate )
	{
		mNeedUpdate = false;

		/// 높이값을 계산
		NAVIMESH->CalcHeight( &mPos.z, mPos.x, mPos.y );

		/// 카메라의 시점을 설정
		mCamera->SetLookAt( mPos + NiPoint3(0.0f, 0.0f, 124.0f) );

		/// 노드를 갱신
		if( mNode )
		{
			/// 회전
			float dot = dir1.Dot( -NiPoint2::UNIT_Y );
			float ang = NiACos( dot );
			if( dir1.x >= 0.0f )
				ang = -ang;
			mNode->SetRotate( NiPoint3::UNIT_Z, ang );

			/// 이동
			mNode->SetTranslate( mPos );

			mBoundSphere = mNode->GetBoundSphere();
		}

		/// 궤적
		//if( mRibbonGeom )
		//{
		//	mRibbonGeom->SetPushPoint( mPos, mPos + NiPoint3(0.0f, 0.0f, 124.0f) );
		//}
	}
}

void cHero::RenderLines()
{
	///
	mLevel0Lines.Process( mPos, SCENEMAN->GetLevelDistance0(), NAVIMESH );
	mLevel1Lines.Process( mPos, SCENEMAN->GetLevelDistance1(), NAVIMESH );
	mLevel2Lines.Process( mPos, SCENEMAN->GetLevelDistance2(), NAVIMESH );
	mLevel0Lines.Render();
	mLevel1Lines.Render();
	mLevel2Lines.Render();

	mPathLines->RenderImmediate( NiRenderer::GetRenderer() );
}

void cHero::SetTranslate( const NiPoint3& pos )
{
	mPos = pos;
	mPathCount = 0;

	/// 카메라의 시점을 설정
	mCamera->SetLookAt( mPos + NiPoint3(0.0f, 0.0f, 124.0f) );

	/// 노드를 갱신
	mNeedUpdate = true;
}

void cHero::SetTargetPos( const NiPoint3& pos )
{
	/// 거리 제한
	//if( (mPos - pos).Length() > 30000.0f )
	//	return;

	/// 길찾기
	if( PATHFINDER->FindPath( mPathArray, &mPathCount, MAX_PATH_COUNT, NiPoint2(mPos.x, mPos.y), NiPoint2(pos.x, pos.y) ) )
	{
		mPathIndex = 0;

		///
		mPosCoords[0] = mPos;
		NiPoint3 p;
		unsigned int i = 0;
		unsigned int iend = mPathCount < MAX_PATH_COUNT - 1 ? mPathCount : MAX_PATH_COUNT - 1;

		for( ; i < iend; ++i )
		{
			p.x = mPathArray[i].x;
			p.y = mPathArray[i].y;

			NAVIMESH->CalcHeight( &(p.z), p.x, p.y );
			p.z += 10.0f;
			mPosCoords[1+i] = p;
		}
		for( ; i < MAX_PATH_COUNT-1; ++i )
		{
			mPosCoords[1+i] = p;
		}

		///
		NiGeometryData* geom = mPathLines->GetModelData();

		geom->Replace(
			MAX_PATH_COUNT,
			mPosCoords,
			0,
			mColors,
			0,
			0,
			NiGeometryData::NBT_METHOD_NONE );

		geom->MarkAsChanged( NiGeometryData::VERTEX_MASK );
	}
}
