////////////////////////////////////////////////////////////////////////////////
// StartGameScreen.cpp
// Start game screen for MechVM
// Copyright Bjoern Ganster 
// Developed since 2007
////////////////////////////////////////////////////////////////////////////////

#include "BGProcess.h"
#include "Config.h"
#include "Font.h"
#include "LevelEd.h"
#include "MechVM.h"
#include "StartGameScreen.h"

#include "GLButton.h"
#include "GLLabel.h"
#include "GLTableView.h"

#include <iostream>

#define FILE_NUMBER FILE_NO_START_GAME_SCREEN_CPP

const int gameLauncherTableID = 13;
const int startCampaignID = 14;
const int instantActionID = 15;
const int editLevelID = 16;

////////////////////////////////////////////////////////////////////////////////

StartGameScreen::StartGameScreen(int _left, int _top, int _width, int _height) 
: GLWindowContainer("StartGameScreen", StartGameScreen_ID, nullptr, _left, _top, _width, _height),
  mPlayGameButton(nullptr),
  mEditLevelButton(nullptr)
{
}

StartGameScreen::~StartGameScreen()
{
	safeDelete(gameTable);
	safeDelete(label);
	safeDelete(mPlayGameButton);
	safeDelete(mEditLevelButton);
}

void StartGameScreen::setup()
{
	SET_SCOPE;
	getMechVm()->createToolbar(this);

	Config* cfg = getConfig();
	Color backgroundColor = cfg->getViewerBackgroundColor();
	Font* font = getDefaultFont();
	int buttonHeight = getDefaultButtonHeight();

	SET_OP;
	gameTable = new GLTableView(this, margin, 3 * margin + 2 * buttonHeight, getWidth()-2*margin, 
		getHeight()-5*margin-3*buttonHeight, gameLauncherTableID);
	gameTable->userData = gameLauncherTableID;
	gameTable->setDataSource(this);
	gameTable->setColWidth(1, 50, 150);
	gameTable->setRowHeights(100, font->getCharHeight()+5);
	gameTable->setListener(this);
	add(gameTable);

	SET_OP;
	int buttonWidth = 20 * font->getMaxCharWidth();
	mPlayGameButton = new GLButton(this, margin, getHeight()-margin-buttonHeight, buttonWidth, buttonHeight);
	mPlayGameButton->userData = startCampaignID;
	mPlayGameButton->setListener(this);
	mPlayGameButton->setTitle(tr(29));
	mPlayGameButton->setBackgroundColor(backgroundColor);
	add(mPlayGameButton);

	#ifdef _DEBUG
	SET_OP;
	mEditLevelButton = new GLButton(this, 2*margin+buttonWidth, getHeight()-margin-buttonHeight, 200, buttonHeight);
	mEditLevelButton->userData = editLevelID;
	mEditLevelButton->setListener(this);
	mEditLevelButton->setTitle(tr(49));
	mEditLevelButton->setBackgroundColor(backgroundColor);
	add(mEditLevelButton);
	#endif

	SET_OP;
	label = new GLLabel (this, 300, 2 * margin + buttonHeight, 600, buttonHeight);
	label->textColor = getConfig()->getTextColor();
	add (label);
	RETURN;
}

///////////////////////////////////////////////////////////////////////////////
// TableView implementation: get number of rows and columns

void StartGameScreen::getDimensions (size_t /*senderID*/, size_t& rowCount, size_t& colCount)
{
	SET_SCOPE;
	Config* config = getConfig();
	rowCount = 0;
	colCount = 1;
	for (size_t i = 0; i < config->getKeyCount(); i++) {
		BGCPString key = config->getKey(i);
		if (key.startsWith ("GameName")) {
			rowCount++;
		}
	}
	RETURN;
}

///////////////////////////////////////////////////////////////////////////////
// TableView implementation: Get string for a cell

