/*************************************************************************
  Crytek Source File.
  Copyright (C), Crytek Studios, 2001-2004.
 -------------------------------------------------------------------------
  $Id$
  $DateTime$
  
 -------------------------------------------------------------------------
  History:
  - 30:8:2004   11:19 : Created by Márcio Martins

*************************************************************************/
#include "StdAfx.h"
#include "EditorGame.h"
#include "GameStartup.h"
#include "IActionMapManager.h"
#include "IActorSystem.h"
#include "IGameRulesSystem.h"
#include "INetwork.h"
#include "IAgent.h"
#include "ILevelSystem.h"
#include "IMovementController.h"
#include "IItemSystem.h"
#include "ItemParamReader.h"

#define EDITOR_SERVER_PORT 0xed17

ICVar * CEditorGame::s_pEditorGameMode;
CEditorGame * CEditorGame::s_pEditorGame = NULL;
struct IGameStartup;

//------------------------------------------------------------------------
CEditorGame::CEditorGame()
: m_pGame(0),
	m_pGameStartup(0)
{
	m_bEnabled = false;
	m_bGameMode = false;
	m_bPlayer = false;
	s_pEditorGame = this;
	s_pEditorGameMode = NULL;
}

void CEditorGame::ResetClient(IConsoleCmdArgs*)
{
	bool value = s_pEditorGame->m_bPlayer;
	s_pEditorGame->EnablePlayer(false);

	IEntityClass *pClass = GetISystem()->GetIEntitySystem()->GetClassRegistry()->FindClass("Player");
	if (pClass)	pClass->LoadScript(true);

	if (value)
		s_pEditorGame->m_pGame->GetIGameFramework()->GetIGameRulesSystem()->CreateGameRules("SinglePlayer");
	s_pEditorGame->EnablePlayer(value);
}

//------------------------------------------------------------------------
CEditorGame::~CEditorGame()
{
	s_pEditorGame = NULL;
}

//------------------------------------------------------------------------
void CEditorGame::OnChangeEditorMode( ICVar * pVar )
{
	assert( pVar == s_pEditorGameMode );
	if (s_pEditorGame)
	{
		s_pEditorGame->SetGameMode( s_pEditorGame->m_bGameMode );
	}
}

//------------------------------------------------------------------------

extern "C" 
{
	GAME_API IGameStartup *CreateGameStartup();
};

bool CEditorGame::Init(ISystem *pSystem,IGameToEditorInterface *pGameToEditorInterface)
{
	assert(pSystem);

	SSystemInitParams startupParams;
	memset(&startupParams, 0, sizeof(startupParams));
	startupParams.bEditor = true;
	startupParams.pSystem = pSystem;

	m_pGameStartup = CreateGameStartup();
	m_pGame = m_pGameStartup->Init(startupParams);

	if (!m_pGame)
	{
		return false;
	}

	InitUIEnums(pGameToEditorInterface);

	GetISystem()->GetIEntitySystem()->SetClientServer( true, true, false );
	GetISystem()->GetINetwork()->EnableLogging( false );

	s_pEditorGameMode = pSystem->GetIConsole()->RegisterInt( "net_gamemode", 0, 0, 
		"Should editor connect a new client?", OnChangeEditorMode );

	SetGameMode(false);

	GetISystem()->GetIConsole()->AddCommand( "net_reseteditorclient", ResetClient, 0, "Resets player and gamerules!" );

	ConfigureNetContext(true);

	return true;
}

//------------------------------------------------------------------------
int CEditorGame::Update(bool haveFocus, unsigned int updateFlags)
{
	return m_pGameStartup->Update(haveFocus, updateFlags);
}

//------------------------------------------------------------------------
void CEditorGame::Shutdown()
{
	EnablePlayer(false);
	SetGameMode(false);
	m_pGameStartup->Shutdown();
}

