// DWBS ------------------------------------------------------------------ DWBS
//
// PLATFORM		: ALL
// PRODUCT		: BIT_DEMON
// VISIBILITY	: PUBLIC
//
// DWBS ------------------------------------------------------------------ DWBS

#include "Simulation.h"
#include <bdLobby/bdStorage/bdStorage.h>
#include <bdLobby/bdStorage/bdUploadFileResult.h>
#include <bdLobby/bdStorage/bdGetFileResult.h>
#include <bdLobby/bdStorage/bdListFilesResult.h>
#include <bdLobby/bdLobbyCommon/bdAuthCommon.h>

#define TITLE_ID (0x12F7)
#define USERNAME "StorageExample"
#define PASSWORD "MyPassword"

Simulation::Simulation()
: m_taskStatus(READY),
  m_task(START_NETWORK),
  m_changedScreen(true),
  m_authGetHostByName(BD_NULL),
  m_authService(BD_NULL),
  m_lsgGetHostByName(BD_NULL),
  m_remoteTask(BD_NULL),
  m_pumpLobbyConnection(false),
  m_lobbyErrorCode(BD_NO_ERROR),
  m_fileId(0),
  m_storageManager(BD_NULL)
{
}

Simulation::~Simulation()
{
}

bdBool Simulation::init(bdInt32 ,
						bdNChar8* [])
{
	bdBool ok = true;

	// start logging
	// we want to hear about all warnings and errors
	bdLogImpl *log = bdLog::getInstance();
	log->subscribe("warn", &m_logSubscriber);
	log->subscribe("err", &m_logSubscriber);
	log->subscribe("info", &m_logSubscriber);

	m_storageManager = BD_NULL;

	return ok;
}

bdBool Simulation::step(const bdFloat32)
{
	const bdBool quitKey = getScreen().wasKeyPressed(BD_QUIT);

	TaskStatus oldTaskStatus = m_taskStatus;

	if(m_taskStatus == READY)
	{
		startTask();
	}

	if(m_taskStatus == PENDING || m_taskStatus == DONE || m_taskStatus == FAILED)
	{
		// pump even if the status is DONE or FAILED to read out any results
		// and close any remote task handles
		pumpTask();
	}

	if(m_taskStatus == DONE || m_taskStatus == FAILED || m_taskStatus == SKIPPED)
	{
		// incase the task fails or completes in a single loop
		if(oldTaskStatus == READY)
		{
			oldTaskStatus = m_taskStatus;
		}

		displayTaskResult();
	}

	m_changedScreen = (oldTaskStatus != m_taskStatus);

	if(m_pumpLobbyConnection)
	{
		bdBool lostConnection = true;
		bdLobbyServiceImpl *const lobbyService = bdLobbyService::getInstance();
		if(lobbyService)
		{
			const bdLobbyConnection::Status status = lobbyService->getStatus();
			switch(status)
			{
				case bdConnection::BD_NOT_CONNECTED:
				case bdConnection::BD_CONNECTING:
				case bdConnection::BD_DISCONNECTING:
				case bdConnection::BD_DISCONNECTED:
				{
					lostConnection = true;
				}
				break;
				case bdConnection::BD_CONNECTED:
				{
					lostConnection = false;
				}
				break;
			}
		}

		if(lostConnection)
		{
			// set up for a reconnect
			m_pumpLobbyConnection = false;
			m_task = STOP_NETWORK;
			m_taskStatus = READY;
			m_changedScreen = true;
			CloseRemoteTask(&m_remoteTask);
			m_lastTaskDescription = "Lost connection to lobby.";
		}
	}

	getScreen().printToScreen(m_screenText);

	if(m_changedScreen)
	{
		m_screenText = "";
	}

	return !quitKey;
}

void Simulation::quit()
{
	bdLogImpl *log = bdLog::getInstance();
	log->unsubscribe("warn", &m_logSubscriber);
	log->unsubscribe("err", &m_logSubscriber);
	log->unsubscribe("info", &m_logSubscriber);

	stopNetwork();

	delete m_authGetHostByName;
	delete m_authService;
	delete m_lsgGetHostByName;

	if(m_remoteTask)
	{
		m_remoteTask->close();
	}
}

