// This file is part of ZetaGlest <https://github.com/ZetaGlest>
//
// Copyright (C) 2018  The ZetaGlest team
//
// ZetaGlest is a fork of MegaGlest <https://megaglest.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>

#include "menu_state_scenario.h"

#include "renderer.h"
#include "menu_state_new_game.h"
#include "sound_renderer.h"
#include "core_data.h"
#include "menu_state_options.h"
#include "network_manager.h"
#include "config.h"
#include "auto_test.h"
#include "game.h"

#include "leak_dumper.h"

namespace Game {
	using namespace::Shared::Xml;

	// =====================================================
	//      class MenuStateScenario
	// =====================================================

	MenuStateScenario::MenuStateScenario(Program * program,
		MainMenu * mainMenu,
		bool isTutorialMode,
		const vector < string > &dirList,
		string
		autoloadScenarioName) :MenuState
		(program, mainMenu, "scenario") {
		containerName = "Scenario";
		this->isTutorialMode = isTutorialMode;

		enableScenarioTexturePreview =
			Config::getInstance().getBool("EnableScenarioTexturePreview",
				"true");
		scenarioLogoTexture = NULL;
		previewLoadDelayTimer = time(NULL);
		needToLoadTextures = true;

		Lang & lang = Lang::getInstance();
		NetworkManager & networkManager = NetworkManager::getInstance();
		try {
			networkManager.init(nrServer);
		} catch (const std::exception & ex) {
			char szBuf[8096] = "";
			snprintf(szBuf, 8096, "In [%s::%s %d] Error detected:\n%s\n",
				extractFileFromDirectoryPath(__FILE__).c_str(),
				__FUNCTION__, __LINE__, ex.what());
			SystemFlags::OutputDebug(SystemFlags::debugError, szBuf);
			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).
				enabled)
				SystemFlags::OutputDebug(SystemFlags::debugSystem, "%s", szBuf);