//------------------------------------------------------------------------
void CEditorGame::EnablePlayer(bool bPlayer)
{
	bool spawnPlayer = false;
	if (m_bPlayer != bPlayer)
	{
		spawnPlayer = m_bPlayer = bPlayer;
	}
	if (!SetGameMode( m_bGameMode ))
	{
		GameWarning("Failed setting game mode");
	}
	else if (m_bEnabled && spawnPlayer)
	{
		if (!m_pGame->GetIGameFramework()->BlockingSpawnPlayer())
			GameWarning("Failed spawning player");
	}
}

//------------------------------------------------------------------------
bool CEditorGame::SetGameMode(bool bGameMode)
{
	m_bGameMode = bGameMode;
	bool on = bGameMode;
	if (s_pEditorGameMode->GetIVal() == 0)
		on = m_bPlayer;
	bool ok = ConfigureNetContext( on );
	if (ok)
	{
		IGameFramework * pGameFramework = m_pGame->GetIGameFramework();

		pGameFramework->OnEditorSetGameMode(bGameMode);
	}
	else
	{
		GameWarning("Failed configuring net context");
	}
	return ok;
}

//------------------------------------------------------------------------
IEntity * CEditorGame::GetPlayer()
{
	IGameFramework * pGameFramework = m_pGame->GetIGameFramework();	

	if(!m_pGame)
		return 0;

	IActor * pActor = pGameFramework->GetClientActor();
	return pActor? pActor->GetEntity() : NULL;
}

//------------------------------------------------------------------------
void CEditorGame::SetPlayerPosAng(Vec3 pos,Vec3 viewDir)
{
	IActor * pClActor = m_pGame->GetIGameFramework()->GetClientActor();

	if (pClActor)
	{
		// pos coming from editor is a camera position, we must convert it into the player position by subtructing eye height.
		IEntity *myPlayer = pClActor->GetEntity();
		if (myPlayer)
		{
			pe_player_dimensions dim;
			dim.heightEye = 0;
			if (myPlayer->GetPhysics())
			{
				myPlayer->GetPhysics()->GetParams( &dim );
				pos.z = pos.z - dim.heightEye;
			}
		}

		pClActor->GetEntity()->SetPosRotScale( pos,Quat::CreateRotationVDir(-viewDir),Vec3(1,1,1) );
	}
}

//------------------------------------------------------------------------
void CEditorGame::HidePlayer(bool bHide)
{
	IEntity * pEntity = GetPlayer();
	if (pEntity)
		pEntity->Hide( bHide );
}

//------------------------------------------------------------------------
bool CEditorGame::ConfigureNetContext( bool on )
{
	bool ok = false;

	IGameFramework * pGameFramework = m_pGame->GetIGameFramework();

	if (on == m_bEnabled)
	{
		ok = true;
	}
	else if (on)
	{
		CryLogAlways( "EDITOR: Set game mode: on" );

		SGameContextParams ctx;

		SGameStartParams gameParams;
		gameParams.flags = eGSF_Server
			| eGSF_NoSpawnPlayer
			| eGSF_Client
			| eGSF_NoLevelLoading
			| eGSF_BlockingClientConnect
			| eGSF_NoGameRules
			| eGSF_LocalOnly
			| eGSF_NoQueries;
		gameParams.connectionString = "";
		gameParams.hostname = "localhost";
		gameParams.port = EDITOR_SERVER_PORT;
		gameParams.pContextParams = &ctx;
		gameParams.maxPlayers = 1;
		gameParams.inactivityTimeout = 0.0f; // no timeout

		if (pGameFramework->StartGameContext( &gameParams ))
			ok = true;
	}
	else
	{
		CryLogAlways( "EDITOR: Set game mode: off" );

		pGameFramework->EndGameContext();
		GetISystem()->GetINetwork()->Update(0.0f);
		ok = true;
	}

	m_bEnabled = on && ok;
	return ok;
}