void Simulation::startTask()
{
	const bdBool skipKey = getScreen().wasKeyPressed(SKIP_KEY);

	if (skipKey)
	{
		m_taskStatus = SKIPPED;
	}
	else
	{
		const bdBool startKey = getScreen().wasKeyPressed(START_KEY);
		bdString taskDescription("Unknown task");

		switch(m_task)
		{
			case START_NETWORK:
			{
				startNetworkDescription(taskDescription);
				if(startKey)
				{
					startNetwork();
				}
			}
			break;
			case CHECK_DNS:
			{
				checkDNSDescription(taskDescription);
				if(startKey)
				{
					startCheckDNS();
				}
			}
			break;
			case CREATE_ACCOUNT:
			{
				createAccountDescription(taskDescription);
				if(startKey)
				{
					startCreateAccount();
				}
			}
			break;
			case AUTHENTICATE_ACCOUNT:
			{
				authenticateAccountDescription(taskDescription);
				if(startKey)
				{
					startAuthenticateAccount();
				}
			}
			break;
			case LOBBY_CONNECT:
			{
				lobbyServiceConnectDescription(taskDescription);
				if(startKey)
				{
					startLobbyServiceConnect();
				}
			}
			break;
			case UPLOAD_FILE:
			{
				generateUploadFileDescription(taskDescription);
				if(startKey)
				{
					startUploadFile();
				}
			}
			break;
			case UPDATE_FILE:
			{
				generateUpdateFileDescription(taskDescription);
				if(startKey)
				{
					startUpdateFile();
				}
			}
			break;
			case UPDATE_FILE_PROPERTIES:
			{
				generateUpdateFilePropertiesDescription(taskDescription);
				if(startKey)
				{
					startUpdateFileProperties();
				}
			}
			break;
			case GET_FILE:
			{
				generateGetFileDescription(taskDescription);
				if(startKey)
				{
					startGetFile();
				}
			}
			break;
			case LIST_FILES_BY_OWNER:
			{
				generateListFilesByOwnerDescription(taskDescription);
				if(startKey)
				{
					startListFilesByOwner();
				}
			}
			break;
			case LIST_ALL_PUBLISHER_FILES:
			{
				generateListAllPublisherFilesDescription(taskDescription);
				if(startKey)
				{
					startListAllPublisherFiles();
				}
			}
			break;
			case REMOVE_FILE:
			{
				generateRemoveFileDescription(taskDescription);
				if(startKey)
				{
					startRemoveFile();
				}
			}
			break;
			case LOBBY_DISCONNECT:
			{
				lobbyServiceDisconnectDescription(taskDescription);
				if(startKey)
				{
					startLobbyServiceDisconnect();
				}
			}
			break;
			case STOP_NETWORK:
			{
				stopNetworkDescription(taskDescription);
				if(startKey)
				{
					stopNetwork();
				}
			}
			break;
		}

		if(m_changedScreen)
		{
			m_screenText += taskDescription;
			m_screenText += "\n" START_TEXT;
			m_screenText += "\n" SKIP_TEXT;

			if(m_lastTaskDescription.getLength())
			{
				m_screenText += "\n\n";
				m_screenText += m_lastTaskDescription;
			}
		}
	}
}

void Simulation::pumpTask()
{
	bdString taskDescription("Unknown task");

	switch(m_task)
	{
		case START_NETWORK:
		{
			startNetworkDescription(taskDescription);
			pumpStartNetwork();
		}
		break;
		case CHECK_DNS:
		{
			checkDNSDescription(taskDescription);
			pumpCheckDNS();
		}
		break;
		case CREATE_ACCOUNT:
		{
			createAccountDescription(taskDescription);
			pumpCreateAccount();
		}
		break;
		break;
		case AUTHENTICATE_ACCOUNT:
		{
			authenticateAccountDescription(taskDescription);
			pumpAuthenticateAccount();
		}
		break;
		case LOBBY_CONNECT:
		{
			lobbyServiceConnectDescription(taskDescription);
			pumpLobbyServiceConnect();
		}
		break;
		case UPLOAD_FILE:
		{
			generateUploadFileDescription(taskDescription);
			pumpUploadFile();
		}
		break;
		case UPDATE_FILE:
		{
			generateUpdateFileDescription(taskDescription);
			pumpUpdateFile();
		}
		break;
		case UPDATE_FILE_PROPERTIES:
		{
			generateUpdateFilePropertiesDescription(taskDescription);
			pumpUpdateFileProperties();
		}
		break;
		case GET_FILE:
		{
			generateGetFileDescription(taskDescription);
			pumpGetFile();
		}
		break;
		case LIST_FILES_BY_OWNER:
		{
			generateListFilesByOwnerDescription(taskDescription);
			pumpListFilesByOwner();
		}
		break;
		case LIST_ALL_PUBLISHER_FILES:
		{
			generateListAllPublisherFilesDescription(taskDescription);
			pumpListAllPublisherFiles();
		}
		break;
		case REMOVE_FILE:
		{
			generateRemoveFileDescription(taskDescription);
			pumpRemoveFile();
		}
		break;
		case LOBBY_DISCONNECT:
		{
			lobbyServiceDisconnectDescription(taskDescription);
			pumpLobbyServiceDisconnect();
		}
		break;
		case STOP_NETWORK:
		{
			// nothing to to here
		}
		break;
	}

	if(m_changedScreen)
	{
		m_screenText += taskDescription;
	}
}

