#include "StdAfx.h"
#include "StageScript.h"

#include "FileLoader.h"
#include "Tokenizer.h"

#include "Player.h"


cStageScript* cStageScript::mpStageScript = NULL;



cStageScript::cStageScript()
{
	/// 싱글톤
	if( !mpStageScript )
	{
		mpStageScript = this;
	}
}

cStageScript::~cStageScript()
{
	Release();
}

bool cStageScript::Init()
{
	Release();

	/// 맵이동 스크립트 로드
	if( !StageChangePosScriptLoad() )
	{
		assert(NULL);
		return false;
	}

#ifndef _CLIENT
	if( !LoadMapInfo() )
	{
		assert(0);
		return false;
	}
#endif

	return true;
}


void cStageScript::Release()
{
	cPointerHashMap::cIterator iter;
	if( mStageChangePosMap.IsEmpty() == false )
	{
		for(iter = mStageChangePosMap.Begin(); iter != mStageChangePosMap.End(); ++iter)
		{
			delete (sStageChangePos*)(*iter).mSecond;
		}
		mStageChangePosMap.Clear();
	}

	mMapPortalArray.Clear();

#ifndef _CLIENT
	cArrayHashMap::cIterator i = mMapInfoMap.Begin();
	cArrayHashMap::cIterator end = mMapInfoMap.End();
	i = mMapInfoMap.Begin();
	end = mMapInfoMap.End();
	for( ; i != end; ++i )
	{
		sMapInfo* p = (sMapInfo*)(*i).mSecond;
		SAFE_DELETE(p);
	}
	mMapInfoMap.Clear();
#endif
}

bool cStageScript::StageChangePosScriptLoad()
{
	cFileLoader loader;
	cString pathName = "./Script/resource/MapChangePos.txt";

	if( loader.Open( pathName, true ) == false )
	{
		assert( 0 && "failed to load MapChangePos.txt" );
		return false;
	}

	cTokenizer tokenizer( loader.GetBufferPtr(), loader.GetSize(), " \t\r\n", pathName.Cstr() );
	cString str;

	cPointerHashMap::cIterator begin = mStageChangePosMap.Begin();
	sStageChangePos* pChangePos = 0;

	while( tokenizer.IsEnd() == false )
	{
		pChangePos = new sStageChangePos;

		/// 고유번호
		if( tokenizer.GetNext( &str ) == false )	{ goto ERR; }
		pChangePos->mPosIdx = str.ToInt();

		///
		if( tokenizer.GetNext( &str ) == false )	{ goto ERR; }
		pChangePos->mFileName = str;

		/// 맵번호
		if( tokenizer.GetNext( &str ) == false )	{ goto ERR; }
		pChangePos->mMapNumber = (unsigned char)str.ToInt();

		/// 좌표X
		if( tokenizer.GetNext( &str ) == false )	{ goto ERR; }
		pChangePos->mPosX = str.ToFloat();

		/// 좌표Y
		if( tokenizer.GetNext( &str ) == false )	{ goto ERR; }
		pChangePos->mPosY = str.ToFloat();

		/// 범위
		if( tokenizer.GetNext( &str ) == false )	{ goto ERR; }
		pChangePos->mRange = str.ToFloat();

		/// 목적지맵idx
		if( tokenizer.GetNext( &str ) == false )	{ goto ERR; }
		pChangePos->mTargetPosIdx = str.ToInt();

		/// 회전 각도
		if( tokenizer.GetNext( &str ) == false )	{ goto ERR; }
		pChangePos->mRotAngle = S_ToRadian( str.ToFloat() );

		/// 보관소 저장
		if( mStageChangePosMap.Insert( pChangePos->mPosIdx, pChangePos ) == false )
		{
			assert(0);
			goto ERR;
		}
	}

	begin = mStageChangePosMap.Begin();
	while( begin != mStageChangePosMap.End() )
	{
		pChangePos = (sStageChangePos*)((*begin).mSecond);
		++begin;

		if( pChangePos == 0 )
		{
			assert(0);
			goto ERR;
		}

		pChangePos->mTargetInfo = (sStageChangePos*)mStageChangePosMap.GetAt( pChangePos->mTargetPosIdx );
	}

	return true;

ERR:
	delete pChangePos;

	return false;
}



