#include "stdafx.h"
#include "MiniMapWindow.h"

#include "RenderSystem.h"
#include "ResourceManager.h"
#include "NaviMesh.h"
#include "Hero.h"
#include "ObjectManager.h"
#include "WorldManager.h"
#include "Npc.h"

#include "UIImage.h"
#include "FontAgent.h"
#include "InputSystem.h"
#include "CameraManager.h"
#include "UITransformImage.h"
#include "Icon.h"
#include "Label.h"
#include "PushButton.h"
#include "NpcScript.h"
#include "PartyManager.h"
#include "GameResourceManager.h"
#include "StageScript.h"

cMiniMapWindow::cMiniMapWindow()
: mpExplain(0)
, mpPush(0)
{
	SetLClickNotifyStage( true );

	mPartyInSkinName = "MiniMap_PartyIn";
	mPartyOutSkinName = "MiniMap_PartyOut";
}

cMiniMapWindow::~cMiniMapWindow()
{
	/// 이미지 데이터 삭제
	cImageDataList::cIterator i = mImageDataList.Begin();
	cImageDataList::cIterator end = mImageDataList.End();
	for( ; i != end; ++i )
	{
		sBaseImageData* p = (sBaseImageData*)(*i);
		if( p && p->mpImage )
		{
			if( p->mType == eTYPE_NPC )
			{
				sNpcImageData* data = (sNpcImageData*)p;
				SAFE_DELETE( data->mAniParam );
				SAFE_DELETE( data->mpAniImage );
			}

			SAFE_DELETE( p->mpImage );
			SAFE_DELETE( p );
		}
	}
	mImageDataList.Clear();

	///
	if( mHeroData )
	{
		SAFE_DELETE( mHeroData->mpImage );
		SAFE_DELETE( mHeroData );
	}

	if( mCameraData )
	{
		SAFE_DELETE( mCameraData->mpImage );
		SAFE_DELETE( mCameraData );
	}

	if( mCoverData )
	{
		SAFE_DELETE( mCoverData->mpImage );
		SAFE_DELETE( mCoverData );
	}

	if( mpMiniMapImage )
		SAFE_DELETE( mpMiniMapImage );
}

void cMiniMapWindow::OnHide()
{
	cUIWindow::OnHide();

	if( mpPush )
		mpPush->SetPress( false );

	if( mpExplain )
		mpExplain->Hide();
}

void cMiniMapWindow::UIStageIn()
{
	/// 화면 중앙에 출력
	int x = (int)(( GetScreenWidth() - GetAbsoluteRect().GetWidth() ) * 0.5f);
	int y = (int)(( GetScreenHeight() - GetAbsoluteRect().GetHeight() ) * 0.5f);
	SetRelativePos( cUIPos( x, y) );

	/// 현재 hero의 맵 번호 알아오기
	unsigned long folderIdx = GAMERESOURCEMAN->GetMapFolderIdx( HERO->GetMapNum() );

	/// 미니맵 로딩
	cString pathName;
	pathName.Format( "./Map/Map%02d/MapImage_%02d.tga", folderIdx, folderIdx );
	if( LoadMiniMap( pathName ) == false )
	{
		assert(0);
		return;
	}
}

void cMiniMapWindow::SetMapData()
{
	/// 채널번호 입력
	if( mpChannelNum )
		mpChannelNum->SetValue( HERO->GetChannelNum() );

	/// 맵이름
	mMapName = GAMERESOURCEMAN->GetMapName( HERO->GetMapNum() );

	tPointerArray<void*>* pMapArray = STAGESCRIPT->GetMapChangeStarterArr( (unsigned short)HERO->GetMapNum() );

	/// 맵포탈 정보 세팅
	if( pMapArray->IsEmpty() == false )
	{
		for( unsigned int i = 0; i < pMapArray->GetSize(); ++i )
		{
			sStageChangePos* p = (sStageChangePos*)(*pMapArray)[i];
			AddPortal( p->mPosIdx );
		}
	}
}