void Simulation::displayTaskResult()
{
	bdString taskDescription("Unknown task result");

	Task nextTaskIfDone = m_task;
	Task nextTaskIfFailed = m_task;
	Task nextTaskIfSkipped = m_task;

	bdBool displayLobbyError = true;

	switch(m_task)
	{
		case START_NETWORK:
		{
			startNetworkDescription(m_lastTaskDescription);
			nextTaskIfDone = CHECK_DNS;
			nextTaskIfFailed = START_NETWORK;
			nextTaskIfSkipped = START_NETWORK;
			displayLobbyError = false;
		}
		break;
		case CHECK_DNS:
		{
			checkDNSDescription(m_lastTaskDescription);
			nextTaskIfDone = CREATE_ACCOUNT;
			nextTaskIfFailed = CHECK_DNS;
			nextTaskIfSkipped = CREATE_ACCOUNT;
			displayLobbyError = false;
		}
		break;
		case CREATE_ACCOUNT:
		{
			createAccountDescription(m_lastTaskDescription);
			checkAuthServiceError();
			nextTaskIfDone = AUTHENTICATE_ACCOUNT;
			nextTaskIfFailed = CREATE_ACCOUNT;
			nextTaskIfSkipped = AUTHENTICATE_ACCOUNT;
		}
		break;
		case AUTHENTICATE_ACCOUNT:
		{
			authenticateAccountDescription(m_lastTaskDescription);
			checkAuthServiceError();
			nextTaskIfDone = LOBBY_CONNECT;
			nextTaskIfFailed = AUTHENTICATE_ACCOUNT;
			nextTaskIfSkipped = LOBBY_CONNECT;
		}
		break;
		case LOBBY_CONNECT:
		{
			lobbyServiceConnectDescription(m_lastTaskDescription);
			nextTaskIfDone = UPLOAD_FILE;
			nextTaskIfFailed = LOBBY_CONNECT;
			nextTaskIfSkipped = UPLOAD_FILE;
		}
		break;
		case UPLOAD_FILE:
		{
			generateUploadFileDescription(m_lastTaskDescription);
			nextTaskIfDone = UPDATE_FILE;
			nextTaskIfFailed = UPLOAD_FILE;
			nextTaskIfSkipped = LIST_FILES_BY_OWNER;
		}
		break;
		case UPDATE_FILE:
		{
			generateUpdateFileDescription(m_lastTaskDescription);
			nextTaskIfDone = UPDATE_FILE_PROPERTIES;
			nextTaskIfFailed = UPDATE_FILE;
			nextTaskIfSkipped = UPDATE_FILE_PROPERTIES;
		}
		break;
		case UPDATE_FILE_PROPERTIES:
		{
			generateUpdateFilePropertiesDescription(m_lastTaskDescription);
			nextTaskIfDone = GET_FILE;
			nextTaskIfFailed = UPDATE_FILE_PROPERTIES;
			nextTaskIfSkipped = GET_FILE;
		}
		break;
		case GET_FILE:
		{
			generateGetFileDescription(m_lastTaskDescription);
			nextTaskIfDone = LIST_FILES_BY_OWNER;
			nextTaskIfFailed = GET_FILE;
			nextTaskIfSkipped = LIST_FILES_BY_OWNER;
		}
		break;
		case LIST_FILES_BY_OWNER:
		{
			generateListFilesByOwnerDescription(m_lastTaskDescription);
			nextTaskIfDone = LIST_ALL_PUBLISHER_FILES;
			nextTaskIfFailed = LIST_FILES_BY_OWNER;
			nextTaskIfSkipped = LIST_ALL_PUBLISHER_FILES;
		}
		break;
		case LIST_ALL_PUBLISHER_FILES:
		{
			generateListAllPublisherFilesDescription(m_lastTaskDescription);
			nextTaskIfDone = (m_fileId ? REMOVE_FILE : LOBBY_DISCONNECT);
			nextTaskIfFailed = LIST_ALL_PUBLISHER_FILES;
			nextTaskIfSkipped = (m_fileId ? REMOVE_FILE : LOBBY_DISCONNECT);
		}
		break;
		case REMOVE_FILE:
		{
			generateRemoveFileDescription(m_lastTaskDescription);
			nextTaskIfDone = LOBBY_DISCONNECT;
			nextTaskIfFailed = REMOVE_FILE;
			nextTaskIfSkipped = LOBBY_DISCONNECT;
		}
		break;
		case LOBBY_DISCONNECT:
		{
			lobbyServiceDisconnectDescription(m_lastTaskDescription);
			nextTaskIfDone = STOP_NETWORK;
			nextTaskIfFailed = LOBBY_DISCONNECT;
			nextTaskIfSkipped = STOP_NETWORK;
		}
		break;
		case STOP_NETWORK:
		{
			stopNetworkDescription(m_lastTaskDescription);
			nextTaskIfDone = START_NETWORK;
			nextTaskIfFailed = STOP_NETWORK;
			nextTaskIfSkipped = CREATE_ACCOUNT;
		}
		break;
	}

	switch(m_taskStatus)
	{
		case READY:
		case PENDING:
		{
			BD_WARN(BD_LOG_LEVEL, "Unexpected task status.");
		}
		break;
		case DONE:
		{
			m_task = nextTaskIfDone;
		}
		break;
		case FAILED:
		{
			m_task = nextTaskIfFailed;
		}
		break;
		case SKIPPED:
		{
			m_task = nextTaskIfSkipped;
		}
		break;
	}

	// if task uses sets a lobby error code and has failed
	// display the error description
	if(displayLobbyError && (m_taskStatus == FAILED))
	{
		bdNChar8 buf[32];
		LobbyErrorCodeToString(m_lobbyErrorCode, buf, sizeof(buf));
		m_lastTaskDescription += "\nError: ";
		m_lastTaskDescription += buf;
		m_lobbyErrorCode = BD_NO_ERROR;
	}

	m_taskStatus = READY;
}

void Simulation::startNetworkDescription(bdString& description)
{
	const bdNChar8 *const descriptions[] = {"Start Network",				// READY
											"Starting Network",				// PENDING
											"Started Network",				// DONE
											"Starting Network Failed",		// FAILED
											"Starting Network Skipped"};	// SKIPPED

	const bdUInt numDescriptions = sizeof(descriptions) / sizeof(bdNChar8*);
	IndexStrings(descriptions, numDescriptions, m_taskStatus, description);
}

void Simulation::startNetwork()
{
	bdNetImpl *const net = bdNet::getInstance();
	bdNetStartParams params;
	params.m_onlineGame = true;

	// platform specific startup parameters
	bdPlatformParams platformParams;
	//TODO: check the return value here
	bdExamplePlatformInit::init(params, &platformParams);

	if(net->start(params))
	{
		m_taskStatus = PENDING;
	}
	else
	{
		m_taskStatus = FAILED;
	}
}

