// ==============================================================
//	This file is part of Glest (www.glest.org)
//
//	Copyright (C) 2001-2008 Martiño Figueroa
//
//	You can redistribute this code and/or modify it under
//	the terms of the GNU General Public License as published
//	by the Free Software Foundation; either version 2 of the
//	License, or (at your option) any later version
// ==============================================================

#include "commander.h"

#include "world.h"
#include "unit.h"
#include "conversion.h"
#include "upgrade.h"
#include "command.h"
#include "command_type.h"
#include "network_manager.h"
#include "console.h"
#include "config.h"
#include "platform_util.h"
#include "game.h"
#include "game_settings.h"
#include "game.h"

using namespace Shared::Graphics;
using namespace Shared::Util;
using namespace Shared::Platform;

namespace Glest{ namespace Game{

// =====================================================
// 	class Commander
// =====================================================
Commander::Commander() {
	this->world					= NULL;
	this->pauseNetworkCommands 	= false;
}

Commander::~Commander() {
}

void Commander::init(World *world){
	this->world= world;
}

bool Commander::canSubmitCommandType(const Unit *unit, const CommandType *commandType) const {
	bool canSubmitCommand=true;
	const MorphCommandType *mct = dynamic_cast<const MorphCommandType*>(commandType);
	if(mct && unit->getCommandSize() > 0) {
		Command *cur_command= unit->getCurrCommand();
		if(cur_command != NULL) {
			const MorphCommandType *cur_mct= dynamic_cast<const MorphCommandType*>(cur_command->getCommandType());
			if(cur_mct && unit->getCurrSkill() && unit->getCurrSkill()->getClass() == scMorph) {
				const UnitType *morphUnitType		= mct->getMorphUnit();
				const UnitType *cur_morphUnitType	= cur_mct->getMorphUnit();

				if(morphUnitType != NULL && cur_morphUnitType != NULL && morphUnitType->getId() == cur_morphUnitType->getId()) {
					canSubmitCommand = false;
				}
			}
		}
	}
	return canSubmitCommand;
}

std::pair<CommandResult,string> Commander::tryGiveCommand(const Selection *selection, const CommandType *commandType,
									const Vec2i &pos, const UnitType* unitType,
									CardinalDir facing, bool tryQueue,Unit *targetUnit) const {
	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	if(this->pauseNetworkCommands == true) {
		return std::pair<CommandResult,string>(crFailUndefined,"");
	}

	std::pair<CommandResult,string> result(crFailUndefined,"");
	if(selection->isEmpty() == false && commandType != NULL) {
		Vec2i refPos;
		CommandResultContainer results;

		refPos = world->getMap()->computeRefPos(selection);

		const Unit *builderUnit = world->getMap()->findClosestUnitToPos(selection, pos, unitType);

		int builderUnitId 					= builderUnit->getId();
		CommandStateType commandStateType 	= cst_None;
		int commandStateValue 				= -1;

		int unitCommandGroupId = -1;
		if(selection->getCount() > 1) {
			unitCommandGroupId = world->getNextCommandGroupId();
		}

		//give orders to all selected units
		for(int i = 0; i < selection->getCount(); ++i) {
			const Unit *unit = selection->getUnit(i);


			std::pair<CommandResult,string> resultCur(crFailUndefined,"");
			bool canSubmitCommand = canSubmitCommandType(unit, commandType);
			if(canSubmitCommand == true) {
				int unitId= unit->getId();
				Vec2i currPos= world->getMap()->computeDestPos(refPos, unit->getPosNotThreadSafe(), pos);

				Vec2i usePos = currPos;
				const CommandType *useCommandtype = commandType;
				if(dynamic_cast<const BuildCommandType *>(commandType) != NULL) {
					usePos = pos;
					if(builderUnit->getId() != unitId) {
						useCommandtype 		= unit->getType()->getFirstRepairCommand(unitType);
						commandStateType 	= cst_linkedUnit;
						commandStateValue 	= builderUnitId;
					}
					else {
						commandStateType 	= cst_None;
						commandStateValue 	= -1;
					}
				}

				if(useCommandtype != NULL) {
					NetworkCommand networkCommand(this->world,nctGiveCommand, unitId,
							useCommandtype->getId(), usePos, unitType->getId(),
							(targetUnit != NULL ? targetUnit->getId() : -1),
							facing, tryQueue, commandStateType,commandStateValue,
							unitCommandGroupId);

					//every unit is ordered to a the position
					resultCur= pushNetworkCommand(&networkCommand);
				}
			}

			results.push_back(resultCur);
		}

		return computeResult(results);
	}
	return std::pair<CommandResult,string>(crFailUndefined,"");
}

std::pair<CommandResult,string> Commander::tryGiveCommand(const Unit* unit, const CommandType *commandType,
									const Vec2i &pos, const UnitType* unitType,
									CardinalDir facing, bool tryQueue,Unit *targetUnit,
									int unitGroupCommandId) const {
	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	if(this->pauseNetworkCommands == true) {
		return std::pair<CommandResult,string>(crFailUndefined,"");
	}

	Chrono chrono;
	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start();

	assert(this->world != NULL);
	assert(unit != NULL);
	assert(commandType != NULL);
	assert(unitType != NULL);

	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,chrono.getMillis());

	std::pair<CommandResult,string> result(crFailUndefined,"");
	bool canSubmitCommand=canSubmitCommandType(unit, commandType);
	if(canSubmitCommand == true) {
		NetworkCommand networkCommand(this->world,nctGiveCommand, unit->getId(),
									commandType->getId(), pos, unitType->getId(),
									(targetUnit != NULL ? targetUnit->getId() : -1),
									facing, tryQueue,cst_None,-1,unitGroupCommandId);

		if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,chrono.getMillis());