bool StartGameScreen::getString (size_t /*senderID*/, int row, int /*col*/, BGCPString& str)
{
	SET_SCOPE;
	size_t i = 0;
	Config* config = getConfig();

	while (i < config->getKeyCount()) {
		SET_OP;
		BGCPString key = config->getKey(i);
		if (key.startsWith("GameName")) {
			if (row == 0) {
				str = config->getValue(i);
				RETURN true;
			}
			row--;
		}
		i++;
	}

	RETURN false;
}

///////////////////////////////////////////////////////////////////////////////
// Receive messages

void StartGameScreen::receiveMessage (VirtualClass* sender, int messageType, VirtualClass* data)
{
	SET_SCOPE;
	GLWindow* win = dynamic_cast <GLWindow*> (sender);
	if (win != NULL) {
		switch (win->userData) {
			case playGameButtonID:
				break;
			case viewFileButtonID:
			case installButtonID:
				getMechVm()->receiveMessage(sender, messageType, data);
				break;
			case MechLabButtonID:
				getMechVm()->startMechLab(this);
				break;
			case startCampaignID:
				startCampaign();
				break;
			case editLevelID:
				editLevel();
				break;
		}
	}
	RETURN;
}

void StartGameScreen::startCampaign()
{
	SET_SCOPE;
	int sel = gameTable->getSelectedRow();
	BGCPString gameName, startMethod, executableFN, parameters, gamePath, levelFN;
	if (getString (gameLauncherTableID, sel, 0, gameName)) {
		Config* config = getConfig();
		int found = -1;

		// Find settings
		SET_OP;
		for (size_t i = 0; i < config->getKeyCount(); ++i) {
			SET_OP;
			BGCPString key = config->getKey(i);
			BGCPString value = config->getValue(i);
			if (key == "GameName" && value == gameName) {
				found = (int) i;
			}
		}

		// Retrieve settings
		SET_OP;
		MechShell* shell = nullptr;
		BGCPString mechVMRootDir = getDataDir();
		if (found >= 0) {
			for (size_t i = found+1; i < config->getKeyCount(); ++i) {
				SET_OP;
				BGCPString key = config->getKey(i);
				BGCPString value = config->getValue(i);

				if (key == "StartMethod") {
					startMethod = value;
					if (value == "InternalShell")
						shell = new MechShell(getLeft(), getTop(), getWidth(), getHeight());
					#ifdef _WIN32
				} else if (key == "compatibility") {
					SetEnvironmentVariable(L"__COMPAT_LAYER", value.getAsPath());
					#endif
				} else if (key == "archive") {
					if (shell != NULL) {
						if (!shell->loadArchive(value.getAsPath())) {
							COUT << "Unable to open ";
							value.printLn();
						}
					} else {
						COUT << "Cannot set archive before StartMethod InternalShell "
							"has been stated" << std::endl;
						COUT << "Ignored: ";
						value.printLn();
					}
				} else if (key == "GamePath") {
					gamePath = value.replace("%mechVMRootDir%", mechVMRootDir);
				} else if (key == "CampaignFile") {
					if (shell != NULL) {
						if (!shell->loadCampaign(value.getAsPath())) {
							COUT << "Unable to load campaign file ";
							value.print();
							COUT << " - aborting" << std::endl;
							delete shell;
							shell = NULL;
						}
					} else {
						COUT << "Cannot set campaign file before StartMethod InternalShell "
							"has been stated" << std::endl << "Ignored: ";
						value.printLn();
					}
				} else if (key == "SetShellVar") {
					if (shell != NULL) {
						shell->processAssignment(value, false);
					} else {
						COUT << "Cannot set variables before StartMethod InternalShell "
							<< "has been stated " << std::endl << "Ignored: ";
						value.printLn();
					}
				} else if (key == "ExecutableFN") {
					executableFN = value.replace("%mechVMRootDir%", mechVMRootDir);
				} else if (key == "parameters") {
					parameters = value.replace("%mechVMRootDir%", mechVMRootDir);
				} else if (key == "LevelFN") {
					levelFN = value.replace("%mechVMRootDir%", mechVMRootDir);
				} else if (key == "GameName") {
					// New "GameName" entry concludes previous game definition
					i = config->getKeyCount();
				}
			}
		}
		else
			COUT << "Internal Error - unknown game name" << std::endl;

		SET_OP;
		if (shell != NULL) {
			SET_OP;
			shell->setReturnScreen(this);
			if (!shell->startCampaign (gameName)) {
				SET_OP;
				safeDelete(shell);
				getMechVm()->setScreen(this);
				label->setTitle("Failed to launch campagin");
			}
		} else {
			if (startMethod == "RunNative") {
				SET_OP;
				BGCPString cmdLine = "\"";
				cmdLine += executableFN;
				cmdLine += "\" ";
				cmdLine += parameters;

				#ifdef _WIN32
				BGProcess process;
				if (process.run(gamePath, cmdLine)) {
					BGCPString msg = "Process ended with code "; 
					msg += BGCPString::fromInt(process.getExitCode());
					label->setTitle (msg);
				} else {
					BGCPString str = "Invocation failed: ";
					str += cmdLine;
					str += ", ";
					str += BGCPString::getOSError();
					str.printLn();
					label->setTitle(str);
				}

				SetEnvironmentVariable(L"__COMPAT_LAYER", L""); 
				#endif
			}
		}
	}
	RETURN;
}