bool cMiniMapWindow::LoadMiniMap( const cString& pathName )
{
	NiTexture* ptex = RESOURCEMAN->LoadTexture( pathName, false );
	if( ptex == 0 )
	{
		assert( 0 && "failed to load texture" );
		return false;
	}

	/// 텍스쳐 설정
	mpMiniMapImage = new cUITransformImage;
	mpMiniMapImage->SetTexture( ptex );

	unsigned short x = (unsigned short)GetAbsoluteRect().mLeft;
	unsigned short y = (unsigned short)GetAbsoluteRect().mTop;

	mpMiniMapImage->SetTextureRect( 0, 0, mOriginTexW, mOriginTexH );
	mpMiniMapImage->SetScreenRect( cUIRect( x, y, x + GetAbsoluteRect().GetWidth(), y + GetAbsoluteRect().GetHeight() ) );

	mpMiniMapImage->SetAlpha( 0.7f );

	return true;
}

///
bool cMiniMapWindow::OnCreate( cUINodeProperty* pproperty )
{
	if( cUIWindow::OnCreate( pproperty ) == false )
		return false;

	mpExplain = (cLabel*)GetChild( eUIID_MINIMAP_EXPLAINIMAGE );
	mpPush = (cPushButton*)GetChild( eUIID_MINIMAP_EXPLAINPUSH );

	if( mpExplain )
		mpExplain->Hide();

	cUISkin* pSkin = UIMAN->GetSkin();
	if( !pSkin )
		return false;

	/// CAMERA 이미지 로드
	cUINodeSkin* pCameraSkin = pSkin->GetNodeSkin( "SmallMiniMap_Camera" );
	if( pCameraSkin  )
	{
		mCameraData = new sBaseImageData;
		if( !mCameraData )
			return false;

		mCameraData->mpImage = new cUITransformImage;
		mCameraData->mpImage->SetTexture( pCameraSkin->mpTexture );
		mCameraData->mType = eTYPE_CAMERA;

		unsigned short tx = (unsigned short)pCameraSkin->mSkinInfo->mTexX;
		unsigned short ty = (unsigned short)pCameraSkin->mSkinInfo->mTexY;
		unsigned short w = (unsigned short)pCameraSkin->mSkinInfo->mTexWidth;
		unsigned short h = (unsigned short)pCameraSkin->mSkinInfo->mTexHeight;

		mCameraData->mSize.mWidth = w;
		mCameraData->mSize.mHeight = h;

		mCameraData->mpImage->SetTextureRect( tx, ty, tx + w, ty + h );
		mCameraData->mpImage->SetScreenRect( cUIRect( mCameraData->mPos.mX, mCameraData->mPos.mY, 
													  mCameraData->mPos.mX + w, mCameraData->mPos.mY + h ) );
	}

	///// HERO 이미지 로드
	cUINodeSkin* pHeroSkin = pSkin->GetNodeSkin( "SmallMiniMap_Hero" );
	if( pHeroSkin  )
	{
		mHeroData = new sBaseImageData;
		if( !mHeroData )
			return false;

		mHeroData->mpImage = new cUITransformImage;
		mHeroData->mpImage->SetTexture( pHeroSkin->mpTexture );
		mHeroData->mType = eTYPE_HERO;

		unsigned short tx = (unsigned short)pHeroSkin->mSkinInfo->mTexX;
		unsigned short ty = (unsigned short)pHeroSkin->mSkinInfo->mTexY;
		unsigned short w = (unsigned short)pHeroSkin->mSkinInfo->mTexWidth;
		unsigned short h = (unsigned short)pHeroSkin->mSkinInfo->mTexHeight;

		mHeroData->mSize.mWidth = w;
		mHeroData->mSize.mHeight = h;

		mHeroData->mpImage->SetTextureRect( tx, ty, tx + w, ty + h );
		mHeroData->mpImage->SetScreenRect( cUIRect( mHeroData->mPos.mX, mHeroData->mPos.mY, 
			mHeroData->mPos.mX + w, mHeroData->mPos.mY + h ) );
	}

	/// 퀘스트 관련 이미지 
	{
		mpQuestNew = pSkin->GetNodeSkin( "SmallMiniMap_QuestNew" );
		if( mpQuestNew == 0 )
			return false;

		mpQuestPlay = pSkin->GetNodeSkin( "SmallMiniMap_QuestPlay" );
		if( mpQuestPlay == 0 )
			return false;

		mpQuestComplete = pSkin->GetNodeSkin( "SmallMiniMap_QuestComplete" );
		if( mpQuestComplete == 0 )
			return false;
	}

	if( mpMapName )
		mpMapName->SetTextColor( eCOLOR_WHITE );

	if( mpChannelNum )
		mpChannelNum->SetTextColor( eCOLOR_WHITE );

	return true;
}