		result = pushNetworkCommand(&networkCommand);
	}

	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s] Line: %d took msecs: %lld\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,chrono.getMillis());

	return result;
}

std::pair<CommandResult,string> Commander::tryGiveCommand(const Selection *selection, CommandClass commandClass,
		const Vec2i &pos, const Unit *targetUnit, bool tryQueue) const{

	if(this->pauseNetworkCommands == true) {
		return std::pair<CommandResult,string>(crFailUndefined,"");
	}

	std::pair<CommandResult,string> result(crFailUndefined,"");
	if(selection->isEmpty() == false) {
		Vec2i refPos, currPos;
		CommandResultContainer results;

		refPos= world->getMap()->computeRefPos(selection);

		int unitCommandGroupId = -1;
		if(selection->getCount() > 1) {
			unitCommandGroupId = world->getNextCommandGroupId();
		}

		//give orders to all selected units
		for(int i = 0; i < selection->getCount(); ++i) {
			const Unit *unit= selection->getUnit(i);
			const CommandType *ct= unit->getType()->getFirstCtOfClass(commandClass);
			if(ct != NULL) {
				std::pair<CommandResult,string> resultCur(crFailUndefined,"");

				bool canSubmitCommand=canSubmitCommandType(unit, ct);
				if(canSubmitCommand == true) {

					int targetId= targetUnit==NULL? Unit::invalidId: targetUnit->getId();
					int unitId= selection->getUnit(i)->getId();
					Vec2i currPos= world->getMap()->computeDestPos(refPos,
							selection->getUnit(i)->getPosNotThreadSafe(), pos);
					NetworkCommand networkCommand(this->world,nctGiveCommand,
							unitId, ct->getId(), currPos, -1, targetId, -1,
							tryQueue,cst_None,-1,unitCommandGroupId);

					//every unit is ordered to a different pos
					resultCur= pushNetworkCommand(&networkCommand);
				}
				results.push_back(resultCur);
			}
			else{
				results.push_back(std::pair<CommandResult,string>(crFailUndefined,""));
			}
		}
		return computeResult(results);
	}
	else {
		return std::pair<CommandResult,string>(crFailUndefined,"");
	}
}

std::pair<CommandResult,string> Commander::tryGiveCommand(const Selection *selection,
						const CommandType *commandType, const Vec2i &pos,
						const Unit *targetUnit, bool tryQueue) const {

	if(this->pauseNetworkCommands == true) {
		return std::pair<CommandResult,string>(crFailUndefined,"");
	}

	std::pair<CommandResult,string> result(crFailUndefined,"");

	if(!selection->isEmpty() && commandType!=NULL){
		Vec2i refPos;
		CommandResultContainer results;

		refPos= world->getMap()->computeRefPos(selection);

		int unitCommandGroupId = -1;
		if(selection->getCount() > 1) {
			unitCommandGroupId = world->getNextCommandGroupId();
		}

		//give orders to all selected units
		for(int i = 0; i < selection->getCount(); ++i) {
			const Unit *unit = selection->getUnit(i);
			assert(unit != NULL);

			std::pair<CommandResult,string> resultCur(crFailUndefined,"");

			bool canSubmitCommand=canSubmitCommandType(unit, commandType);
			if(canSubmitCommand == true) {
				int targetId= targetUnit==NULL? Unit::invalidId: targetUnit->getId();
				int unitId= unit->getId();
				Vec2i currPos= world->getMap()->computeDestPos(refPos, unit->getPosNotThreadSafe(), pos);
				NetworkCommand networkCommand(this->world,nctGiveCommand, unitId,
						commandType->getId(), currPos, -1, targetId, -1, tryQueue,
						cst_None, -1, unitCommandGroupId);

				//every unit is ordered to a different position
				resultCur= pushNetworkCommand(&networkCommand);
			}
			results.push_back(resultCur);
		}

		return computeResult(results);
	}
	else{
		return std::pair<CommandResult,string>(crFailUndefined,"");
	}
}

//auto command
std::pair<CommandResult,string> Commander::tryGiveCommand(const Selection *selection, const Vec2i &pos,
		const Unit *targetUnit, bool tryQueue, int unitCommandGroupId) const {

	if(this->pauseNetworkCommands == true) {
		return std::pair<CommandResult,string>(crFailUndefined,"");
	}

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	std::pair<CommandResult,string> result(crFailUndefined,"");

	if(selection->isEmpty() == false){
		Vec2i refPos, currPos;
		CommandResultContainer results;

		if(unitCommandGroupId == -1 && selection->getCount() > 1) {
			unitCommandGroupId = world->getNextCommandGroupId();
		}

		//give orders to all selected units
		refPos= world->getMap()->computeRefPos(selection);
		for(int i=0; i < selection->getCount(); ++i) {
			//every unit is ordered to a different pos
			const Unit *unit = selection->getUnit(i);
			assert(unit != NULL);

			currPos= world->getMap()->computeDestPos(refPos, unit->getPosNotThreadSafe(), pos);

			//get command type
			const CommandType *commandType= unit->computeCommandType(pos, targetUnit);

			//give commands
			if(commandType != NULL) {
				int targetId= targetUnit==NULL? Unit::invalidId: targetUnit->getId();
				int unitId= unit->getId();

				std::pair<CommandResult,string> resultCur(crFailUndefined,"");

				bool canSubmitCommand=canSubmitCommandType(unit, commandType);
				if(canSubmitCommand == true) {
					NetworkCommand networkCommand(this->world,nctGiveCommand,
							unitId, commandType->getId(), currPos, -1, targetId,
							-1, tryQueue, cst_None, -1, unitCommandGroupId);
					resultCur= pushNetworkCommand(&networkCommand);
				}
				results.push_back(resultCur);
			}
			else if(unit->isMeetingPointSettable() == true) {
				NetworkCommand command(this->world,nctSetMeetingPoint,
						unit->getId(), -1, currPos,-1,-1,-1,false,
						cst_None,-1,unitCommandGroupId);

				std::pair<CommandResult,string> resultCur= pushNetworkCommand(&command);
				results.push_back(resultCur);
			}
			else {
				results.push_back(std::pair<CommandResult,string>(crFailUndefined,""));
			}
		}
		result = computeResult(results);
	}

	return result;
}