void Simulation::pumpStartNetwork()
{
	bdNetImpl *const net = bdNet::getInstance();
	net->pump();

	const bdNetImpl::bdNetStatus networkStatus = net->getStatus();
	switch(networkStatus)
	{
		case bdNetImpl::BD_NET_BIND_FAILED:
		case bdNetImpl::BD_NET_ONLINE_FAILED:
		case bdNetImpl::BD_NET_INIT_FAILED:
		case bdNetImpl::BD_NET_STOPPED:
		{
			// error conditions
			m_taskStatus = FAILED;
		}
		break;
		case bdNetImpl::BD_NET_PENDING:
		{
			// wait
			m_taskStatus = PENDING;
		}
		break;
		case bdNetImpl::BD_NET_DONE:
		{
			// complete
			m_taskStatus = DONE;
		}
	}
}

void Simulation::checkDNSDescription(bdString& description)
{
	const bdNChar8 *const descriptions[] = {"Start DNS Lookup",			// READY
											"Performing DNS Lookup",	// PENDING
											"DNS Lookup Succeeded",		// DONE
											"DNS Lookup Failed",		// FAILED
											"DNS Lookup Skipped"};	// SKIPPED

	const bdUInt numDescriptions = sizeof(descriptions) / sizeof(bdNChar8*);
	IndexStrings(descriptions, numDescriptions, m_taskStatus, description);

	if(m_taskStatus == DONE)
	{
		bdNChar8 addrString[BD_INET_ADDR_STRING_SIZE];
		m_authAddr.toString(addrString, sizeof(addrString));
		description +=  "\n " AUTH_ADDRESS ":";
		description += addrString;
		description +=  "\n";

		m_lsgAddr.toString(addrString, sizeof(addrString));
		description +=  " " LSG_ADDRESS ":";
		description += addrString;
	}

}

void Simulation::startCheckDNS()
{
	const TaskStatus authDNSStatus = StartGetHostByName(AUTH_ADDRESS, &m_authGetHostByName, m_authAddr);
	const TaskStatus lsgDNSStatus = StartGetHostByName(LSG_ADDRESS, &m_lsgGetHostByName, m_lsgAddr);
	if((authDNSStatus == FAILED) || (lsgDNSStatus == FAILED))
	{
		m_taskStatus = FAILED;
	}

	if((authDNSStatus == PENDING) || (lsgDNSStatus == PENDING))
	{
		m_taskStatus = PENDING;
	}

	if((authDNSStatus == DONE) && (lsgDNSStatus == DONE))
	{
		m_taskStatus = DONE;
	}
}

void Simulation::pumpCheckDNS()
{
	TaskStatus authDNSStatus = DONE;
	TaskStatus lsgDNSStatus = DONE;

	if(m_authGetHostByName)
	{
		authDNSStatus = PumpGetHostByName(&m_authGetHostByName, m_authAddr);
	}

	if(m_lsgGetHostByName)
	{
		lsgDNSStatus = PumpGetHostByName(&m_lsgGetHostByName, m_lsgAddr);
	}

	if((authDNSStatus == FAILED) || (lsgDNSStatus == FAILED))
	{
		m_taskStatus = FAILED;
	}

	if((authDNSStatus == PENDING) || (lsgDNSStatus == PENDING))
	{
		m_taskStatus = PENDING;
	}

	if((authDNSStatus == DONE) && (lsgDNSStatus == DONE))
	{
		m_taskStatus = DONE;
	}
}

bdAuthService* Simulation::getAuthService()
{
	if(m_authService == BD_NULL)
	{
		const bdAddr authAddr(m_authAddr, AUTH_PORT);
		m_authService = new bdAuthService(TITLE_ID, authAddr);
	}

	return m_authService;
}


const bdAuthInfo *Simulation::getAuthInfo()
{
	if(m_authService != BD_NULL)
	{
		return m_authService->getAuthInfo();
	}

	return BD_NULL;
}

void Simulation::checkAuthServiceError()
{
	bdAuthService *const authService = getAuthService();
	if(authService)
	{
		m_lobbyErrorCode = authService->getErrorCode();
	}
}

void Simulation::pumpAuthServiceTask()
{
	// fail safe
	m_taskStatus = FAILED;

	bdAuthService *const authService = getAuthService();
	if(authService)
	{
		m_taskStatus = PENDING;
		const bdAuthService::bdStatus status = authService->getStatus();
		if(status == bdAuthService::BD_READY)
		{
			const bdLobbyErrorCode lobbyErrorCode = authService->getErrorCode();
			if(lobbyErrorCode == BD_AUTH_NO_ERROR)
			{
				m_taskStatus = DONE;
			}
			else
			{
				m_taskStatus = FAILED;
			}
		}
	}
}

void Simulation::createAccountDescription(bdString& description)
{
	const bdNChar8 *const descriptions[] = {"Create Account (" USERNAME "/" PASSWORD ")",	// READY
											"Creating Account",								// PENDING
											"Created Account",								// DONE
											"Create Account Failed",						// FAILED
											"Create Account Skipped"};						// SKIPPED

	const bdUInt numDescriptions = sizeof(descriptions) / sizeof(bdNChar8*);
	IndexStrings(descriptions, numDescriptions, m_taskStatus, description);
}