//------------------------------------------------------------------------
void CEditorGame::OnBeforeLevelLoad()
{
	EnablePlayer(false);
	ConfigureNetContext(true);
	m_pGame->GetIGameFramework()->GetIGameRulesSystem()->CreateGameRules("SinglePlayer");
	m_pGame->GetIGameFramework()->GetILevelSystem()->OnLoadingStart(0);
}

//------------------------------------------------------------------------
void CEditorGame::OnAfterLevelLoad(const char *levelName, const char *levelFolder)
{
	m_pGame->GetIGameFramework()->SetEditorLevel(levelName, levelFolder);
	m_pGame->GetIGameFramework()->GetILevelSystem()->OnLoadingComplete(0);

	EnablePlayer(true);
}

//------------------------------------------------------------------------
IFlowSystem * CEditorGame::GetIFlowSystem()
{
	return m_pGame->GetIGameFramework()->GetIFlowSystem();
}

//------------------------------------------------------------------------
IGameTokenSystem* CEditorGame::GetIGameTokenSystem()
{
	return m_pGame->GetIGameFramework()->GetIGameTokenSystem();
}

//------------------------------------------------------------------------
void CEditorGame::InitUIEnums(IGameToEditorInterface* pGTE)
{
	InitItemUIEnums(pGTE);

	// Read in enums stored offline XML. Format is
	// <GlobalEnums>
	//   <EnumName>
	//     <entry enum="someName=someValue" />  <!-- displayed name != value -->
	// 	   <entry enum="someNameValue" />       <!-- displayed name == value -->
	//   </EnumName>
	// </GlobalEnums>
	//
	XmlNodeRef rootNode = GetISystem()->LoadXmlFile("Libs/UI/GlobalEnums.xml");
	if (!rootNode || !rootNode->getTag() || stricmp(rootNode->getTag(), "GlobalEnums") != 0)
	{
		// GameWarning("CEditorGame::InitUIEnums: File 'Libs/UI/GlobalEnums.xml' is not a GlobalEnums file");
		return;
	}
	for (int i = 0; i < rootNode->getChildCount(); ++i)
	{
		XmlNodeRef enumNameNode = rootNode->getChild(i);
		const char* enumId = enumNameNode->getTag();
		if (enumId == 0 || *enumId=='\0')
			continue;
		int maxChilds = enumNameNode->getChildCount();
		if (maxChilds > 0)
		{
			// allocate enough space to hold all strings
			const char** nameValueStrings = new const char*[maxChilds];
			int curEntryIndex = 0;
			for (int j = 0; j < maxChilds; ++j)
			{
				XmlNodeRef enumNode = enumNameNode->getChild(j);
				const char* nameValue = enumNode->getAttr("enum");
				if (nameValue != 0 && *nameValue!='\0')
				{
					// put in the nameValue pair
					nameValueStrings[curEntryIndex++] = nameValue;
				}
			}
			// if we found some entries inform CUIDataBase about it
			if (curEntryIndex > 0)
				pGTE->SetUIEnums(enumId, nameValueStrings, curEntryIndex);

			// be nice and free our array
			delete[] nameValueStrings;
		}
	}
}