CommandResult Commander::tryCancelCommand(const Selection *selection) const {
	if(this->pauseNetworkCommands == true) {
		return crFailUndefined;
	}

	int unitCommandGroupId = -1;
	if(selection->getCount() > 1) {
		unitCommandGroupId = world->getNextCommandGroupId();
	}

	for(int i = 0; i < selection->getCount(); ++i) {
		NetworkCommand command(this->world,nctCancelCommand,
				selection->getUnit(i)->getId(),-1,Vec2i(0),-1,-1,-1,false,
				cst_None,-1,unitCommandGroupId);
		pushNetworkCommand(&command);
	}

	return crSuccess;
}

void Commander::trySetMeetingPoint(const Unit* unit, const Vec2i &pos) const {
	if(this->pauseNetworkCommands == true) {
		return;
	}

	NetworkCommand command(this->world,nctSetMeetingPoint, unit->getId(), -1, pos);
	pushNetworkCommand(&command);
}

void Commander::trySwitchTeam(const Faction* faction, int teamIndex) const {
	if(this->pauseNetworkCommands == true) {
		return;
	}

	NetworkCommand command(this->world,nctSwitchTeam, faction->getIndex(), teamIndex);
	pushNetworkCommand(&command);
}

void Commander::trySwitchTeamVote(const Faction* faction, SwitchTeamVote *vote) const {
	if(this->pauseNetworkCommands == true) {
		return;
	}

	NetworkCommand command(this->world,nctSwitchTeamVote, faction->getIndex(), vote->factionIndex,Vec2i(0),vote->allowSwitchTeam);
	pushNetworkCommand(&command);
}

void Commander::tryDisconnectNetworkPlayer(const Faction* faction, int playerIndex) const {
	NetworkCommand command(this->world,nctDisconnectNetworkPlayer, faction->getIndex(), playerIndex);
	pushNetworkCommand(&command);
}

void Commander::tryPauseGame(bool joinNetworkGame, bool clearCaches) const {
	NetworkCommand command(this->world,nctPauseResume,1);
	command.commandTypeId = (clearCaches == true ? 1 : 0);
	command.unitTypeId = (joinNetworkGame == true ? 1 : 0);
	pushNetworkCommand(&command);
}

void Commander::tryResumeGame(bool joinNetworkGame, bool clearCaches) const {
	NetworkCommand command(this->world,nctPauseResume,0);
	command.commandTypeId = (clearCaches == true ? 1 : 0);
	command.unitTypeId = (joinNetworkGame == true ? 1 : 0);
	pushNetworkCommand(&command);
}

void Commander::tryNetworkPlayerDisconnected(int factionIndex) const {
	//printf("tryNetworkPlayerDisconnected factionIndex: %d\n",factionIndex);

	//if(this->pauseNetworkCommands == true) {
	//	return;
	//}

	NetworkCommand command(this->world,nctPlayerStatusChange, factionIndex, npst_Disconnected);
	pushNetworkCommand(&command);
}

// ==================== PRIVATE ====================

std::pair<CommandResult,string> Commander::computeResult(const CommandResultContainer &results) const {
	std::pair<CommandResult,string> result(crFailUndefined,"");
	switch(results.size()) {
        case 0:
            return std::pair<CommandResult,string>(crFailUndefined,"");
        case 1:
            return results.front();
        default:
            for(int i = 0; i < (int)results.size(); ++i) {
                if(results[i].first != crSuccess) {
                	return std::pair<CommandResult,string>(crSomeFailed,results[i].second);
                }
            }
            break;
	}
	return std::pair<CommandResult,string>(crSuccess,"");
}

std::pair<CommandResult,string> Commander::pushNetworkCommand(const NetworkCommand* networkCommand) const {
	GameNetworkInterface *gameNetworkInterface= NetworkManager::getInstance().getGameNetworkInterface();
	std::pair<CommandResult,string> result(crSuccess,"");

	//validate unit
	const Unit* unit = NULL;
	if( networkCommand->getNetworkCommandType() != nctSwitchTeam &&
		networkCommand->getNetworkCommandType() != nctSwitchTeamVote &&
		networkCommand->getNetworkCommandType() != nctPauseResume &&
		networkCommand->getNetworkCommandType() != nctPlayerStatusChange &&
		networkCommand->getNetworkCommandType() != nctDisconnectNetworkPlayer) {
		unit= world->findUnitById(networkCommand->getUnitId());
		if(unit == NULL) {
			char szBuf[8096]="";
			snprintf(szBuf,8096,"In [%s::%s - %d] Command refers to non existent unit id = %d. Game out of synch.",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,networkCommand->getUnitId());
			GameNetworkInterface *gameNetworkInterface= NetworkManager::getInstance().getGameNetworkInterface();
			if(gameNetworkInterface != NULL) {
				char szMsg[8096]="";
				snprintf(szMsg,8096,"Player detected an error: Command refers to non existent unit id = %d. Game out of synch.",networkCommand->getUnitId());
				gameNetworkInterface->sendTextMessage(szMsg,-1, true, "");
			}
			throw megaglest_runtime_error(szBuf);
		}
	}

	//add the command to the interface
	gameNetworkInterface->requestCommand(networkCommand);

	//calculate the result of the command
	if(unit != NULL && networkCommand->getNetworkCommandType() == nctGiveCommand) {
		//printf("In [%s::%s Line: %d] result.first = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,result.first);
		Command* command= buildCommand(networkCommand);
		result= unit->checkCommand(command);
		delete command;
	}
	return result;
}