void Simulation::startCreateAccount()
{
	// fail safe
	m_taskStatus = FAILED;

	bdAuthService *const authService = getAuthService();
	if(authService)
	{
		if(authService->createAccount(USERNAME, PASSWORD))
		{
			m_taskStatus = PENDING;
		}
	}
}

void Simulation::pumpCreateAccount()
{
	pumpAuthServiceTask();
}

void Simulation::authenticateAccountDescription(bdString& description)
{
	const bdNChar8 *const descriptions[] = {"Authenticate Account (" USERNAME "/" PASSWORD ")",	// READY
											"Authenticating Account",									// PENDING
											"Authenticated Account",									// DONE
											"Authenticate Account Failed",								// FAILED
											"Authenticate Account Skipped"};							// SKIPPED

	const bdUInt numDescriptions = sizeof(descriptions) / sizeof(bdNChar8*);
	IndexStrings(descriptions, numDescriptions, m_taskStatus, description);
}

void Simulation::startAuthenticateAccount()
{
	// fail safe
	m_taskStatus = FAILED;

	bdAuthService *const authService = getAuthService();
	if(authService)
	{
		if(authService->authorizeAccount(USERNAME, PASSWORD))
		{
			m_taskStatus = PENDING;
		}
	}
}

void Simulation::pumpAuthenticateAccount()
{
	pumpAuthServiceTask();
}

void Simulation::lobbyServiceConnectDescription(bdString& description)
{
	const bdNChar8 *const descriptions[] = {"Connect to Lobby Service",				// READY
											"Connecting to Lobby Service",			// PENDING
											"Connected to Lobby Service",			// DONE
											"Connect to Lobby Service Failed",		// FAILED
											"Connect to Lobby Service Skipped"};	// SKIPPED

	const bdUInt numDescriptions = sizeof(descriptions) / sizeof(bdNChar8*);
	IndexStrings(descriptions, numDescriptions, m_taskStatus, description);
}

void Simulation::startLobbyServiceConnect()
{
	// fail safe
	m_taskStatus = FAILED;

	bdLobbyServiceImpl *const lobbyService = bdLobbyService::getInstance();
	if(lobbyService)
	{
		const bdAddr lsgAddr(m_lsgAddr, LSG_PORT);
		const bdBool connecting = lobbyService->connect(lsgAddr, getAuthInfo());
		if(connecting)
		{
			m_taskStatus = PENDING;
		}
		// no need to keep the bdAuthService instance after we are connected to the lobby
		delete m_authService;
		m_authService = BD_NULL;
	}
}

void Simulation::pumpLobbyServiceConnect()
{
	// fail safe
	m_taskStatus = FAILED;

	m_pumpLobbyConnection = false;

	bdLobbyServiceImpl *const lobbyService = bdLobbyService::getInstance();
	if(lobbyService)
	{
		const bdLobbyConnection::Status connectionStatus = lobbyService->getStatus();
		switch(connectionStatus)
		{
			case bdConnection::BD_NOT_CONNECTED:
			case bdConnection::BD_DISCONNECTING:
			case bdConnection::BD_DISCONNECTED:
			{
				m_taskStatus = FAILED;
			}
			break;
			case bdConnection::BD_CONNECTING:
			{
				m_taskStatus = PENDING;
			}
			break;
			case bdConnection::BD_CONNECTED:
			{
				m_taskStatus = DONE;
				m_pumpLobbyConnection = true;
				m_storageManager = bdLobbyService::getInstance()->getStorage();
				m_UserId = bdAuthUtility::getUserID(USERNAME);
			}
			break;
		}
	}
}

void Simulation::generateUploadFileDescription(bdString& description)
{
	const bdNChar8 *const descriptions[] =
	{
		"Upload file",				// READY
		"Uploading file",			// PENDING
		"File uploaded",			// DONE
		"File upload failed",		// FAILED
		"File upload skipped"		// SKIPPED
	};

	const bdUInt numDescriptions = sizeof(descriptions) / sizeof(bdNChar8*);
	IndexStrings(descriptions, numDescriptions, m_taskStatus, description);

	if (m_taskStatus == FAILED)
	{
		description += ":\n";
		const bdUInt errorStrLen = 128;
		bdNChar8 errorStr[errorStrLen];
		LobbyErrorCodeToString(m_storageManager->getErrorCode(), errorStr, errorStrLen);
		description += errorStr;
	}
}

void Simulation::startUploadFile()
{
	BD_ASSERT(m_storageManager, "could not get storage manager from lobby service");

	// fail safe
	m_taskStatus = FAILED;

	// Upload a private lobby file
	bdLobbyFile * file = new bdLobbyFile(1024);

	if (file)
	{
		bdRandom rand;
		rand.nextUBytes(static_cast<bdUByte8*>(file->getData()),1024);

		bdUInt resSize = 24;
		m_tigerHash.hash(
			static_cast<bdUByte8*>(file->getData()),
			file->getFileSize(),
			m_hashFile,
			resSize);

		m_remoteTask = m_storageManager->uploadFile(0,
			"example priv usr file 1-(1k)",
			file,
			bdLobbyFileHeader::BD_VISIBLE_PUBLIC,
			bdLobbyFileHeader::BD_OWNER_USER);

		m_taskStatus = CheckRemoteTaskStatus(m_remoteTask);

		delete file;
	}
}