void StartGameScreen::editLevel()
{
	SET_SCOPE;
	int sel = gameTable->getSelectedRow();
	BGCPString gameName, startMethod, executableFN, parameters, gamePath, levelFN;
	if (getString(gameLauncherTableID, sel, 0, gameName)) {
		Config* config = getConfig();
		int found = -1;

		// Find settings
		for (size_t i = 0; i < config->getKeyCount(); ++i) {
			BGCPString key = config->getKey(i);
			BGCPString value = config->getValue(i);
			if (key == "GameName" && value == gameName) {
				found = (int)i;
			}
		}

		// Retrieve settings
		if (found >= 0) {
			for (size_t i = found+1; i < config->getKeyCount(); ++i) {
				BGCPString key = config->getKey(i);
				BGCPString value = config->getValue(i);

				if (key == "StartMethod") {
					startMethod = value;
					if (value == "InternalShell") {
						new MechShell(getLeft(), getTop(), getWidth(), getHeight());
					}
				} else if (key == "GamePath") {
					gamePath = value;
				} else if (key == "LevelFN") {
					levelFN = BGCPString(value.getAsPath(), CP_SYSTEM);
				} else if (key == "GameName") {
					// New "GameName" entry concludes previous game definition
					i = config->getKeyCount();
				} 
			}
		} else {
			COUT << "Internal Error - unknown game name" << std::endl;
		}
	}

	if (startMethod == "StartLevel") {
		LevelEd* levelEdit = new LevelEd();
		levelEdit->loadLevel(gamePath.getAsPath(), levelFN.getAsPath());
		getMechVm()->setScreen(levelEdit);
	} else {
		label->setTitle("Cannot edit this level");
	}

	RETURN;
}

void StartGameScreen::setGeometry (int _left, int _top, int _width, int _height)
{
	SET_SCOPE;

	if (gameTable != nullptr) {
		SET_OP;
		gameTable->setGeometry(margin, 80, _width-2*margin, _height-5*margin-3*getDefaultButtonHeight());
	}

	if (mPlayGameButton != nullptr) {
		SET_OP;
		mPlayGameButton->setGeometry(margin, getHeight()-2*margin-getDefaultButtonHeight(), 200, getDefaultButtonHeight());
	}

	if (mEditLevelButton != nullptr) {
		SET_OP;
		mEditLevelButton->setGeometry(2*margin+200, getHeight()-2*margin-getDefaultButtonHeight(), 200, getDefaultButtonHeight());
	}

	SET_OP;
	GLWindow::setGeometry(_left, _top, _width, _height);
	RETURN;
}