void Commander::signalNetworkUpdate(Game *game) {
    updateNetwork(game);
}

bool Commander::getReplayCommandListForFrame(int worldFrameCount) {
	bool haveReplyCommands = false;
	if(replayCommandList.empty() == false) {
		if(SystemFlags::VERBOSE_MODE_ENABLED) printf("worldFrameCount = %d replayCommandList.size() = " MG_SIZE_T_SPECIFIER "\n",worldFrameCount,replayCommandList.size());

		std::vector<NetworkCommand> replayList;
		for(unsigned int i = 0; i < replayCommandList.size(); ++i) {
			std::pair<int,NetworkCommand> &cmd = replayCommandList[i];
			if(cmd.first <= worldFrameCount) {
				replayList.push_back(cmd.second);
				haveReplyCommands = true;
			}
		}
		if(haveReplyCommands == true) {
			replayCommandList.erase(replayCommandList.begin(),replayCommandList.begin() + replayList.size());

			if(SystemFlags::VERBOSE_MODE_ENABLED) printf("worldFrameCount = %d GIVING COMMANDS replayList.size() = " MG_SIZE_T_SPECIFIER "\n",worldFrameCount,replayList.size());
			for(int i= 0; i < (int)replayList.size(); ++i){
				giveNetworkCommand(&replayList[i]);
			}
			GameNetworkInterface *gameNetworkInterface= NetworkManager::getInstance().getGameNetworkInterface();
			gameNetworkInterface->setKeyframe(worldFrameCount);
		}
	}
	return haveReplyCommands;
}

bool Commander::hasReplayCommandListForFrame() const {
	return (replayCommandList.empty() == false);
}

int Commander::getReplayCommandListForFrameCount() const {
	return (int)replayCommandList.size();
}

void Commander::updateNetwork(Game *game) {
	if(world == NULL) {
		return;
	}
	NetworkManager &networkManager= NetworkManager::getInstance();

	//check that this is a keyframe
	if(game != NULL) {
        GameSettings *gameSettings = game->getGameSettings();
        if( networkManager.isNetworkGame() == false ||
            (world->getFrameCount() % gameSettings->getNetworkFramePeriod()) == 0) {

        	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] networkManager.isNetworkGame() = %d,world->getFrameCount() = %d, gameSettings->getNetworkFramePeriod() = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,networkManager.isNetworkGame(),world->getFrameCount(),gameSettings->getNetworkFramePeriod());

        	if(getReplayCommandListForFrame(world->getFrameCount()) == false) {
				GameNetworkInterface *gameNetworkInterface= NetworkManager::getInstance().getGameNetworkInterface();

				if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) perfTimer.start();
				//update the keyframe
				gameNetworkInterface->updateKeyframe(world->getFrameCount());
				if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && perfTimer.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] gameNetworkInterface->updateKeyframe for %d took %lld msecs\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,world->getFrameCount(),perfTimer.getMillis());

				if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) perfTimer.start();
				//give pending commands
				if(SystemFlags::VERBOSE_MODE_ENABLED) printf("START process: %d network commands in frame: %d\n",gameNetworkInterface->getPendingCommandCount(),this->world->getFrameCount());
				for(int i= 0; i < gameNetworkInterface->getPendingCommandCount(); ++i){
					giveNetworkCommand(gameNetworkInterface->getPendingCommand(i));
				}
				if(SystemFlags::VERBOSE_MODE_ENABLED) printf("END process: %d network commands in frame: %d\n",gameNetworkInterface->getPendingCommandCount(),this->world->getFrameCount());
				if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && perfTimer.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] giveNetworkCommand took %lld msecs, PendingCommandCount = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,perfTimer.getMillis(),gameNetworkInterface->getPendingCommandCount());
				gameNetworkInterface->clearPendingCommands();
				if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Cleared network commands in frame: %d\n",this->world->getFrameCount());
        	}
        }
	}
}

void Commander::addToReplayCommandList(NetworkCommand &command,int worldFrameCount) {
	replayCommandList.push_back(make_pair(worldFrameCount,command));
}