void Simulation::pumpUploadFile()
{
	m_taskStatus = CheckRemoteTaskStatus(m_remoteTask);

	if(m_taskStatus == DONE)
	{
		// fail safe
		m_taskStatus = FAILED;

		// Retrieve the fileID of the uploaded file
		bdUploadFileResult result;
		if (m_storageManager->handleTask(m_remoteTask, &result))
		{
			m_fileId = result.m_fileID;
			m_taskStatus = DONE;
		}
	}

	if((m_taskStatus == DONE) || (m_taskStatus == FAILED))
	{
		CloseRemoteTask(&m_remoteTask);
	}
}

void Simulation::generateUpdateFileDescription(bdString& description)
{
	const bdNChar8 *const descriptions[] =
	{
		"Update file",				// READY
		"Updating file",			// PENDING
		"File updated",				// DONE
		"File update failed",		// FAILED
		"File update skipped"		// SKIPPED
	};

	const bdUInt numDescriptions = sizeof(descriptions) / sizeof(bdNChar8*);
	IndexStrings(descriptions, numDescriptions, m_taskStatus, description);

	if (m_taskStatus == FAILED)
	{
		description += ":\n";
		const bdUInt errorStrLen = 128;
		bdNChar8 errorStr[errorStrLen];
		LobbyErrorCodeToString(m_storageManager->getErrorCode(), errorStr, errorStrLen);
		description += errorStr;
	}
}

void Simulation::startUpdateFile()
{
	// fail safe
	m_taskStatus = FAILED;

	bdLobbyFile *file = new bdLobbyFile(1024);
	if (file)
	{
		bdMemset(file->getData(),'!',1024);

		bdUInt resSize = 24;
		m_tigerHash.hash(
			static_cast<bdUByte8*>(file->getData()),
			file->getFileSize(),
			m_hashFile,
			resSize);

		m_remoteTask = m_storageManager->updateFile(0, m_fileId, file);
		m_taskStatus = CheckRemoteTaskStatus(m_remoteTask);
		delete file;
	}
}

void Simulation::pumpUpdateFile()
{
	m_taskStatus = CheckRemoteTaskStatus(m_remoteTask);

	if(m_taskStatus == DONE)
	{
		// fail safe
		m_taskStatus = FAILED;

		// check the task status
		if (m_storageManager->handleTask(m_remoteTask))
		{
			m_taskStatus = DONE;
		}
	}

	if((m_taskStatus == DONE) || (m_taskStatus == FAILED))
	{
		CloseRemoteTask(&m_remoteTask);
	}
}

void Simulation::generateUpdateFilePropertiesDescription(bdString& description)
{
	const bdNChar8 *const descriptions[] =
	{
		"Update file properties",			// READY
		"Updating file properties",			// PENDING
		"File properties updated",			// DONE
		"File properties update failed",	// FAILED
		"File properties update skipped"	// SKIPPED
	};

	const bdUInt numDescriptions = sizeof(descriptions) / sizeof(bdNChar8*);
	IndexStrings(descriptions, numDescriptions, m_taskStatus, description);

	if (m_taskStatus == FAILED)
	{
		description += ":\n";
		const bdUInt errorStrLen = 128;
		bdNChar8 errorStr[errorStrLen];
		LobbyErrorCodeToString(m_storageManager->getErrorCode(), errorStr, errorStrLen);
		description += errorStr;
	}
}

void Simulation::startUpdateFileProperties()
{
	m_remoteTask = m_storageManager->updateFileProperties(0, m_fileId,"file 1: updated text",bdLobbyFileHeader::BD_VISIBLE_PUBLIC);
	m_taskStatus = CheckRemoteTaskStatus(m_remoteTask);
}

void Simulation::pumpUpdateFileProperties()
{
	m_taskStatus = CheckRemoteTaskStatus(m_remoteTask);

	if(m_taskStatus == DONE)
	{
		// fail safe
		m_taskStatus = FAILED;

		// check the task status
		if (m_storageManager->handleTask(m_remoteTask))
		{
			m_taskStatus = DONE;
		}
	}

	if((m_taskStatus == DONE) || (m_taskStatus == FAILED))
	{
		CloseRemoteTask(&m_remoteTask);
	}
}

void Simulation::generateGetFileDescription(bdString& description)
{
	const bdNChar8 *const descriptions[] =
	{
		"Get file",			// READY
		"Getting file",		// PENDING
		"Got file",			// DONE
		"File get failed",	// FAILED
		"File get skipped"	// SKIPPED
	};

	const bdUInt numDescriptions = sizeof(descriptions) / sizeof(bdNChar8*);
	IndexStrings(descriptions, numDescriptions, m_taskStatus, description);

	if (m_taskStatus == FAILED)
	{
		description += ":\n";
		const bdUInt errorStrLen = 128;
		bdNChar8 errorStr[errorStrLen];
		LobbyErrorCodeToString(m_storageManager->getErrorCode(), errorStr, errorStrLen);
		description += errorStr;
	}
}

void Simulation::startGetFile()
{
	m_remoteTask = m_storageManager->getFile(0, m_fileId);
	m_taskStatus = CheckRemoteTaskStatus(m_remoteTask);
}