void cMiniMapWindow::UpdateRect()
{
	cUIWindow::UpdateRect();

	if( mpMiniMapImage )
	{
		cUIRect rc = GetAbsoluteRect();
		mpMiniMapImage->SetScreenRect( rc );
	}

	mNeedUpdate = true;
}

void cMiniMapWindow::UpdateSkin()
{
	cUIWindow::UpdateSkin();
	
	/// 화면 중앙에 출력
	int x = (int)(( GetScreenWidth() - GetAbsoluteRect().GetWidth() ) * 0.5f);
	int y = (int)(( GetScreenHeight() - GetAbsoluteRect().GetHeight() ) * 0.5f);
	SetRelativePos( cUIPos( x, y) );

	mNeedUpdate = true;
}

///
void cMiniMapWindow::OnProcess( unsigned long deltaTime, unsigned long accumTime )
{
	if( mVisible == false )
		return;

	cUIWindow::OnProcess( deltaTime, accumTime );

	cHero* hero = OBJECTMANAGER->GetHero();
	cCamera* camera = CAMERAMAN->GetCurrent();

	cNaviMesh* navi = WORLDMAN->GetNaviMesh();
	if( !navi )
	{
		assert(0);
		return;
	}
	if( !(hero && camera && navi) )
		return;

	float segLen = navi->GetSegmentLength();


	if( !(mHeroData && mHeroData->mpImage) )
		return;

	if( !(mCameraData && mCameraData->mpImage) )
		return;

	/// 이동
	float x = hero->GetXPos();
	float y = hero->GetYPos();

	if( x != mOldX || y != mOldY || mNeedUpdate )
	{
		mOldX = x;
		mOldY = y;

		/// 텍스쳐 상의 캐릭터 위치 GetSegmentLength : 맵의 변 길이 
		int heroX = (int)(x / segLen * mOriginTexW);
		int heroY = (int)(mOriginTexH - (y / segLen * mOriginTexH ));

		mHeroX = (int)(x * 0.01);
		mHeroY = (int)(y * 0.01);

		mHeroData->mScreenPos.mX = (short)(heroX - (mHeroData->mSize.mWidth >> 1 ));
		mHeroData->mScreenPos.mY = (short)(heroY - (mHeroData->mSize.mHeight >> 1 ));
		mHeroData->mpImage->SetTranslate( (short)(GetAbsoluteRect().mLeft + mHeroData->mScreenPos.mX), 
										  (short)(GetAbsoluteRect().mTop + mHeroData->mScreenPos.mY) );

		short camX = (short)(heroX - (mCameraData->mSize.mWidth >> 1 ));
		short camY = (short)(heroY - (mCameraData->mSize.mHeight >> 1 ));
		mCameraData->mpImage->SetTranslate( (short)(GetAbsoluteRect().mLeft + camX), 
											(short)(GetAbsoluteRect().mTop + camY) );

		/// 이미지 갱신
		cImageDataList::cIterator i = mImageDataList.Begin();
		cImageDataList::cIterator end = mImageDataList.End();
		for( ; i != end; ++i )
		{
			sBaseImageData* p = (sBaseImageData*)(*i);
			if( p && p->mpImage )
			{
				int cx = (int)(p->mSize.mWidth >> 1 );
				int cy = (int)(p->mSize.mHeight >> 1 );

				if( p->mType == eTYPE_PARTY )
				{
					sUserImageData* data = (sUserImageData*)p;
					if( data->mMapNum != HERO->GetMapNum() )
						continue;
				}
			
				p->mScreenPos.mX = (int)p->mPos.mX - cx + GetAbsoluteRect().mLeft; 
				p->mScreenPos.mY = (int)p->mPos.mY - cy + GetAbsoluteRect().mTop;
				p->mpImage->SetPos( p->mScreenPos.mX, p->mScreenPos.mY );

				if( p->mType == eTYPE_NPC )
				{
					sNpcImageData* data =(sNpcImageData*)p;
					if( data->mpAniImage )
						data->mpAniImage->SetPos( p->mScreenPos.mX, p->mScreenPos.mY - 8 );
				}
			}
		}

		/// 갱신
		if( mHoverData )
			UpdateNamePos();

		/// 업데이트 완료 
		mNeedUpdate = false;
	}

	/// hero 회전
	NiPoint3 heroDir = hero->GetDirection();
	heroDir.z = 0.f;
	heroDir.Unitize();

	if( heroDir != mHeroDir )
	{
		mHeroDir = heroDir;

		float dot = heroDir.Dot( NiPoint3::UNIT_Y );
		float ang = NiACos( dot );

		if( heroDir.x > 0.f )
			ang = -ang;

		mHeroData->mpImage->SetRotate( ang );
	}	

	/// 카메라 회전
	NiPoint3  camDir = camera->GetWorldDirection();
	camDir.z = 0.f;
	camDir.Unitize();

	if( camDir != mCameraDir )
	{
		mCameraDir = camDir;

		float dot = camDir.Dot( NiPoint3::UNIT_Y );
		float ang = NiACos( dot );

		if( camDir.x > 0.f )
			ang = -ang;

		mCameraData->mpImage->SetRotate( ang );
	}

	/// 미니맵 노점상 표시 시간 체크
	if( mUserSellData && mUserSellData->mShow == true )
	{
		if( accumTime - mUserSellData->mStartTime > MAINTAIN_TIME )
		{
			RemoveUserSell( mUserSellData );
		}
	}

	/// 애니메이션 갱신
	cImageDataList::cIterator i = mImageDataList.Begin();
	cImageDataList::cIterator end = mImageDataList.End();
	for( ; i != end; ++i )
	{
		sBaseImageData* p = (sBaseImageData*)(*i);
		if( p && p->mType == eTYPE_NPC )
		{
			sNpcImageData* data = (sNpcImageData*)p;
			if( data->mQuestStatus != eNPCQUEST_NONE )
			{
				UpdateQuestAni( data, accumTime );
			}
		}
	}
}