void Commander::giveNetworkCommand(NetworkCommand* networkCommand) const {
	Chrono chrono;
	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start();

	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,chrono.getMillis());


	world->getGame()->addNetworkCommandToReplayList(networkCommand,world->getFrameCount());

    networkCommand->preprocessNetworkCommand(this->world);

    if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [after networkCommand->preprocessNetworkCommand]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,chrono.getMillis());

    bool commandWasHandled = false;
    // Handle special commands first (that just use network command members as placeholders)
    switch(networkCommand->getNetworkCommandType()) {
    	case nctSwitchTeam: {
        	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctSwitchTeam\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

        	commandWasHandled = true;
        	int factionIndex = networkCommand->getUnitId();
        	int newTeam = networkCommand->getCommandTypeId();

        	// Auto join empty team or ask players to join
        	bool autoJoinTeam = true;
        	for(int i = 0; i < world->getFactionCount(); ++i) {
        		if(newTeam == world->getFaction(i)->getTeam()) {
        			autoJoinTeam = false;
        			break;
        		}
        	}

        	if(autoJoinTeam == true) {
        		Faction *faction = world->getFaction(factionIndex);
        		int oldTeam = faction->getTeam();
        		faction->setTeam(newTeam);
        		GameSettings *settings = world->getGameSettingsPtr();
        		settings->setTeam(factionIndex,newTeam);
        		world->getStats()->setTeam(factionIndex, newTeam);

        		if(factionIndex == world->getThisFactionIndex()) {
        			world->setThisTeamIndex(newTeam);
					GameNetworkInterface *gameNetworkInterface= NetworkManager::getInstance().getGameNetworkInterface();
					if(gameNetworkInterface != NULL) {

	    		    	Lang &lang= Lang::getInstance();
	    		    	const vector<string> languageList = settings->getUniqueNetworkPlayerLanguages();
	    		    	for(unsigned int i = 0; i < languageList.size(); ++i) {
							char szMsg[8096]="";
							if(lang.hasString("PlayerSwitchedTeam",languageList[i]) == true) {
								snprintf(szMsg,8096,lang.getString("PlayerSwitchedTeam",languageList[i]).c_str(),settings->getNetworkPlayerName(factionIndex).c_str(),oldTeam,newTeam);
							}
							else {
								snprintf(szMsg,8096,"Player %s switched from team# %d to team# %d.",settings->getNetworkPlayerName(factionIndex).c_str(),oldTeam,newTeam);
							}
							bool localEcho = lang.isLanguageLocal(languageList[i]);
							gameNetworkInterface->sendTextMessage(szMsg,-1, localEcho,languageList[i]);
	    		    	}
					}
					world->getGame()->reInitGUI();
        		}
        	}
        	else {
        		for(int i = 0; i < world->getFactionCount(); ++i) {
					if(newTeam == world->getFaction(i)->getTeam()) {
						Faction *faction = world->getFaction(factionIndex);

						SwitchTeamVote vote;
						vote.factionIndex = factionIndex;
						vote.allowSwitchTeam = false;
						vote.oldTeam = faction->getTeam();
						vote.newTeam = newTeam;
						vote.voted = false;

						world->getFaction(i)->setSwitchTeamVote(vote);
					}
        		}
        	}

            if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [after unit->setMeetingPos]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,chrono.getMillis());

            if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctSetMeetingPoint\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
        }
            break;

        case nctSwitchTeamVote: {
        	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctSwitchTeamVote\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

        	commandWasHandled = true;

        	int votingFactionIndex = networkCommand->getUnitId();
        	int factionIndex = networkCommand->getCommandTypeId();
        	bool allowSwitchTeam = networkCommand->getUnitTypeId() != 0;

        	Faction *faction = world->getFaction(votingFactionIndex);

			SwitchTeamVote *vote = faction->getSwitchTeamVote(factionIndex);
			if(vote == NULL) {
				throw megaglest_runtime_error("vote == NULL");
			}
			vote->voted = true;
			vote->allowSwitchTeam = allowSwitchTeam;

        	// Join the new team if > 50 % said yes
			int newTeamTotalMemberCount=0;
			int newTeamVotedYes=0;
			int newTeamVotedNo=0;

        	for(int i = 0; i < world->getFactionCount(); ++i) {
        		if(vote->newTeam == world->getFaction(i)->getTeam()) {
        			newTeamTotalMemberCount++;

        			SwitchTeamVote *teamVote = world->getFaction(i)->getSwitchTeamVote(factionIndex);
        			if(teamVote != NULL && teamVote->voted == true) {
        				if(teamVote->allowSwitchTeam == true) {
        					newTeamVotedYes++;
        				}
        				else {
        					newTeamVotedNo++;
        				}
        			}
        		}
        	}

        	// If > 50% of team vote yes, switch th eplayers team
        	if(newTeamTotalMemberCount > 0 && newTeamVotedYes > 0 &&
        			static_cast<float>(newTeamVotedYes) / static_cast<float>(newTeamTotalMemberCount) > 0.5) {
        		Faction *faction = world->getFaction(factionIndex);
        		int oldTeam = faction->getTeam();
        		faction->setTeam(vote->newTeam);
        		GameSettings *settings = world->getGameSettingsPtr();
        		settings->setTeam(factionIndex,vote->newTeam);
        		world->getStats()->setTeam(factionIndex, vote->newTeam);

        		if(factionIndex == world->getThisFactionIndex()) {
        			world->setThisTeamIndex(vote->newTeam);
					GameNetworkInterface *gameNetworkInterface= NetworkManager::getInstance().getGameNetworkInterface();
					if(gameNetworkInterface != NULL) {

	    		    	Lang &lang= Lang::getInstance();
	    		    	const vector<string> languageList = settings->getUniqueNetworkPlayerLanguages();
	    		    	for(unsigned int i = 0; i < languageList.size(); ++i) {
							char szMsg[8096]="";
							if(lang.hasString("PlayerSwitchedTeam",languageList[i]) == true) {
								snprintf(szMsg,8096,lang.getString("PlayerSwitchedTeam",languageList[i]).c_str(),settings->getNetworkPlayerName(factionIndex).c_str(),oldTeam,vote->newTeam);
							}
							else {
								snprintf(szMsg,8096,"Player %s switched from team# %d to team# %d.",settings->getNetworkPlayerName(factionIndex).c_str(),oldTeam,vote->newTeam);
							}
							bool localEcho = lang.isLanguageLocal(languageList[i]);
							gameNetworkInterface->sendTextMessage(szMsg,-1, localEcho,languageList[i]);
	    		    	}
					}
					world->getGame()->reInitGUI();
        		}
        	}
        	else if(newTeamTotalMemberCount == (newTeamVotedYes + newTeamVotedNo)) {
        		if(factionIndex == world->getThisFactionIndex()) {
        			GameSettings *settings = world->getGameSettingsPtr();
            		Faction *faction = world->getFaction(factionIndex);
            		int oldTeam = faction->getTeam();

					GameNetworkInterface *gameNetworkInterface= NetworkManager::getInstance().getGameNetworkInterface();
					if(gameNetworkInterface != NULL) {

	    		    	Lang &lang= Lang::getInstance();
	    		    	const vector<string> languageList = settings->getUniqueNetworkPlayerLanguages();
	    		    	for(unsigned int i = 0; i < languageList.size(); ++i) {
							char szMsg[8096]="";
							if(lang.hasString("PlayerSwitchedTeamDenied",languageList[i]) == true) {
								snprintf(szMsg,8096,lang.getString("PlayerSwitchedTeamDenied",languageList[i]).c_str(),settings->getNetworkPlayerName(factionIndex).c_str(),oldTeam,vote->newTeam);
							}
							else {
								snprintf(szMsg,8096,"Player %s was denied the request to switch from team# %d to team# %d.",settings->getNetworkPlayerName(factionIndex).c_str(),oldTeam,vote->newTeam);
							}
							bool localEcho = lang.isLanguageLocal(languageList[i]);
							gameNetworkInterface->sendTextMessage(szMsg,-1, localEcho,languageList[i]);
	    		    	}
					}
        		}
        	}

            if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [after unit->setMeetingPos]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,chrono.getMillis());

            if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctSetMeetingPoint\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
        }
            break;

    	case nctDisconnectNetworkPlayer: {
        	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctDisconnectNetworkPlayer\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

        	commandWasHandled = true;

        	NetworkManager &networkManager= NetworkManager::getInstance();
        	NetworkRole role = networkManager.getNetworkRole();
        	//GameSettings *settings = world->getGameSettingsPtr();

        	if(role == nrServer) {
				//int factionIndex = networkCommand->getUnitId();
				int playerIndex = networkCommand->getCommandTypeId();

				GameNetworkInterface *gameNetworkInterface= NetworkManager::getInstance().getGameNetworkInterface();
				if(gameNetworkInterface != NULL) {
					ServerInterface *server = networkManager.getServerInterface();
					if(server != NULL && server->isClientConnected(playerIndex) == true) {

						MutexSafeWrapper safeMutex(server->getSlotMutex(playerIndex),CODE_AT_LINE);
						ConnectionSlot *slot = server->getSlot(playerIndex,false);
						if(slot != NULL) {
							safeMutex.ReleaseLock();
							NetworkMessageQuit networkMessageQuit;
							slot->sendMessage(&networkMessageQuit);
							sleep(5);

							//printf("Sending nctDisconnectNetworkPlayer\n");
							server = networkManager.getServerInterface(false);
							if(server != NULL) {
								MutexSafeWrapper safeMutex2(server->getSlotMutex(playerIndex),CODE_AT_LINE);
								slot = server->getSlot(playerIndex,false);
								if(slot != NULL) {
									safeMutex2.ReleaseLock();
									slot->close();
								}
							}
						}
					}
				}
        	}
            if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctDisconnectNetworkPlayer\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
        }
            break;

        case nctPauseResume:
        	{
        	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctPauseResume\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

        	commandWasHandled = true;

        	bool pauseGame 			= networkCommand->getUnitId() != 0;
        	bool clearCaches 		= (networkCommand->getCommandTypeId() == 1);
        	bool joinNetworkGame 	= (networkCommand->getUnitTypeId() == 1);
       		Game *game = this->world->getGame();

       		//printf("nctPauseResume pauseGame = %d\n",pauseGame);
       		game->setPaused(pauseGame,true,clearCaches,joinNetworkGame);

            if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctPauseResume\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
        	}
            break;

        case nctPlayerStatusChange:
			{
			if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctPlayerStatusChange\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

			commandWasHandled = true;

			int factionIndex = networkCommand->getUnitId();
			int playerStatus = networkCommand->getCommandTypeId();

			if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"nctPlayerStatusChange factionIndex = %d playerStatus = %d\n",factionIndex,playerStatus);

			//printf("#1 nctPlayerStatusChange factionIndex = %d playerStatus = %d\n",factionIndex,playerStatus);

			GameSettings *settings = world->getGameSettingsPtr();
    		if(playerStatus == npst_Disconnected) {
    			//printf("Commander nctPlayerStatusChange factionIndex: %d\n",factionIndex);

    			settings->setNetworkPlayerStatuses(factionIndex,npst_Disconnected);

    			//printf("nctPlayerStatusChange -> faction->getPersonalityType() = %d index [%d] control [%d] networkstatus [%d]\n",
    			//		world->getFaction(factionIndex)->getPersonalityType(),world->getFaction(factionIndex)->getIndex(),world->getFaction(factionIndex)->getControlType(),settings->getNetworkPlayerStatuses(factionIndex));

    			//printf("#2 nctPlayerStatusChange factionIndex = %d playerStatus = %d\n",factionIndex,playerStatus);
    			settings->setFactionControl(factionIndex,ctCpuUltra);
    			settings->setResourceMultiplierIndex(factionIndex,settings->getFallbackCpuMultiplier());
    			//Game *game = this->world->getGame();
    			//game->get
    			Faction *faction = this->world->getFaction(factionIndex);
    			faction->setControlType(ctCpuUltra);

        		if(!world->getGame()->getGameOver()&& !this->world->getGame()->factionLostGame(factionIndex)){
        			// use the fallback multiplier here

        			// mark player as "leaver"
        			this->world->getStats()->setPlayerLeftBeforeEnd(factionIndex,true);
        			// set disconnect time for endgame stats
        			this->world->getStats()->setTimePlayerLeft(factionIndex,this->world->getStats()->getFramesToCalculatePlaytime());
        		}
    		}

			if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctPlayerStatusChange\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
			}
			break;

    }

    if(commandWasHandled == false) {
        Unit* unit= world->findUnitById(networkCommand->getUnitId());

        if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [after world->findUnitById]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,chrono.getMillis());

        if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Running command NetworkCommandType = %d, unitid = %d [%p] factionindex = %d\n",networkCommand->getNetworkCommandType(),networkCommand->getUnitId(),unit,(unit != NULL ? unit->getFactionIndex() : -1));
        //execute command, if unit is still alive
        if(unit != NULL) {
            switch(networkCommand->getNetworkCommandType()) {
                case nctGiveCommand:{
                    assert(networkCommand->getCommandTypeId() != CommandType::invalidId);

                    if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctGiveCommand networkCommand->getUnitId() = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,networkCommand->getUnitId());

                    Command* command= buildCommand(networkCommand);

                    if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [after buildCommand]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,chrono.getMillis());

                    if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] command = %p\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,command);

                    unit->giveCommand(command, (networkCommand->getWantQueue() != 0));

                    if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [after unit->giveCommand]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,chrono.getMillis());

                    if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctGiveCommand networkCommand->getUnitId() = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,networkCommand->getUnitId());
                    }
                    break;
                case nctCancelCommand: {
                	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctCancelCommand\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

                	unit->cancelCommand();

                	if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [after unit->cancelCommand]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,chrono.getMillis());

                	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctCancelCommand\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
                }
                    break;
                case nctSetMeetingPoint: {
                	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctSetMeetingPoint\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

                    unit->setMeetingPos(networkCommand->getPosition());

                    if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [after unit->setMeetingPos]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,chrono.getMillis());

                    if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] found nctSetMeetingPoint\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
                }
                    break;

                default:
                    assert(false);
                    break;
            }
        }
        else {
        	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] NULL Unit for id = %d, networkCommand->getNetworkCommandType() = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,networkCommand->getUnitId(),networkCommand->getNetworkCommandType());
        }
    }

    if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [END]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,chrono.getMillis());
}