unsigned long cStageScript::GetStageChangeIndex( float posX, float posY )
{
	NiPoint2 characterPos( posX,  posY );
	NiPoint2 teleportStartPos = NiPoint2::ZERO;

	sStageChangePos* pChangePos = 0;
	cPointerHashMap::cIterator begin = mStageChangePosMap.Begin();
	while( begin != mStageChangePosMap.End() )
	{
		pChangePos = (sStageChangePos*)(*begin).mSecond;
		unsigned long posIndex = (*begin).mFirst;
		++begin;

		if( pChangePos == 0 )
			continue;

		/// 검사 기준에서 목적지 제외
		if( pChangePos->mTargetInfo == 0 )
			continue;

		teleportStartPos.x = pChangePos->mPosX;
		teleportStartPos.y = pChangePos->mPosY;

		mRangeCheck.SetRadius( pChangePos->mRange );

		/// 입력좌표가 스크립트좌표 허용범위 안에 들어온경우 스크립트idx를 리턴
		if( mRangeCheck.IsRange( teleportStartPos, characterPos ) )
		{
			return posIndex;
		}
	}
	return 0;
}


sTargetPos cStageScript::GetStageChangePos( float posX, float posY, unsigned short mapNum, float objectSize, unsigned long posIdx )
{
	sTargetPos targetPos;
	targetPos.mMapNumber = 0;
	targetPos.mPosX = 0.0f;
	targetPos.mPosY = 0.0f;

	NiPoint2 characterPos( posX, posY );

	/// 스크립트상의 시작좌표
	sStageChangePos* pChangePos = (sStageChangePos*)mStageChangePosMap.GetAt( posIdx );
	if( pChangePos == 0 )
	{
		assert(0);
		return targetPos;
	}

	NiPoint2 teleportStartPos( pChangePos->mPosX, pChangePos->mPosY );

	/// 플레이어 현재 맵번호 확인
	if( pChangePos->mMapNumber != mapNum )
	{
		assert(0);
		return targetPos;
	}

	/// 시작좌표와 플레이어좌표를 확인
	/// 이동시 좌표위치 허용범위 더함
	mRangeCheck.SetRadius( pChangePos->mRange + objectSize + SYNC_MOVE_RANGE );
	if( mRangeCheck.IsNotRange( teleportStartPos, characterPos ) )
	{
		return targetPos;
	}

	/// 랜덤 위치
	sStageChangePos* pTargetChangePos = pChangePos->mTargetInfo;
	if( pTargetChangePos == 0 )
		return targetPos;

	int range = (int) pTargetChangePos->mRange;
	/// 좌표가 맞는경우 목적지좌표를 리턴
	targetPos.mMapNumber = pTargetChangePos->mMapNumber;
	targetPos.mPosX = pTargetChangePos->mPosX + ( rand() % range ) - ( rand() % range );
	targetPos.mPosY = pTargetChangePos->mPosY + ( rand() % range ) - ( rand() % range );
	targetPos.mRotAngle = pTargetChangePos->mRotAngle;

	return targetPos;
}


sTargetPos cStageScript::GetStageChangePos( unsigned long posIdx )
{
	sTargetPos targetPos;
	targetPos.mMapNumber = 0;
	targetPos.mPosX = 0.0f;
	targetPos.mPosY = 0.0f;

	/// 스크립트상의 시작좌표
	sStageChangePos* pChangePos = (sStageChangePos*)mStageChangePosMap.GetAt( posIdx );
	if( pChangePos == NULL )
	{
		assert(NULL);
		return targetPos;
	}

	NiPoint2 teleportStartPos( pChangePos->mPosX, pChangePos->mPosY );

	/// 랜덤 위치
	int range = (int)pChangePos->mRange;
	/// 좌표가 맞는경우 목적지좌표를 리턴
	targetPos.mMapNumber = pChangePos->mMapNumber;
	targetPos.mPosX = pChangePos->mPosX + ( rand() % range ) - ( rand() % range );
	targetPos.mPosY = pChangePos->mPosY + ( rand() % range ) - ( rand() % range );
	targetPos.mRotAngle = pChangePos->mRotAngle;

	return targetPos;
}


NiPoint2 cStageScript::CalcNearTargetMapPos( unsigned short mapNum, float posX, float posY )
{
	NiPoint2 heroPos( posX, posY );
	NiPoint2 nearPos( posX, posY );
	unsigned long nearRange = ULONG_MAX;

	unsigned long range;
	NiPoint2 targetPos;
	unsigned long randRange = 0;

	cPointerHashMap::cIterator begin = mStageChangePosMap.Begin();
	while( begin != mStageChangePosMap.End() )
	{
		sStageChangePos* pChangePos = (sStageChangePos*)(*begin++).mSecond;
		if( pChangePos == 0 )
		{
			assert(0);
			continue;
		}
		if( pChangePos->mMapNumber != mapNum )
			continue;

		/// 검사 기준에서 출발지 제외
		if( pChangePos->mTargetInfo != 0 )
			continue;

		targetPos.x = pChangePos->mPosX;
		targetPos.y = pChangePos->mPosY;
		randRange = (unsigned long)pChangePos->mRange;

		range = (unsigned long)(( heroPos - targetPos ).Length());
		if( range < nearRange )
		{
			nearRange = range;
			nearPos = targetPos;     
		}
	}

	if( randRange != 0 )
	{
		nearPos.x = nearPos.x + ( rand() % randRange ) - ( rand() % randRange );
		nearPos.y = nearPos.y + ( rand() % randRange ) - ( rand() % randRange );
	}

	return nearPos;
}