			mainMessageBoxState = 1;
			showMessageBox("Error: " + string(ex.what()), "Error detected",
				false);
		}

		mainMessageBox.registerGraphicComponent(containerName,
			"mainMessageBox");
		mainMessageBox.init(lang.getString("Ok"));
		mainMessageBox.setEnabled(false);
		mainMessageBoxState = 0;

		this->autoloadScenarioName = autoloadScenarioName;
		vector < string > results;

		this->dirList = dirList;

		int buttonStartY = 50;
		int buttonStartX = 70;

		buttonReturn.registerGraphicComponent(containerName, "buttonReturn");
		buttonReturn.init(buttonStartX, buttonStartY, 125);
		buttonReturn.setText(lang.getString("Return"));

		buttonPlayNow.registerGraphicComponent(containerName, "buttonPlayNow");
		buttonPlayNow.init(buttonStartX + 150, buttonStartY, 125);
		buttonPlayNow.setText(lang.getString("PlayNow"));

		int startY = 700;
		int startX = 50;

		labelScenario.registerGraphicComponent(containerName, "labelScenario");
		labelScenario.init(startX, startY);

		listBoxScenario.registerGraphicComponent(containerName,
			"listBoxScenario");
		listBoxScenario.init(startX, startY - 30, 290);

		labelScenarioName.registerGraphicComponent(containerName,
			"labelScenarioName");
		labelScenarioName.init(startX, startY - 80);
		labelScenarioName.setFont(CoreData::getInstance().getMenuFontBig());
		labelScenarioName.setFont3D(CoreData::getInstance().
			getMenuFontBig3D());

		labelInfo.registerGraphicComponent(containerName, "labelInfo");
		labelInfo.init(startX, startY - 110);
		labelInfo.setFont(CoreData::getInstance().getMenuFontNormal());
		labelInfo.setFont3D(CoreData::getInstance().getMenuFontNormal3D());

		if (this->isTutorialMode == true) {
			labelScenario.setText(lang.getString("Tutorial"));
		} else {
			labelScenario.setText(lang.getString("Scenario"));
		}

		//scenario listbox
		findDirs(dirList, results);
		scenarioFiles = results;
		//printf("scenarioFiles[0] [%s]\n",scenarioFiles[0].c_str());

		if (results.empty() == true) {
			//throw game_runtime_error("There are no scenarios found to load");
			mainMessageBoxState = 1;
			if (this->isTutorialMode == true) {
				showMessageBox("Error: There are no tutorials found to load",
					"Error detected", false);
			} else {
				showMessageBox("Error: There are no scenarios found to load",
					"Error detected", false);
			}
		}

		std::map < string, string > scenarioErrors;
		for (int i = 0; i < (int) results.size(); ++i) {
			results[i] = formatString(results[i]);
		}
		listBoxScenario.setItems(results);

		try {
			if (SystemFlags::VERBOSE_MODE_ENABLED)
				printf
				("In [%s::%s Line: %d] listBoxScenario.getSelectedItemIndex() = %d scenarioFiles.size() = %d\n",
					extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__,
					__LINE__, listBoxScenario.getSelectedItemIndex(),
					(int) scenarioFiles.size());

			if (listBoxScenario.getItemCount() > 0
				&& listBoxScenario.getSelectedItemIndex() >= 0
				&& listBoxScenario.getSelectedItemIndex() <
				(int) scenarioFiles.size()) {
				string scenarioPath =
					Scenario::getScenarioPath(dirList,
						scenarioFiles[listBoxScenario.
						getSelectedItemIndex
						()]);
				//printf("scenarioPath [%s]\n",scenarioPath.c_str());

				loadScenarioInfo(scenarioPath, &scenarioInfo);
				labelInfo.setText(scenarioInfo.desc);
				if (scenarioInfo.namei18n != "") {
					labelScenarioName.setText(scenarioInfo.namei18n);
				} else {
					labelScenarioName.setText(listBoxScenario.getSelectedItem());
				}
			}

			GraphicComponent::applyAllCustomProperties(containerName);
		} catch (const std::exception & ex) {
			char szBuf[8096] = "";
			snprintf(szBuf, 8096, "In [%s::%s %d] Error detected:\n%s\n",
				extractFileFromDirectoryPath(__FILE__).c_str(),
				__FUNCTION__, __LINE__, ex.what());
			SystemFlags::OutputDebug(SystemFlags::debugError, szBuf);
			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).
				enabled)
				SystemFlags::OutputDebug(SystemFlags::debugSystem, "%s", szBuf);

			mainMessageBoxState = 1;
			showMessageBox("Error: " + string(ex.what()), "Error detected",
				false);
		}

		if (scenarioErrors.empty() == false) {
			mainMessageBoxState = 1;

			string errorMsg = "";
			for (std::map < string, string >::iterator iterMap =
				scenarioErrors.begin(); iterMap != scenarioErrors.end();
				++iterMap) {
				errorMsg +=
					"scenario: " + iterMap->first + " error text: " +
					iterMap->second.substr(0, 400) + "\n";
			}
			showMessageBox("Error loading scenario(s): " + errorMsg,
				"Error detected", false);
		}
	}

	void MenuStateScenario::reloadUI() {
		Lang & lang = Lang::getInstance();

		console.resetFonts();
		mainMessageBox.init(lang.getString("Ok"));
		labelInfo.setFont(CoreData::getInstance().getMenuFontNormal());
		labelInfo.setFont3D(CoreData::getInstance().getMenuFontNormal3D());

		labelScenarioName.setFont(CoreData::getInstance().
			getMenuFontNormal());
		labelScenarioName.setFont3D(CoreData::getInstance().
			getMenuFontNormal3D());

		buttonReturn.setText(lang.getString("Return"));
		buttonPlayNow.setText(lang.getString("PlayNow"));

		labelScenario.setText(lang.getString("Scenario"));

		GraphicComponent::
			reloadFontsForRegisterGraphicComponents(containerName);
	}

	MenuStateScenario::~MenuStateScenario() {
		cleanupPreviewTexture();
	}

	void MenuStateScenario::cleanupPreviewTexture() {
		if (SystemFlags::VERBOSE_MODE_ENABLED)
			printf("In [%s::%s Line: %d] scenarioLogoTexture [%p]\n",
				extractFileFromDirectoryPath(__FILE__).c_str(),
				__FUNCTION__, __LINE__, scenarioLogoTexture);

		if (scenarioLogoTexture != NULL) {
			Renderer::getInstance().endTexture(rsGlobal, scenarioLogoTexture,
				false);
		}
		scenarioLogoTexture = NULL;
	}

	void MenuStateScenario::mouseClick(int x, int y, MouseButton mouseButton) {
		CoreData & coreData = CoreData::getInstance();
		SoundRenderer & soundRenderer = SoundRenderer::getInstance();
		string advanceToItemStartingWith = "";

		if (mainMessageBox.getEnabled()) {
			int button = 0;
			if (mainMessageBox.mouseClick(x, y, button)) {
				soundRenderer.playFx(coreData.getClickSoundA());
				if (button == 0) {
					mainMessageBox.setEnabled(false);

					if (scenarioFiles.empty() == true && mainMessageBoxState == 1) {
						mainMenu->setState(new MenuStateNewGame(program, mainMenu));
						return;
					}
				}
			}
			return;
		} else {
			if (::Shared::Platform::Window::isKeyStateModPressed(KMOD_SHIFT) ==
				true) {
				const wchar_t lastKey = ::Shared::Platform::Window::
					extractLastKeyPressed();
				//                      xxx:
				//                      string hehe=lastKey;
				//                      printf("lastKey = %d [%c] '%s'\n",lastKey,lastKey,hehe);
				advanceToItemStartingWith = lastKey;
			}
		}

		if (buttonReturn.mouseClick(x, y)) {
			soundRenderer.playFx(coreData.getClickSoundA());
			mainMenu->setState(new MenuStateNewGame(program, mainMenu));
			return;
		} else if (buttonPlayNow.mouseClick(x, y)) {
			soundRenderer.playFx(coreData.getClickSoundC());
			launchGame();
			return;
		} else if (listBoxScenario.mouseClick(x, y, advanceToItemStartingWith)) {
			try {
				if (SystemFlags::VERBOSE_MODE_ENABLED)
					printf
					("In [%s::%s Line: %d] listBoxScenario.getSelectedItemIndex() = %d scenarioFiles.size() = %d\n",
						extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__,
						__LINE__, listBoxScenario.getSelectedItemIndex(),
						(int) scenarioFiles.size());

				if (listBoxScenario.getItemCount() > 0
					&& listBoxScenario.getSelectedItemIndex() >= 0
					&& listBoxScenario.getSelectedItemIndex() <
					(int) scenarioFiles.size()) {
					loadScenarioInfo(Scenario::
						getScenarioPath(dirList,
							scenarioFiles[listBoxScenario.
							getSelectedItemIndex
							()]),
						&scenarioInfo);
					labelInfo.setText(scenarioInfo.desc);
					if (scenarioInfo.namei18n != "") {
						labelScenarioName.setText(scenarioInfo.namei18n);
					} else {
						labelScenarioName.setText(listBoxScenario.getSelectedItem());
					}
				}
			} catch (const std::exception & ex) {
				char szBuf[8096] = "";
				snprintf(szBuf, 8096, "In [%s::%s %d] Error detected:\n%s\n",
					extractFileFromDirectoryPath(__FILE__).c_str(),
					__FUNCTION__, __LINE__, ex.what());
				SystemFlags::OutputDebug(SystemFlags::debugError, szBuf);
				if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).
					enabled)
					SystemFlags::OutputDebug(SystemFlags::debugSystem, "%s", szBuf);

				mainMessageBoxState = 1;
				showMessageBox("Error: " + string(ex.what()), "Error detected",
					false);
			}
		}
	}

	void MenuStateScenario::mouseMove(int x, int y, const MouseState * ms) {

		if (mainMessageBox.getEnabled()) {
			mainMessageBox.mouseMove(x, y);
		}

		listBoxScenario.mouseMove(x, y);

		buttonReturn.mouseMove(x, y);
		buttonPlayNow.mouseMove(x, y);
	}

	void MenuStateScenario::render() {

		Renderer & renderer = Renderer::getInstance();

		if (scenarioLogoTexture != NULL) {
			renderer.renderTextureQuad(450, 200, 533, 400, scenarioLogoTexture, NULL);
			//renderer.renderBackground(scenarioLogoTexture);
		}

		if (mainMessageBox.getEnabled()) {
			renderer.renderMessageBox(&mainMessageBox);
		} else {
			renderer.renderLabel(&labelInfo);
			renderer.renderLabel(&labelScenarioName);

			renderer.renderLabel(&labelScenario);
			renderer.renderListBox(&listBoxScenario);

			renderer.renderButton(&buttonReturn);
			renderer.renderButton(&buttonPlayNow);
		}
		renderer.renderConsole(&console);
		if (program != NULL)
			program->renderProgramMsgBox();
	}

	void MenuStateScenario::update() {
		if (Config::getInstance().getBool("AutoTest")) {
			AutoTest::getInstance().updateScenario(this);
			return;
		}
		if (this->autoloadScenarioName != "") {
			string scenarioPath =
				Scenario::getScenarioPath(dirList, this->autoloadScenarioName);

			//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("[%s:%s] Line: %d this->autoloadScenarioName [%s] scenarioPath [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,this->autoloadScenarioName.c_str(),scenarioPath.c_str());
			printf
			("[%s:%s] Line: %d this->autoloadScenarioName [%s] scenarioPath [%s]\n",
				extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__,
				__LINE__, this->autoloadScenarioName.c_str(),
				scenarioPath.c_str());

			loadScenarioInfo(scenarioPath, &scenarioInfo);
			//if(scenarioInfo.namei18n != "") {
			//      this->autoloadScenarioName = scenarioInfo.namei18n;
			//}
			//else {
			this->autoloadScenarioName =
				formatString(this->autoloadScenarioName);
			//}

			//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("[%s:%s] Line: %d this->autoloadScenarioName [%s] scenarioPath [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,this->autoloadScenarioName.c_str(),scenarioPath.c_str());
			printf
			("[%s:%s] Line: %d this->autoloadScenarioName [%s] scenarioPath [%s] file [%s]\n",
				extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__,
				__LINE__, this->autoloadScenarioName.c_str(),
				scenarioPath.c_str(), scenarioInfo.file.c_str());

			listBoxScenario.setSelectedItem(this->autoloadScenarioName, false);

			if (listBoxScenario.getSelectedItem() != this->autoloadScenarioName) {
				mainMessageBoxState = 1;
				showMessageBox("Could not find scenario name: " +
					this->autoloadScenarioName, "Scenario Missing",
					false);
				this->autoloadScenarioName = "";
			} else {
				try {
					this->autoloadScenarioName = "";
					if (listBoxScenario.getItemCount() > 0
						&& listBoxScenario.getSelectedItemIndex() >= 0
						&& listBoxScenario.getSelectedItemIndex() <
						(int) scenarioFiles.size()) {

						printf
						("[%s:%s] Line: %d scenarioFiles[listBoxScenario.getSelectedItemIndex()] [%s]\n",
							extractFileFromDirectoryPath(__FILE__).c_str(),
							__FUNCTION__, __LINE__,
							scenarioFiles[listBoxScenario.getSelectedItemIndex()].
							c_str());

						loadScenarioInfo(Scenario::
							getScenarioPath(dirList,
								scenarioFiles
								[listBoxScenario.
								getSelectedItemIndex()]),
							&scenarioInfo);

						printf("[%s:%s] Line: %d scenarioInfo.file [%s]\n",
							extractFileFromDirectoryPath(__FILE__).c_str(),
							__FUNCTION__, __LINE__, scenarioInfo.file.c_str());

						labelInfo.setText(scenarioInfo.desc);
						if (scenarioInfo.namei18n != "") {
							labelScenarioName.setText(scenarioInfo.namei18n);
						} else {
							labelScenarioName.setText(listBoxScenario.
								getSelectedItem());
						}

						SoundRenderer & soundRenderer = SoundRenderer::getInstance();
						CoreData & coreData = CoreData::getInstance();
						soundRenderer.playFx(coreData.getClickSoundC());
						launchGame();
						return;
					}
				} catch (const std::exception & ex) {
					char szBuf[8096] = "";
					snprintf(szBuf, 8096, "In [%s::%s %d] Error detected:\n%s\n",
						extractFileFromDirectoryPath(__FILE__).c_str(),
						__FUNCTION__, __LINE__, ex.what());
					SystemFlags::OutputDebug(SystemFlags::debugError, szBuf);
					if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).
						enabled)
						SystemFlags::OutputDebug(SystemFlags::debugSystem, "%s",
							szBuf);

					mainMessageBoxState = 1;
					showMessageBox("Error: " + string(ex.what()), "Error detected",
						false);
				}
			}
		}

		if (needToLoadTextures) {
			// this delay is done to make it possible to switch faster
			if (difftime(time(NULL), previewLoadDelayTimer) >= 2) {
				loadScenarioPreviewTexture();
				needToLoadTextures = false;
			}
		}
		console.update();
	}

	void MenuStateScenario::launchGame() {
		if (SystemFlags::VERBOSE_MODE_ENABLED)
			printf("In [%s::%s Line: %d] scenarioInfo.file [%s] [%s][%s][%s]\n",
				extractFileFromDirectoryPath(__FILE__).c_str(),
				__FUNCTION__, __LINE__, scenarioInfo.file.c_str(),
				scenarioInfo.tilesetName.c_str(),
				scenarioInfo.mapName.c_str(),
				scenarioInfo.techTreeName.c_str());

		if (scenarioInfo.file != "" && scenarioInfo.tilesetName != ""
			&& scenarioInfo.mapName != "" && scenarioInfo.techTreeName != "") {
			GameSettings gameSettings;
			loadGameSettings(&scenarioInfo, &gameSettings);

			if (SystemFlags::VERBOSE_MODE_ENABLED)
				printf("In [%s::%s Line: %d] gameSettings.getScenarioDir() [%s]\n",
					extractFileFromDirectoryPath(__FILE__).c_str(),
					__FUNCTION__, __LINE__,
					gameSettings.getScenarioDir().c_str());

			const vector < string > pathTechList =
				Config::getInstance().getPathListForType(ptTechs,
					gameSettings.
					getScenarioDir());
			if (TechTree::exists(gameSettings.getTech(), pathTechList) == false) {
				char szBuf[8096] = "";
				snprintf(szBuf, 8096,
					"Line ref: %d Error: cannot find techtree [%s]\n",
					__LINE__, scenarioInfo.techTreeName.c_str());
				SystemFlags::OutputDebug(SystemFlags::debugError, szBuf);

				mainMessageBoxState = 1;
				showMessageBox(szBuf, "Error detected", false);

				return;
			}
			program->setState(new Game(program, &gameSettings, false));
			return;
		}
	}

	void MenuStateScenario::setScenario(int i) {
		listBoxScenario.setSelectedItemIndex(i);
		loadScenarioInfo(Scenario::
			getScenarioPath(dirList,
				scenarioFiles[listBoxScenario.
				getSelectedItemIndex
				()]), &scenarioInfo);
	}

	void MenuStateScenario::loadScenarioInfo(string file,
		ScenarioInfo * scenarioInfo) {
		bool isTutorial = Scenario::isGameTutorial(file);

		cleanupPreviewTexture();
		needToLoadTextures = false;

		if (Scenario::loadScenarioInfo(file, scenarioInfo, isTutorial) == true) {
			previewLoadDelayTimer = time(NULL);
			needToLoadTextures = true;
		}
	}

	void MenuStateScenario::loadScenarioPreviewTexture() {
		if (enableScenarioTexturePreview == true) {
			//if(listBoxScenario.getSelectedItemIndex() >= 0) {
			if (listBoxScenario.getItemCount() > 0
				&& listBoxScenario.getSelectedItemIndex() >= 0
				&& listBoxScenario.getSelectedItemIndex() <
				(int) scenarioFiles.size()) {
				GameSettings gameSettings;
				loadGameSettings(&scenarioInfo, &gameSettings);

				string scenarioLogo = "";
				bool loadingImageUsed = false;

				Game::extractScenarioLogoFile(&gameSettings, scenarioLogo,
					loadingImageUsed);

				if (SystemFlags::VERBOSE_MODE_ENABLED)
					printf("In [%s::%s Line: %d] scenarioLogo [%s]\n",
						extractFileFromDirectoryPath(__FILE__).c_str(),
						__FUNCTION__, __LINE__, scenarioLogo.c_str());

				if (scenarioLogo != "") {
					cleanupPreviewTexture();
					scenarioLogoTexture = Renderer::findTexture(scenarioLogo);
				} else {
					cleanupPreviewTexture();
					scenarioLogoTexture = NULL;
				}
			}
		}
	}

	void MenuStateScenario::loadGameSettings(const ScenarioInfo *
		scenarioInfo,
		GameSettings * gameSettings) {
		if (listBoxScenario.getSelectedItemIndex() < 0) {
			char szBuf[8096] = "";
			snprintf(szBuf, 8096,
				"listBoxScenario.getSelectedItemIndex() < 0, = %d",
				listBoxScenario.getSelectedItemIndex());
			throw game_runtime_error(szBuf);
		} else if (listBoxScenario.getSelectedItemIndex() >=
			(int) scenarioFiles.size()) {
			char szBuf[8096] = "";
			snprintf(szBuf, 8096,
				"listBoxScenario.getSelectedItemIndex() >= scenarioFiles.size(), = [%d][%d]",
				listBoxScenario.getSelectedItemIndex(),
				(int) scenarioFiles.size());
			throw game_runtime_error(szBuf);
		}

		Scenario::loadGameSettings(dirList, scenarioInfo, gameSettings,
			formatString(scenarioFiles
				[listBoxScenario.
				getSelectedItemIndex()]));
	}

	void MenuStateScenario::showMessageBox(const string & text,
		const string & header,
		bool toggle) {
		if (!toggle) {
			mainMessageBox.setEnabled(false);
		}

		if (!mainMessageBox.getEnabled()) {
			mainMessageBox.setText(text);
			mainMessageBox.setHeader(header);
			mainMessageBox.setEnabled(true);
		} else {
			mainMessageBox.setEnabled(false);
		}
	}

	void MenuStateScenario::keyDown(SDL_KeyboardEvent key) {
		Config & configKeys =
			Config::getInstance(std::pair < ConfigType,
				ConfigType >(cfgMainKeys, cfgUserKeys));
		if (isKeyPressed(configKeys.getSDLKey("SaveGUILayout"), key) == true) {
			GraphicComponent::saveAllCustomProperties(containerName);
		}
	}

} //end namespace