void cMiniMapWindow::OnRender( cUIFontItemKeeper* pKeeper )
{
	/// 이미지 렌더링
	if( mpImage )
		mpImage->Draw();

	if( mpMiniMapImage )
		mpMiniMapImage->Draw();

	/// 이미지 렌더링
	{
		cImageDataList::cIterator i = mImageDataList.Begin();
		cImageDataList::cIterator end = mImageDataList.End();
		for( ; i != end; ++i )
		{
			sBaseImageData* p = (sBaseImageData*)(*i);
			if( p && p->mpImage && p->mShow )
			{
				p->mpImage->Draw();
	
				/// 퀘스트 상태에 따라 애니메이션 출력 결정
				if( p->mType == eTYPE_NPC )
				{
					sNpcImageData* data = (sNpcImageData*)p;
					if( data->mpAniImage && data->mQuestStatus != eNPCQUEST_NONE )
						data->mpAniImage->Draw();
				}
			}
		}
	}

	if( mUserSellData && mUserSellData->mShow == true )
		mUserSellData->mpImage->Draw();

	if( mCameraData )
		mCameraData->mpImage->Draw();

	if( mHeroData )
		mHeroData->mpImage->Draw();

	/// 자식 렌더링
	{
		cChildList::cIterator i = mChildList.Begin();
		cChildList::cIterator end = mChildList.End();

		for( ; i != end; ++i )
		{
			if( ((cUINode*)(*i))->IsVisible() == true )
				((cUINode*)(*i))->OnRender(pKeeper);
		}
	}

	/// 문자열 출력 
	cStringT str;
	str.Format( _T("%d, %d"), mHeroX, mHeroY );

	unsigned int posWidth = FONTAGENT->GetTextExtent( cFontAgent::eFont_System, (LPTSTR)str.Cstr(), str.GetLength() );
	pKeeper->AddFontItem( cFontAgent::eFont_System, (LPCTSTR)str.Cstr(), GetAbsoluteRect().mRight - posWidth - 20, GetAbsoluteRect().mTop + 53, eCOLOR_WHITE );

	/// 맵 정보 출력
	int textHeight = FONTAGENT->GetTextHeight( cFontAgent::eFont_System );
	unsigned int mapNameWidth = FONTAGENT->GetTextExtent( cFontAgent::eFont_System, (LPTSTR)mMapName.Cstr(), mMapName.GetLength() );
	unsigned int zoneNameWidth = FONTAGENT->GetTextExtent( cFontAgent::eFont_System, (LPTSTR)mZoneName.Cstr(), mZoneName.GetLength() );

	pKeeper->AddFontItem( cFontAgent::eFont_System, (LPCTSTR)mMapName.Cstr(), GetAbsoluteRect().mRight - mapNameWidth - 20, GetAbsoluteRect().mTop + 24, eCOLOR_WHITE );
	pKeeper->AddFontItem( cFontAgent::eFont_System, (LPCTSTR)mZoneName.Cstr(), GetAbsoluteRect().mRight - zoneNameWidth - 20, GetAbsoluteRect().mTop + 37, eCOLOR_WHITE );

	/// 이름 라벨 출력 
	if( mJobName )
		pKeeper->AddFontItem( cFontAgent::eFont_System, const_cast<LPTSTR>(mJobName.Cstr()), mJobPos.mX, mJobPos.mY, eCOLOR_WHITE );

	if( mName )
		pKeeper->AddFontItem( cFontAgent::eFont_System, const_cast<LPTSTR>(mName.Cstr()), mNamePos.mX, mNamePos.mY, eCOLOR_WHITE );

	///
	pKeeper->DrawAll();
}