//------------------------------------------------------------------------
void CEditorGame::InitItemUIEnums(IGameToEditorInterface* pGTE)
{
	// Get ItemParams from ItemSystem
	// Creates the following entries
	// "item"               All Item classes
	// "item_selectable"    All Item classes which can be selected
	// "item_givable",      All Item classes which can be given
	// "weapon"             All Weapon classes (an Item of class 'Weapon' or an Item which has ammo)
	// "weapon_selectable"  All Weapon classes which can be selected
	// "weapon_givable"     All Weapon classes which can be given
	// and for any weapon which has ammo
	// "ammo_WEAPONNAME"    All Ammos for this weapon

	IItemSystem* pItemSys = m_pGame->GetIGameFramework()->GetIItemSystem();
	int numAllItems = pItemSys->GetItemParamsCount();
	const char** allItemClasses = new const char*[numAllItems];
	const char** givableItemClasses = new const char*[numAllItems];
	const char** selectableItemClasses = new const char*[numAllItems];
	const char** allWeaponClasses = new const char*[numAllItems];
	const char** givableWeaponClasses = new const char*[numAllItems];
	const char** selectableWeaponClasses = new const char*[numAllItems];

	int numAllWeapons = 0;
	int numGivableItems = 0;
	int numSelectableItems = 0;
	int numSelectableWeapons = 0;
	int numGivableWeapons = 0;

	for (int i=0; i<numAllItems; ++i)
	{
		const char* itemName = pItemSys->GetItemParamName(i);
		allItemClasses[i] = itemName;

		const IItemParamsNode* pItemRootParams = pItemSys->GetItemParams(itemName);

		if (pItemRootParams)
		{
			bool givable = false;
			bool selectable = false;
			bool uiWeapon = false;
			const IItemParamsNode *pChildItemParams = pItemRootParams->GetChild("params");
			if (pChildItemParams) 
			{
				CItemParamReader reader (pChildItemParams);
				reader.Read("giveable", givable);
				reader.Read("selectable", selectable);
				reader.Read("ui_weapon", uiWeapon);
			}
			
			if (givable)
				givableItemClasses[numGivableItems++] = itemName;
			if (selectable)
				selectableItemClasses[numSelectableItems++] = itemName;

			const IItemParamsNode* pAmmos = pItemRootParams->GetChild("ammos");
			if (pAmmos)
			{
				int maxAmmos = pAmmos->GetChildCount();
				int numAmmos = 0;
				const char** ammoNames = new const char*[maxAmmos];
				for (int j=0; j<maxAmmos; ++j)
				{
					const IItemParamsNode* pAmmoParams = pAmmos->GetChild(j);
					if (stricmp(pAmmoParams->GetName(), "ammo") == 0)
					{
						const char* ammoName = pAmmoParams->GetAttribute("name");
						if (ammoName)
						{
							ammoNames[numAmmos] = ammoName;
							++numAmmos;
						}
					}
				}
				if (numAmmos > 0)
				{
					// make it a weapon when there's ammo
					allWeaponClasses[numAllWeapons++] = itemName;
					if (selectable)
						selectableWeaponClasses[numSelectableWeapons++] = itemName;
					if (givable)
						givableWeaponClasses[numGivableWeapons++] = itemName;

					string ammoEntryName = "ammo_";
					ammoEntryName+=itemName;
					pGTE->SetUIEnums(ammoEntryName.c_str(), ammoNames, numAmmos);
				}
				delete[] ammoNames;
			}
			else
			{
				const char* itemClass = pItemRootParams->GetAttribute("class");
				if (uiWeapon || (itemClass != 0 && stricmp(itemClass, "weapon") == 0))
				{
					// make it a weapon when there's ammo
					allWeaponClasses[numAllWeapons++] = itemName;
					if (selectable)
						selectableWeaponClasses[numSelectableWeapons++] = itemName;
					if (givable)
						givableWeaponClasses[numGivableWeapons++] = itemName;
				}
			}
		}
	}

	pGTE->SetUIEnums("weapon_selectable", selectableWeaponClasses, numSelectableWeapons);
	pGTE->SetUIEnums("weapon_givable", givableWeaponClasses, numGivableWeapons);
	pGTE->SetUIEnums("weapon", allWeaponClasses, numAllWeapons);
	pGTE->SetUIEnums("item_selectable", selectableItemClasses, numSelectableItems);
	pGTE->SetUIEnums("item_givable", givableItemClasses, numGivableItems);
	pGTE->SetUIEnums("item", allItemClasses, numAllItems);

	delete[] selectableWeaponClasses;
	delete[] givableWeaponClasses;
	delete[] allWeaponClasses;
	delete[] selectableItemClasses;
	delete[] givableItemClasses; 
	delete[] allItemClasses;
}