Command* Commander::buildCommand(const NetworkCommand* networkCommand) const {
	assert(networkCommand->getNetworkCommandType()==nctGiveCommand);

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] networkCommand [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,networkCommand->toString().c_str());

	if(world == NULL) {
	    char szBuf[8096]="";
	    snprintf(szBuf,8096,"In [%s::%s Line: %d] world == NULL for unit with id: %d",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,networkCommand->getUnitId());
		throw megaglest_runtime_error(szBuf);
	}

	Unit* target= NULL;
	const CommandType* ct= NULL;
	const Unit* unit= world->findUnitById(networkCommand->getUnitId());

	//validate unit
	if(unit == NULL) {
	    char szBuf[8096]="";
	    snprintf(szBuf,8096,"In [%s::%s Line: %d] Can not find unit with id: %d. Game out of synch.",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,networkCommand->getUnitId());
	    SystemFlags::OutputDebug(SystemFlags::debugError,"%s\n",szBuf);
	    if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"%s\n",szBuf);

        GameNetworkInterface *gameNetworkInterface= NetworkManager::getInstance().getGameNetworkInterface();
        if(gameNetworkInterface != NULL) {
            char szMsg[8096]="";
            snprintf(szMsg,8096,"Player detected an error: Can not find unit with id: %d. Game out of synch.",networkCommand->getUnitId());
            gameNetworkInterface->sendTextMessage(szMsg,-1, true, "");
        }

		throw megaglest_runtime_error(szBuf);
	}

    ct = unit->getType()->findCommandTypeById(networkCommand->getCommandTypeId());

	if(unit->getFaction()->getIndex() != networkCommand->getUnitFactionIndex()) {

	    char szBuf[8096]="";
	    snprintf(szBuf,8096,"In [%s::%s Line: %d]\nUnit / Faction mismatch for network command = [%s]\n%s\nfor unit = %d\n[%s]\n[%s]\nactual local factionIndex = %d.\nGame out of synch.",
            __FILE__,__FUNCTION__,__LINE__,networkCommand->toString().c_str(),unit->getType()->getCommandTypeListDesc().c_str(),unit->getId(), unit->getFullName(false).c_str(),unit->getDesc(false).c_str(),unit->getFaction()->getIndex());

	    SystemFlags::OutputDebug(SystemFlags::debugError,"%s\n",szBuf);
	    if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"%s\n",szBuf);
	    //std::string worldLog = world->DumpWorldToLog();
	    world->DumpWorldToLog();

        GameNetworkInterface *gameNetworkInterface= NetworkManager::getInstance().getGameNetworkInterface();
        if(gameNetworkInterface != NULL && gameNetworkInterface->isConnected() == true) {
            char szMsg[8096]="";
            snprintf(szMsg,8096,"Player detected an error: Unit / Faction mismatch for unitId: %d",networkCommand->getUnitId());
            gameNetworkInterface->sendTextMessage(szMsg,-1, true, "");
            snprintf(szMsg,8096,"Local faction index = %d, remote index = %d. Game out of synch.",unit->getFaction()->getIndex(),networkCommand->getUnitFactionIndex());
            gameNetworkInterface->sendTextMessage(szMsg,-1, true, "");

        }
        else if(gameNetworkInterface != NULL) {
            char szMsg[8096]="";
            snprintf(szMsg,8096,"Player detected an error: Connection lost, possible Unit / Faction mismatch for unitId: %d",networkCommand->getUnitId());
            gameNetworkInterface->sendTextMessage(szMsg,-1, true,"");
            snprintf(szMsg,8096,"Local faction index = %d, remote index = %d. Game out of synch.",unit->getFaction()->getIndex(),networkCommand->getUnitFactionIndex());
            gameNetworkInterface->sendTextMessage(szMsg,-1, true,"");
        }

	    std::string sError = "Error [#1]: Game is out of sync (Unit / Faction mismatch)\nplease check log files for details.";
		throw megaglest_runtime_error(sError);
	}

    const UnitType* unitType= world->findUnitTypeById(unit->getFaction()->getType(), networkCommand->getUnitTypeId());

	// debug test!
	//throw megaglest_runtime_error("Test missing command type!");

	//validate command type

    // !!!Test out of synch behaviour
    //ct = NULL;

    // Check if the command was for the unit before it morphed, if so cancel it
    bool isCancelPreMorphCommand = false;
    if(ct == NULL && unit->getPreMorphType() != NULL) {
    	const CommandType *ctPreMorph = unit->getPreMorphType()->findCommandTypeById(networkCommand->getCommandTypeId());
    	if(ctPreMorph != NULL) {
    		ct = unit->getType()->getFirstCtOfClass(ccStop);
    		isCancelPreMorphCommand = true;
    	}
    }

	if(ct == NULL) {
	    char szBuf[8096]="";
	    snprintf(szBuf,8096,"In [%s::%s Line: %d]\nCan not find command type for network command = [%s]\n%s\nfor unit = %d\n[%s]\n[%s]\nactual local factionIndex = %d.\nUnit Type Info:\n[%s]\nNetwork unit type:\n[%s]\nisCancelPreMorphCommand: %d\nGame out of synch.",
	    	extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,networkCommand->toString().c_str(),unit->getType()->getCommandTypeListDesc().c_str(),
            unit->getId(), unit->getFullName(false).c_str(),unit->getDesc(false).c_str(),unit->getFaction()->getIndex(),unit->getType()->toString().c_str(),
            (unitType != NULL ? unitType->getName(false).c_str() : "null"),isCancelPreMorphCommand);

	    SystemFlags::OutputDebug(SystemFlags::debugSystem,"%s\n",szBuf);
	    SystemFlags::OutputDebug(SystemFlags::debugError,"%s\n",szBuf);
	    world->DumpWorldToLog();

        GameNetworkInterface *gameNetworkInterface= NetworkManager::getInstance().getGameNetworkInterface();
        if(gameNetworkInterface != NULL) {
            char szMsg[8096]="";
            snprintf(szMsg,8096,"Player detected an error: Can not find command type: %d for unitId: %d [%s]. isCancelPreMorphCommand: %d Game out of synch.",networkCommand->getCommandTypeId(),networkCommand->getUnitId(),(unitType != NULL ? unitType->getName(false).c_str() : "null"),isCancelPreMorphCommand);
            gameNetworkInterface->sendTextMessage(szMsg,-1, true, "");
        }

	    std::string sError = "Error [#3]: Game is out of sync, please check log files for details.";
	    //abort();
		throw megaglest_runtime_error(sError);
	}

	CardinalDir facing;
	// get facing/target ... the target might be dead due to lag, cope with it
	if(isCancelPreMorphCommand == false) {
		if(ct->getClass() == ccBuild) {
			if(networkCommand->getTargetId() < 0 || networkCommand->getTargetId() >= 4) {
				char szBuf[8096]="";
				snprintf(szBuf,8096,"networkCommand->getTargetId() >= 0 && networkCommand->getTargetId() < 4, [%s]",networkCommand->toString().c_str());
				throw megaglest_runtime_error(szBuf);
			}
			facing = CardinalDir(networkCommand->getTargetId());
		}
		else if (networkCommand->getTargetId() != Unit::invalidId ) {
			target= world->findUnitById(networkCommand->getTargetId());
		}
	}

	//create command
	Command *command= NULL;
	if(isCancelPreMorphCommand == false) {
		if(unitType != NULL) {
			command= new Command(ct, networkCommand->getPosition(), unitType, facing);
		}
		else if(target == NULL) {
			command= new Command(ct, networkCommand->getPosition());
		}
		else {
			command= new Command(ct, target);
		}
	}
	else {
		command= new Command(ct, NULL);
	}

	// Add in any special state
	CommandStateType commandStateType 	= networkCommand->getCommandStateType();
	int commandStateValue 				= networkCommand->getCommandStateValue();

	command->setStateType(commandStateType);
	command->setStateValue(commandStateValue);
	command->setUnitCommandGroupId(networkCommand->getUnitCommandGroupId());

	if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);

	//issue command
	return command;
}

}}//end namespace