bool cStageScript::CalcTargetMapPos( unsigned long posIdx, float *pX, float *pY )
{
	/// 스크립트상의 시작좌표
	sStageChangePos* pChagePos = (sStageChangePos*)mStageChangePosMap.GetAt( posIdx );
	if( pChagePos == 0 )
		return false;

	if( pX == 0 || pY == 0 )
		return false;

	unsigned long range = (unsigned long)pChagePos->mRange;

	*pX = pChagePos->mPosX + ( rand() % range ) - ( rand() % range );
	*pY = pChagePos->mPosY + ( rand() % range ) - ( rand() % range );

	return true;
}

///
tPointerArray<void*>* cStageScript::GetMapChangeStarterArr( unsigned short mapNum )
{
	mMapPortalArray.Clear();

	if( mStageChangePosMap.IsEmpty() )
		return &mMapPortalArray;

	cPointerHashMap::cIterator begin = mStageChangePosMap.Begin();

	while( begin != mStageChangePosMap.End() )
	{
		sStageChangePos* pChangePos = (sStageChangePos*)(*begin++).mSecond;
		if( pChangePos == 0 )
		{
			assert(0);
			continue;
		}

		if( pChangePos->mMapNumber != mapNum )
			continue;

		/// 검사 기준에서 목적지 제외
		if( pChangePos->mTargetInfo == 0 )
			continue;

		mMapPortalArray.PushBack( pChangePos );
	}

	return &mMapPortalArray;
}

///
tPointerArray<void*>* cStageScript::GetMapChangeDestArr( unsigned short mapNum )
{
	mMapPortalArray.Clear();

	if( mStageChangePosMap.IsEmpty() )
		return &mMapPortalArray;

	cPointerHashMap::cIterator begin = mStageChangePosMap.Begin();

	while( begin != mStageChangePosMap.End() )
	{
		sStageChangePos* pChangePos = (sStageChangePos*)(*begin++).mSecond;
		if( pChangePos == 0 )
		{
			assert(0);
			continue;
		}

		if( pChangePos->mMapNumber != mapNum )
			continue;

		/// 검사 기준에서 출발지 제외
		if( pChangePos->mTargetInfo != 0 )
			continue;

		mMapPortalArray.PushBack( pChangePos );
	}

	return &mMapPortalArray;
}

sStageChangePos* cStageScript::GetPosScriptInfo( unsigned long idx )
{
	return (sStageChangePos*)mStageChangePosMap.GetAt( idx );
}

#ifndef _CLIENT
bool cStageScript::LoadMapInfo()
{
	cFileLoader loader;
	cString pathName = "./Script/Resource/MapData.txt";

	if( loader.Open( pathName, true ) == false )
	{
		assert( 0 && "failed to load MapData.txt" );
		return false;
	}

	cTokenizer tokenizer( loader.GetBufferPtr(), loader.GetSize(), " \t\r\n", pathName.Cstr() );
	cString str;

	sMapInfo* pInfo = 0;
	while( tokenizer.IsEnd() == false )
	{
		pInfo = new sMapInfo;

		// Map number
		if( tokenizer.GetNext( &str ) == false )	{ goto ERR; }
		pInfo->mapNum = str.ToInt();

		/// Map 폴더번호
		if( tokenizer.GetNext( &str ) == false )	{ goto ERR; }
		pInfo->folderIdx = str.ToInt();

		if( tokenizer.GetNext( &str ) == false )	{ goto ERR; }
		if( tokenizer.GetNext( &str ) == false )	{ goto ERR; }

		/// 해쉬에 몬스터 별로 기록
		if( mMapInfoMap.Insert( pInfo->mapNum, pInfo ) == false )
		{
			assert(0);
			goto ERR;
		}
	}

	return true;

ERR:
	delete pInfo;
	return false;
}

unsigned long cStageScript::GetMapFolderIdx( unsigned long mapIdx )
{
	sMapInfo* info = (sMapInfo*)mMapInfoMap.GetAt( mapIdx );
	if( info == 0 )
	{
		assert(0);
		return 0;
	}
	return info->folderIdx;
}
#endif