void cMiniMapWindow::OnPushButtonPressed( cUINode* , unsigned int id )
{
	/// 설명버튼 누르면
	if( mpPush && mpExplain && id == eUIID_MINIMAP_EXPLAINPUSH )
	{
		if( mpPush->IsPress() == true )
			mpExplain->Show();
		else
			mpExplain->Hide();
	}
}

/// 이름 정보 위치 업데이트
void cMiniMapWindow::UpdateNamePos()
{
	if( mName.IsEmpty() )
		return;

	if( !(mHoverData && mHoverData->mpImage && mHoverData->mShow) )
		return;

	cUIRect rc;
	rc.mLeft = mHoverData->mScreenPos.mX;
	rc.mTop = mHoverData->mScreenPos.mY;
	rc.mRight = rc.mLeft + mHoverData->mSize.mWidth;
	rc.mBottom = rc.mTop + mHoverData->mSize.mHeight;

	/// 맵 이미지 좌표 비교후 보정
	int mapLeft = GetAbsoluteRect().mLeft;
	int mapTop = GetAbsoluteRect().mTop;

	int textHeight = FONTAGENT->GetTextHeight( cFontAgent::eFont_System );

	/// 직업이름이 있으면 좌표 세팅
	if( mJobName.IsEmpty() == false )
	{
		/// 직업좌표 세팅
		int textWidth = FONTAGENT->GetTextExtent( cFontAgent::eFont_System, mJobName.Cstr(), mJobName.GetLength() );

		mJobPos.mX = (int)(rc.mLeft + ( rc.GetWidth() - textWidth ) * 0.5);
		mJobPos.mY = rc.mTop - 10 - textHeight;

		/// 이름이 튀어나가지 않도록 보정
		if( mJobPos.mX <= mapLeft )
			mJobPos.mX = mapLeft;
		else if( mJobPos.mX + textWidth >= mapLeft + GetAbsoluteRect().GetWidth() )
			mJobPos.mX = mapLeft + GetAbsoluteRect().GetWidth() - textWidth;

		/// 너무 위에 찍히는것을 방지
		if( mJobPos.mY <= mapTop )
			mJobPos.mY = rc.mBottom + 2;
	}

	/// 이름좌표 세팅
	int textWidth = FONTAGENT->GetTextExtent( cFontAgent::eFont_System, mName.Cstr(), mName.GetLength() );

	mNamePos.mX = (int)(rc.mLeft + ( rc.GetWidth() - textWidth ) * 0.5);
	mNamePos.mY = rc.mTop - 10;

	/// 이름이 튀어나가지 않도록 보정
	if( mNamePos.mX <= mapLeft )
		mNamePos.mX = mapLeft;
	else if( mNamePos.mX + textWidth >= mapLeft + GetAbsoluteRect().GetWidth() )
		mNamePos.mX = mapLeft + GetAbsoluteRect().GetWidth() - textWidth;

	/// 너무 위에 찍히는 것을 방지
	if( mJobName.IsEmpty() == false )
		mNamePos.mY = mJobPos.mY + 2 + textHeight;
}