void Simulation::pumpGetFile()
{
	m_taskStatus = CheckRemoteTaskStatus(m_remoteTask);

	if(m_taskStatus == DONE)
	{
		// fail safe
		m_taskStatus = FAILED;

		// check the task status
		bdGetFileResult result;
		if (m_storageManager->handleTask(m_remoteTask, &result))
		{
			m_taskStatus = DONE;

			bdUInt resSize = 24;
			bdUByte8 * rtnHashFile = new bdUByte8[24];
			m_tigerHash.hash(
				static_cast<bdUByte8*>(result.m_files[0]->getData()),
				result.m_files[0]->getFileSize(),
				rtnHashFile,
				resSize);

			BD_ASSERT(bdMemcmp(m_hashFile, rtnHashFile, 24) == 0, "Tiger Hash not correct");

			// <apm> display file from get?
			//m_lobbyStrings.appendGetFileText(result,&m_screenText);

			delete rtnHashFile;
		}
	}
	if((m_taskStatus == DONE) || (m_taskStatus == FAILED))
	{
		CloseRemoteTask(&m_remoteTask);
	}
}

void Simulation::generateListFilesByOwnerDescription(bdString& description)
{
	const bdNChar8 *const descriptions[] =
	{
		"List files by owner",			// READY
		"Listing files by owner",		// PENDING
		"Files by owner listed",		// DONE
		"List files by owner failed",	// FAILED
		"List files by owner skipped"	// SKIPPED
	};

	const bdUInt numDescriptions = sizeof(descriptions) / sizeof(bdNChar8*);
	IndexStrings(descriptions, numDescriptions, m_taskStatus, description);

	if (m_taskStatus == FAILED)
	{
		description += ":\n";
		const bdUInt errorStrLen = 128;
		bdNChar8 errorStr[errorStrLen];
		LobbyErrorCodeToString(m_storageManager->getErrorCode(), errorStr, errorStrLen);
		description += errorStr;
	}
	else if (m_taskStatus == DONE)
	{
		description += ":\n";
		if (m_fileListString.getLength())
		{
			description += m_fileListString;
		}
		else
		{
			description += "No files found.";
		}
	}
}

void Simulation::startListFilesByOwner()
{
	bdUInt32 startDate = 0;
	bdUInt16 maxResults = 10;
	m_remoteTask = m_storageManager->listFilesByOwner(0, m_UserId, startDate, maxResults);
	m_taskStatus = CheckRemoteTaskStatus(m_remoteTask);
}

void Simulation::pumpListFilesByOwner()
{
	m_taskStatus = CheckRemoteTaskStatus(m_remoteTask);

	if(m_taskStatus == DONE)
	{
		// fail safe
		m_taskStatus = FAILED;

		// check the task status
		bdListFilesResult result;
		if (m_storageManager->handleTask(m_remoteTask, &result))
		{
			m_taskStatus = DONE;
			m_fileListString = "";
			bdUInt i;
			for(i = 0; i < result.m_fileHeaders.getSize(); i++)
			{
				m_fileListString += "Name: \"";
				m_fileListString += result.m_fileHeaders[i]->getFileName();
				m_fileListString += "\", Size: ";
				const bdUInt sizeStrLen = 128;
				bdNChar8 sizeStr[sizeStrLen];
				bdSnprintf(sizeStr, sizeStrLen, "%u", result.m_fileHeaders[i]->getFileSize());
				m_fileListString += sizeStr;
				m_fileListString += ", Owner type: ";
				m_fileListString += ( result.m_fileHeaders[i]->getOwnerType() == bdLobbyFileHeader::BD_OWNER_USER ) ? "User":"Publisher";
				m_fileListString += ", Visibility: ";
				m_fileListString += ( result.m_fileHeaders[i]->getVisibility() == bdLobbyFileHeader::BD_VISIBLE_PUBLIC) ? "Public":"Private";
				m_fileListString += "\n";
			}
		}
	}

	if((m_taskStatus == DONE) || (m_taskStatus == FAILED))
	{
		CloseRemoteTask(&m_remoteTask);
	}
}

void Simulation::generateListAllPublisherFilesDescription(bdString& description)
{
	const bdNChar8 *const descriptions[] =
	{
		"List all publisher files",			// READY
		"Listing all publisher files",		// PENDING
		"All publisher files listed",		// DONE
		"List all publisher files failed",	// FAILED
		"List all publisher files skipped"	// SKIPPED
	};

	const bdUInt numDescriptions = sizeof(descriptions) / sizeof(bdNChar8*);
	IndexStrings(descriptions, numDescriptions, m_taskStatus, description);

	if (m_taskStatus == FAILED)
	{
		description += ":\n";
		const bdUInt errorStrLen = 128;
		bdNChar8 errorStr[errorStrLen];
		LobbyErrorCodeToString(m_storageManager->getErrorCode(), errorStr, errorStrLen);
		description += errorStr;
	}
	else if (m_taskStatus == DONE)
	{
		description += ":\n";
		if (m_fileListString.getLength())
		{
			description += m_fileListString;
		}
		else
		{
			description += "No files found.";
		}
	}
}

void Simulation::startListAllPublisherFiles()
{
	bdUInt32 startDate = 0;
	bdUInt16 maxResults = 10;

	m_remoteTask = m_storageManager->listAllPublisherFiles(0, startDate, maxResults);
	m_taskStatus = CheckRemoteTaskStatus(m_remoteTask);
}

void Simulation::pumpListAllPublisherFiles()
{
	m_taskStatus = CheckRemoteTaskStatus(m_remoteTask);

	if(m_taskStatus == DONE)
	{
		// fail safe
		m_taskStatus = FAILED;

		// check the task status
		bdListFilesResult result;
		if (m_storageManager->handleTask(m_remoteTask, &result))
		{
			m_taskStatus = DONE;
			m_fileListString = "";
			bdUInt i;
			for(i = 0; i < result.m_fileHeaders.getSize(); i++)
			{
				m_fileListString += "Name: \"";
				m_fileListString += result.m_fileHeaders[i]->getFileName();
				m_fileListString += "\", Size: ";
				const bdUInt sizeStrLen = 128;
				bdNChar8 sizeStr[sizeStrLen];
				bdSnprintf(sizeStr, sizeStrLen, "%u", result.m_fileHeaders[i]->getFileSize());
				m_fileListString += sizeStr;
				m_fileListString += ", Owner type: ";
				m_fileListString += ( result.m_fileHeaders[i]->getOwnerType() == bdLobbyFileHeader::BD_OWNER_USER ) ? "User":"Publisher";
				m_fileListString += ", Visibility: ";
				m_fileListString += ( result.m_fileHeaders[i]->getVisibility() == bdLobbyFileHeader::BD_VISIBLE_PUBLIC) ? "Public":"Private";
				m_fileListString += "\n";
			}
		}
	}

	if((m_taskStatus == DONE) || (m_taskStatus == FAILED))
	{
		CloseRemoteTask(&m_remoteTask);
	}
}

void Simulation::generateRemoveFileDescription(bdString& description)
{
	const bdNChar8 *const descriptions[] =
	{
		"Remove file",			// READY
		"Removing file",		// PENDING
		"Removed file",			// DONE
		"Remove file failed",	// FAILED
		"Remove file skipped"	// SKIPPED
	};

	const bdUInt numDescriptions = sizeof(descriptions) / sizeof(bdNChar8*);
	IndexStrings(descriptions, numDescriptions, m_taskStatus, description);

	if (m_taskStatus == FAILED)
	{
		description += ":\n";
		const bdUInt errorStrLen = 128;
		bdNChar8 errorStr[errorStrLen];
		LobbyErrorCodeToString(m_storageManager->getErrorCode(), errorStr, errorStrLen);
		description += errorStr;
	}
}

void Simulation::startRemoveFile()
{
	m_remoteTask = m_storageManager->removeFile(0, m_fileId);
	m_taskStatus = CheckRemoteTaskStatus(m_remoteTask);
}

void Simulation::pumpRemoveFile()
{
	m_taskStatus = CheckRemoteTaskStatus(m_remoteTask);

	if(m_taskStatus == DONE)
	{
		// fail safe
		m_taskStatus = FAILED;

		// check the task status
		if (m_storageManager->handleTask(m_remoteTask))
		{
			m_taskStatus = DONE;
			m_fileId = 0;
		}
	}

	if((m_taskStatus == DONE) || (m_taskStatus == FAILED))
	{
		CloseRemoteTask(&m_remoteTask);
	}
}

void Simulation::lobbyServiceDisconnectDescription(bdString& description)
{
	const bdNChar8 *const descriptions[] = {"Disconnect from Lobby Service",			// READY
											"Disconnecting from Lobby Service",			// PENDING
											"Disconnected from Lobby Service",			// DONE
											"Disconnect from Lobby Service Failed",		// FAILED
											"Disconnect from Lobby Service Skipped"};	// SKIPPED

	const bdUInt numDescriptions = sizeof(descriptions) / sizeof(bdNChar8*);
	IndexStrings(descriptions, numDescriptions, m_taskStatus, description);
}

void Simulation::startLobbyServiceDisconnect()
{
	// fail safe
	m_taskStatus = FAILED;

	bdLobbyServiceImpl *const lobbyService = bdLobbyService::getInstance();
	if(lobbyService)
	{
		lobbyService->disconnect();
		m_taskStatus = PENDING;
	}
}

void Simulation::pumpLobbyServiceDisconnect()
{
	// fail safe
	m_taskStatus = FAILED;

	bdLobbyServiceImpl *const lobbyService = bdLobbyService::getInstance();
	if(lobbyService)
	{
		const bdLobbyConnection::Status status = lobbyService->getStatus();
		switch(status)
		{
			case bdConnection::BD_DISCONNECTED:
			case bdConnection::BD_NOT_CONNECTED:
			{
				m_taskStatus = DONE;
				m_UserId = 0;
			}
			break;
			case bdConnection::BD_CONNECTING:
			case bdConnection::BD_CONNECTED:
			{
				m_taskStatus = FAILED;
			}
			break;
			case bdConnection::BD_DISCONNECTING:
			{
				m_taskStatus = PENDING;
			}
			break;
		}
	}
}

void Simulation::stopNetworkDescription(bdString& description)
{
	const bdNChar8 *const descriptions[] = {"Stop Network",					// READY
											"Stopping Network",				// PENDING
											"Stopped Network",				// DONE
											"Stopping Network Failed",		// FAILED
											"Stopping Network Skipped"};	// SKIPPED

	const bdUInt numDescriptions = sizeof(descriptions) / sizeof(bdNChar8*);
	IndexStrings(descriptions, numDescriptions, m_taskStatus, description);
}

void Simulation::stopNetwork()
{
	bdNetImpl *const net = bdNet::getInstance();
	net->stop();
	if(net->getStatus() == bdNetImpl::BD_NET_STOPPED)
	{
		bdExamplePlatformInit::quit();
		m_taskStatus = DONE;
	}
	else
	{
		m_taskStatus = FAILED;
	}
}

