/*
Copyright © 2012 Clint Bellanger
Copyright © 2012 davidriod
Copyright © 2012 Igor Paliychuk
Copyright © 2012 Stefan Beller
Copyright © 2013 Kurt Rinnert
Copyright © 2014 Henrik Andersson
Copyright © 2014-2016 Justin Jacobs

This file is part of FLARE.

FLARE 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.

FLARE 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
FLARE.  If not, see http://www.gnu.org/licenses/
*/

/**
 * GameStateConfigBase
 *
 * Handle game Settings Menu
 */

#include "CombatText.h"
#include "CommonIncludes.h"
#include "DeviceList.h"
#include "EngineSettings.h"
#include "FileParser.h"
#include "FontEngine.h"
#include "GameStateConfigBase.h"
#include "GameStateTitle.h"
#include "InputState.h"
#include "MenuConfirm.h"
#include "MessageEngine.h"
#include "ModManager.h"
#include "Platform.h"
#include "RenderDevice.h"
#include "Settings.h"
#include "SharedResources.h"
#include "SoundManager.h"
#include "Stats.h"
#include "TooltipManager.h"
#include "UtilsFileSystem.h"
#include "UtilsParsing.h"
#include "Version.h"
#include "WidgetButton.h"
#include "WidgetCheckBox.h"
#include "WidgetListBox.h"
#include "WidgetSlider.h"
#include "WidgetTabControl.h"

GameStateConfigBase::GameStateConfigBase (bool do_init)
	: GameState()
	, child_widget()
	, ok_button(new WidgetButton(WidgetButton::DEFAULT_FILE))
	, defaults_button(new WidgetButton(WidgetButton::DEFAULT_FILE))
	, cancel_button(new WidgetButton(WidgetButton::DEFAULT_FILE))
	, background(NULL)
	, show_fps_cb(new WidgetCheckBox(WidgetCheckBox::DEFAULT_FILE))
	, show_fps_lb(new WidgetLabel())
	, hardware_cursor_cb(new WidgetCheckBox(WidgetCheckBox::DEFAULT_FILE))
	, hardware_cursor_lb(new WidgetLabel())
	, colorblind_cb(new WidgetCheckBox(WidgetCheckBox::DEFAULT_FILE))
	, colorblind_lb(new WidgetLabel())
	, dev_mode_cb(new WidgetCheckBox(WidgetCheckBox::DEFAULT_FILE))
	, dev_mode_lb(new WidgetLabel())
	, subtitles_cb(new WidgetCheckBox(WidgetCheckBox::DEFAULT_FILE))
	, subtitles_lb(new WidgetLabel())
	, music_volume_sl(new WidgetSlider(WidgetSlider::DEFAULT_FILE))
	, music_volume_lb(new WidgetLabel())
	, sound_volume_sl(new WidgetSlider(WidgetSlider::DEFAULT_FILE))
	, sound_volume_lb(new WidgetLabel())
	, activemods_lstb(new WidgetListBox(10, WidgetListBox::DEFAULT_FILE))
	, activemods_lb(new WidgetLabel())
	, inactivemods_lstb(new WidgetListBox(10, WidgetListBox::DEFAULT_FILE))
	, inactivemods_lb(new WidgetLabel())
	, language_lstb(new WidgetListBox(10, WidgetListBox::DEFAULT_FILE))
	, language_lb(new WidgetLabel())
	, activemods_shiftup_btn(new WidgetButton("images/menus/buttons/up.png"))
	, activemods_shiftdown_btn(new WidgetButton("images/menus/buttons/down.png"))
	, activemods_deactivate_btn(new WidgetButton(WidgetButton::DEFAULT_FILE))
	, inactivemods_activate_btn(new WidgetButton(WidgetButton::DEFAULT_FILE))
	, defaults_confirm(new MenuConfirm(msg->get("Defaults"), msg->get("Reset ALL settings?")))
	, active_tab(0)
	, frame(0,0)
	, frame_offset(11,8)
	, tab_offset(3,0)
	, new_render_device(settings->render_device_name)
{

	// don't save settings if we close the game while in this menu
	save_settings_on_exit = false;

	Image *graphics;
	graphics = render_device->loadImage("images/menus/config.png", RenderDevice::ERROR_NORMAL);
	if (graphics) {
		background = graphics->createSprite();
		graphics->unref();
	}

	tab_control = new WidgetTabControl();

	ok_button->setLabel(msg->get("OK"));
	defaults_button->setLabel(msg->get("Defaults"));
	cancel_button->setLabel(msg->get("Cancel"));

	language_lstb->can_deselect = false;

	// Finish Mods ListBoxes setup
	activemods_lstb->multi_select = true;
	for (unsigned int i = 0; i < mods->mod_list.size() ; i++) {
		if (mods->mod_list[i].name != mods->FALLBACK_MOD)
			activemods_lstb->append(mods->mod_list[i].name,createModTooltip(&mods->mod_list[i]));
	}

	inactivemods_lstb->multi_select = true;
	for (unsigned int i = 0; i<mods->mod_dirs.size(); i++) {
		bool skip_mod = false;
		for (unsigned int j = 0; j<mods->mod_list.size(); j++) {
			if (mods->mod_dirs[i] == mods->mod_list[j].name) {
				skip_mod = true;
				break;
			}
		}
		if (!skip_mod && mods->mod_dirs[i] != mods->FALLBACK_MOD) {
			Mod temp_mod = mods->loadMod(mods->mod_dirs[i]);
			inactivemods_lstb->append(mods->mod_dirs[i],createModTooltip(&temp_mod));
		}
	}
	inactivemods_lstb->sort();

	if (do_init) {
		init();
	}
	else {
		// these will be initialized properly by a derevitive class (i.e. GameStateConfigDesktop)
		AUDIO_TAB = 0;
		INTERFACE_TAB = 0;
		MODS_TAB = 0;
	}

	render_device->setBackgroundColor(Color(0,0,0,0));
}

GameStateConfigBase::~GameStateConfigBase() {
	cleanup();
}

void GameStateConfigBase::init() {
	AUDIO_TAB = 0;
	INTERFACE_TAB = 1;
	MODS_TAB = 2;

	tab_control->setTabTitle(AUDIO_TAB, msg->get("Audio"));
	tab_control->setTabTitle(INTERFACE_TAB, msg->get("Interface"));
	tab_control->setTabTitle(MODS_TAB, msg->get("Mods"));

	readConfig();

	addChildWidgets();
	setupTabList();

	refreshWidgets();

	update();
}

void GameStateConfigBase::readConfig() {
	//Load the menu configuration from file

	FileParser infile;
	if (infile.open("menus/config.txt", FileParser::MOD_FILE, FileParser::ERROR_NORMAL)) {
		while (infile.next()) {
			if (parseKeyButtons(infile))
				continue;

			int x1 = Parse::popFirstInt(infile.val);
			int y1 = Parse::popFirstInt(infile.val);
			int x2 = Parse::popFirstInt(infile.val);
			int y2 = Parse::popFirstInt(infile.val);

			if (parseKey(infile, x1, y1, x2, y2))
				continue;
			else if (parseStub(infile))
				continue;
			else {
				infile.error("GameStateConfigBase: '%s' is not a valid key.", infile.key.c_str());
			}
		}
		infile.close();
	}
}

bool GameStateConfigBase::parseKeyButtons(FileParser &infile) {
	// @CLASS GameStateConfigBase|Description of menus/config.txt

	if (infile.key == "button_ok") {
		// @ATTR button_ok|int, int, alignment : X, Y, Alignment|Position of the "OK" button.
		int x = Parse::popFirstInt(infile.val);
		int y = Parse::popFirstInt(infile.val);
		int a = Parse::toAlignment(Parse::popFirstString(infile.val));
		ok_button->setBasePos(x, y, a);
	}
	else if (infile.key == "button_defaults") {
		// @ATTR button_defaults|int, int, alignment : X, Y, Alignment|Position of the "Defaults" button.
		int x = Parse::popFirstInt(infile.val);
		int y = Parse::popFirstInt(infile.val);
		int a = Parse::toAlignment(Parse::popFirstString(infile.val));
		defaults_button->setBasePos(x, y, a);
	}
	else if (infile.key == "button_cancel") {
		// @ATTR button_cancel|int, int, alignment : X, Y, Alignment|Position of the "Cancel" button.
		int x = Parse::popFirstInt(infile.val);
		int y = Parse::popFirstInt(infile.val);
		int a = Parse::toAlignment(Parse::popFirstString(infile.val));
		cancel_button->setBasePos(x, y, a);
	}
	else {
		return false;
	}

	return true;
}
bool GameStateConfigBase::parseKey(FileParser &infile, int &x1, int &y1, int &x2, int &y2) {
	if (infile.key == "listbox_scrollbar_offset") {
		// @ATTR listbox_scrollbar_offset|int|Horizontal offset from the right of listboxes (mods, languages, etc) to place the scrollbar.
		activemods_lstb->scrollbar_offset = x1;
		inactivemods_lstb->scrollbar_offset = x1;
		language_lstb->scrollbar_offset = x1;
	}
	else if (infile.key == "frame_offset") {
		// @ATTR frame_offset|point|Offset for all the widgets contained under each tab.
		frame_offset.x = x1;
		frame_offset.y = y1;
	}
	else if (infile.key == "tab_offset") {
		// @ATTR tab_offset|point|Offset for the row of tabs.
		tab_offset.x = x1;
		tab_offset.y = y1;
	}
	else if (infile.key == "music_volume") {
		// @ATTR music_volume|int, int, int, int : Label X, Label Y, Widget X, Widget Y|Position of the "Music Volume" slider relative to the frame.
		placeLabeledWidget(music_volume_lb, music_volume_sl, x1, y1, x2, y2, msg->get("Music Volume"), FontEngine::JUSTIFY_RIGHT);
	}
	else if (infile.key == "sound_volume") {
		// @ATTR sound_volume|int, int, int, int : Label X, Label Y, Widget X, Widget Y|Position of the "Sound Volume" slider relative to the frame.
		placeLabeledWidget(sound_volume_lb, sound_volume_sl, x1, y1, x2, y2, msg->get("Sound Volume"), FontEngine::JUSTIFY_RIGHT);
	}
	else if (infile.key == "language") {
		// @ATTR language|int, int, int, int : Label X, Label Y, Widget X, Widget Y|Position of the "Language" list box relative to the frame.
		placeLabeledWidget(language_lb, language_lstb, x1, y1, x2, y2, msg->get("Language"));
		language_lb->setJustify(FontEngine::JUSTIFY_CENTER);
	}
	else if (infile.key == "language_height") {
		// @ATTR language_height|int|Number of visible rows for the "Language" list box.
		language_lstb->setHeight(x1);
	}
	else if (infile.key == "show_fps") {
		// @ATTR show_fps|int, int, int, int : Label X, Label Y, Widget X, Widget Y|Position of the "Show FPS" checkbox relative to the frame.
		placeLabeledWidget(show_fps_lb, show_fps_cb, x1, y1, x2, y2, msg->get("Show FPS"), FontEngine::JUSTIFY_RIGHT);
	}
	else if (infile.key == "colorblind") {
		// @ATTR colorblind|int, int, int, int : Label X, Label Y, Widget X, Widget Y|Position of the "Colorblind Mode" checkbox relative to the frame.
		placeLabeledWidget(colorblind_lb, colorblind_cb, x1, y1, x2, y2, msg->get("Colorblind Mode"), FontEngine::JUSTIFY_RIGHT);
	}
	else if (infile.key == "hardware_cursor") {
		// @ATTR hardware_cursor|int, int, int, int : Label X, Label Y, Widget X, Widget Y|Position of the "Hardware mouse cursor" checkbox relative to the frame.
		placeLabeledWidget(hardware_cursor_lb, hardware_cursor_cb, x1, y1, x2, y2, msg->get("Hardware mouse cursor"), FontEngine::JUSTIFY_RIGHT);
	}
	else if (infile.key == "dev_mode") {
		// @ATTR dev_mode|int, int, int, int : Label X, Label Y, Widget X, Widget Y|Position of the "Developer Mode" checkbox relative to the frame.
		placeLabeledWidget(dev_mode_lb, dev_mode_cb, x1, y1, x2, y2, msg->get("Developer Mode"), FontEngine::JUSTIFY_RIGHT);
	}
	else if (infile.key == "subtitles") {
		// @ATTR subtitles|int, int, int, int : Label X, Label Y, Widget X, Widget Y|Position of the "Subtitles" checkbox relative to the frame.
		placeLabeledWidget(subtitles_lb, subtitles_cb, x1, y1, x2, y2, msg->get("Subtitles"), FontEngine::JUSTIFY_RIGHT);
	}
	else if (infile.key == "activemods") {
		// @ATTR activemods|int, int, int, int : Label X, Label Y, Widget X, Widget Y|Position of the "Active Mods" list box relative to the frame.
		placeLabeledWidget(activemods_lb, activemods_lstb, x1, y1, x2, y2, msg->get("Active Mods"));
		activemods_lb->setJustify(FontEngine::JUSTIFY_CENTER);
	}
	else if (infile.key == "activemods_height") {
		// @ATTR activemods_height|int|Number of visible rows for the "Active Mods" list box.
		activemods_lstb->setHeight(x1);
	}
	else if (infile.key == "inactivemods") {
		// @ATTR inactivemods|int, int, int, int : Label X, Label Y, Widget X, Widget Y|Position of the "Available Mods" list box relative to the frame.
		placeLabeledWidget(inactivemods_lb, inactivemods_lstb, x1, y1, x2, y2, msg->get("Available Mods"));
		inactivemods_lb->setJustify(FontEngine::JUSTIFY_CENTER);
	}
	else if (infile.key == "inactivemods_height") {
		// @ATTR inactivemods_height|int|Number of visible rows for the "Available Mods" list box.
		inactivemods_lstb->setHeight(x1);
	}
	else if (infile.key == "activemods_shiftup") {
		// @ATTR activemods_shiftup|point|Position of the button to shift mods up in "Active Mods" relative to the frame.
		activemods_shiftup_btn->setBasePos(x1, y1, Utils::ALIGN_TOPLEFT);
		activemods_shiftup_btn->refresh();
	}
	else if (infile.key == "activemods_shiftdown") {
		// @ATTR activemods_shiftdown|point|Position of the button to shift mods down in "Active Mods" relative to the frame.
		activemods_shiftdown_btn->setBasePos(x1, y1, Utils::ALIGN_TOPLEFT);
		activemods_shiftdown_btn->refresh();
	}
	else if (infile.key == "activemods_deactivate") {
		// @ATTR activemods_deactivate|point|Position of the "Disable" button relative to the frame.
		activemods_deactivate_btn->setLabel(msg->get("<< Disable"));
		activemods_deactivate_btn->setBasePos(x1, y1, Utils::ALIGN_TOPLEFT);
		activemods_deactivate_btn->refresh();
	}
	else if (infile.key == "inactivemods_activate") {
		// @ATTR inactivemods_activate|point|Position of the "Enable" button relative to the frame.
		inactivemods_activate_btn->setLabel(msg->get("Enable >>"));
		inactivemods_activate_btn->setBasePos(x1, y1, Utils::ALIGN_TOPLEFT);
		inactivemods_activate_btn->refresh();
	}
	else {
		return false;
	}

	return true;
}

bool GameStateConfigBase::parseStub(FileParser &infile) {
	// not used for base configuration
	// checking them here prevents getting an "invalid key" warning
	if (infile.key == "renderer");
	else if (infile.key == "renderer_height");
	else if (infile.key == "fullscreen");
	else if (infile.key == "mouse_move");
	else if (infile.key == "hwsurface");
	else if (infile.key == "vsync");
	else if (infile.key == "texture_filter");
	else if (infile.key == "enable_joystick");
	else if (infile.key == "change_gamma");
	else if (infile.key == "mouse_aim");
	else if (infile.key == "no_mouse");
	else if (infile.key == "gamma");
	else if (infile.key == "joystick_deadzone");
	else if (infile.key == "resolution");
	else if (infile.key == "joystick_device");
	else if (infile.key == "joystick_device_height");
	else if (infile.key == "hws_note");
	else if (infile.key == "dbuf_note");
	else if (infile.key == "test_note");
	else if (infile.key == "handheld_note");
	else if (infile.key == "secondary_offset");
	else if (infile.key == "keybinds_bg_color");
	else if (infile.key == "keybinds_bg_alpha");
	else if (infile.key == "scrollpane");
	else if (infile.key == "scrollpane_contents");
	else if (infile.key == "cancel");
	else if (infile.key == "accept");
	else if (infile.key == "up");
	else if (infile.key == "down");
	else if (infile.key == "left");
	else if (infile.key == "right");
	else if (infile.key == "bar1");
	else if (infile.key == "bar2");
	else if (infile.key == "bar3");
	else if (infile.key == "bar4");
	else if (infile.key == "bar5");
	else if (infile.key == "bar6");
	else if (infile.key == "bar7");
	else if (infile.key == "bar8");
	else if (infile.key == "bar9");
	else if (infile.key == "bar0");
	else if (infile.key == "main1");
	else if (infile.key == "main2");
	else if (infile.key == "character");
	else if (infile.key == "inventory");
	else if (infile.key == "powers");
	else if (infile.key == "log");
	else if (infile.key == "ctrl");
	else if (infile.key == "shift");
	else if (infile.key == "alt");
	else if (infile.key == "delete");
	else if (infile.key == "actionbar");
	else if (infile.key == "actionbar_back");
	else if (infile.key == "actionbar_forward");
	else if (infile.key == "actionbar_use");
	else if (infile.key == "developer_menu");
	else return false;

	return true;
}

void GameStateConfigBase::addChildWidgets() {
	addChildWidget(music_volume_sl, AUDIO_TAB);
	addChildWidget(music_volume_lb, AUDIO_TAB);
	addChildWidget(sound_volume_sl, AUDIO_TAB);
	addChildWidget(sound_volume_lb, AUDIO_TAB);

	addChildWidget(show_fps_cb, INTERFACE_TAB);
	addChildWidget(show_fps_lb, INTERFACE_TAB);
	addChildWidget(colorblind_cb, INTERFACE_TAB);
	addChildWidget(colorblind_lb, INTERFACE_TAB);
	addChildWidget(hardware_cursor_cb, INTERFACE_TAB);
	addChildWidget(hardware_cursor_lb, INTERFACE_TAB);
	addChildWidget(dev_mode_cb, INTERFACE_TAB);
	addChildWidget(dev_mode_lb, INTERFACE_TAB);
	addChildWidget(subtitles_cb, INTERFACE_TAB);
	addChildWidget(subtitles_lb, INTERFACE_TAB);
	addChildWidget(language_lstb, INTERFACE_TAB);
	addChildWidget(language_lb, INTERFACE_TAB);

	addChildWidget(activemods_lstb, MODS_TAB);
	addChildWidget(activemods_lb, MODS_TAB);
	addChildWidget(inactivemods_lstb, MODS_TAB);
	addChildWidget(inactivemods_lb, MODS_TAB);
	addChildWidget(activemods_shiftup_btn, MODS_TAB);
	addChildWidget(activemods_shiftdown_btn, MODS_TAB);
	addChildWidget(activemods_deactivate_btn, MODS_TAB);
	addChildWidget(inactivemods_activate_btn, MODS_TAB);
}

void GameStateConfigBase::setupTabList() {
	tablist.add(tab_control);
	tablist.setPrevTabList(&tablist_main);

	tablist_main.add(ok_button);
	tablist_main.add(defaults_button);
	tablist_main.add(cancel_button);
	tablist_main.setPrevTabList(&tablist);
	tablist_main.setNextTabList(&tablist);
	tablist_main.lock();

	tablist_audio.add(music_volume_sl);
	tablist_audio.add(sound_volume_sl);
	tablist_audio.setPrevTabList(&tablist);
	tablist_audio.setNextTabList(&tablist_main);
	tablist_audio.lock();

	tablist_interface.add(show_fps_cb);
	tablist_interface.add(colorblind_cb);
	tablist_interface.add(hardware_cursor_cb);
	tablist_interface.add(dev_mode_cb);
	tablist_interface.add(subtitles_cb);
	tablist_interface.add(language_lstb);
	tablist_interface.setPrevTabList(&tablist);
	tablist_interface.setNextTabList(&tablist_main);
	tablist_interface.lock();

	tablist_mods.add(inactivemods_lstb);
	tablist_mods.add(activemods_lstb);
	tablist_mods.add(inactivemods_activate_btn);
	tablist_mods.add(activemods_deactivate_btn);
	tablist_mods.add(activemods_shiftup_btn);
	tablist_mods.add(activemods_shiftdown_btn);
	tablist_mods.setPrevTabList(&tablist);
	tablist_mods.setNextTabList(&tablist_main);
	tablist_mods.lock();
}

void GameStateConfigBase::update() {
	updateAudio();
	updateInterface();
	updateMods();
}

void GameStateConfigBase::updateAudio() {
	if (settings->audio) {
		music_volume_sl->set(0, 128, settings->music_volume);
		snd->setVolumeMusic(settings->music_volume);
		sound_volume_sl->set(0, 128, settings->sound_volume);
		snd->setVolumeSFX(settings->sound_volume);
	}
	else {
		music_volume_sl->set(0,128,0);
		sound_volume_sl->set(0,128,0);
	}
}

void GameStateConfigBase::updateInterface() {
	show_fps_cb->setChecked(settings->show_fps);
	colorblind_cb->setChecked(settings->colorblind);
	hardware_cursor_cb->setChecked(settings->hardware_cursor);
	dev_mode_cb->setChecked(settings->dev_mode);
	subtitles_cb->setChecked(settings->subtitles);

	refreshLanguages();
}

void GameStateConfigBase::updateMods() {
	activemods_lstb->refresh();
	inactivemods_lstb->refresh();
}

void GameStateConfigBase::logic() {
	if (inpt->window_resized)
		refreshWidgets();

	if (defaults_confirm->visible) {
		// reset defaults confirmation
		logicDefaults();
		return;
	}
	else {
		if (!logicMain())
			return;
	}

	// tab contents
	active_tab = tab_control->getActiveTab();

	if (active_tab == AUDIO_TAB) {
		tablist.setNextTabList(&tablist_audio);
		logicAudio();
	}
	else if (active_tab == INTERFACE_TAB) {
		tablist.setNextTabList(&tablist_interface);
		logicInterface();

		if (platform.force_hardware_cursor) {
			// for some platforms, hardware mouse cursor can not be turned off
			settings->hardware_cursor = true;
			hardware_cursor_cb->setChecked(settings->hardware_cursor);
		}
	}
	else if (active_tab == MODS_TAB) {
		tablist.setNextTabList(&tablist_mods);
		logicMods();
	}
}

bool GameStateConfigBase::logicMain() {
	for (unsigned int i = 0; i < child_widget.size(); i++) {
		if (child_widget[i]->in_focus) {
			tab_control->setActiveTab(optiontab[i]);
			break;
		}
	}

	// tabs & the bottom 3 main buttons
	tab_control->logic();
	tablist.logic();
	tablist_main.logic();
	tablist_audio.logic();
	tablist_interface.logic();
	tablist_mods.logic();

	// Ok/Cancel Buttons
	if (ok_button->checkClick()) {
		logicAccept();

		// GameStateConfigBase deconstructed, proceed with caution
		return false;
	}
	else if (defaults_button->checkClick()) {
		defaults_confirm->visible = true;
		return true;
	}
	else if (cancel_button->checkClick() || (inpt->pressing[Input::CANCEL] && !inpt->lock[Input::CANCEL])) {
		logicCancel();

		// GameStateConfigBase deconstructed, proceed with caution
		return false;
	}

	return true;
}

void GameStateConfigBase::logicDefaults() {
	defaults_confirm->logic();
	if (defaults_confirm->confirmClicked) {
		settings->fullscreen = false;
		settings->loadDefaults();
		eset->load();
		inpt->defaultQwertyKeyBindings();
		inpt->defaultJoystickBindings();
		update();
		render_device->windowResize();
		defaults_confirm->visible = false;
		defaults_confirm->confirmClicked = false;
	}
}

void GameStateConfigBase::logicAccept() {
	if (setMods()) {
		snd->unloadMusic();
		reload_music = true;
		reload_backgrounds = true;
		delete mods;
		mods = new ModManager(NULL);
		settings->prev_save_slot = -1;
	}
	delete msg;
	msg = new MessageEngine();
	inpt->saveKeyBindings();
	inpt->setKeybindNames();
	eset->load();
	Stats::init();
	refreshFont();
	if ((settings->enable_joystick) && (inpt->getNumJoysticks() > 0)) {
		inpt->initJoystick();
	}
	cleanup();

	showLoading();
	// need to delete the "Loading..." message here, as we're recreating our render context
	if (loading_tip) {
		delete loading_tip;
		loading_tip = NULL;
	}

	delete tooltipm;

	// we can't replace the render device in-place, so soft-reset the game
	if (new_render_device != settings->render_device_name) {
		settings->render_device_name = new_render_device;
		inpt->done = true;
		settings->soft_reset = true;
	}

	render_device->createContext();
	tooltipm = new TooltipManager();
	settings->saveSettings();
	setRequestedGameState(new GameStateTitle());
}

void GameStateConfigBase::logicCancel() {
	inpt->lock[Input::CANCEL] = true;
	settings->loadSettings();
	inpt->loadKeyBindings();
	delete msg;
	msg = new MessageEngine();
	inpt->setKeybindNames();
	eset->load();
	Stats::init();
	refreshFont();
	update();
	cleanup();
	render_device->windowResize();
	render_device->updateTitleBar();
	showLoading();
	setRequestedGameState(new GameStateTitle());
}

void GameStateConfigBase::logicAudio() {
	if (settings->audio) {
		if (music_volume_sl->checkClick()) {
			if (settings->music_volume == 0)
				reload_music = true;
			settings->music_volume = static_cast<short>(music_volume_sl->getValue());
			snd->setVolumeMusic(settings->music_volume);
		}
		else if (sound_volume_sl->checkClick()) {
			settings->sound_volume = static_cast<short>(sound_volume_sl->getValue());
			snd->setVolumeSFX(settings->sound_volume);
		}
	}
}

void GameStateConfigBase::logicInterface() {
	if (language_lstb->checkClick()) {
		int lang_id = language_lstb->getSelected();
		if (lang_id != -1)
			settings->language = language_ISO[lang_id];
	}
	else if (show_fps_cb->checkClick()) {
		settings->show_fps = show_fps_cb->isChecked();
	}
	else if (colorblind_cb->checkClick()) {
		settings->colorblind = colorblind_cb->isChecked();
	}
	else if (hardware_cursor_cb->checkClick()) {
		settings->hardware_cursor = hardware_cursor_cb->isChecked();
	}
	else if (dev_mode_cb->checkClick()) {
		settings->dev_mode = dev_mode_cb->isChecked();
	}
	else if (subtitles_cb->checkClick()) {
		settings->subtitles = subtitles_cb->isChecked();
	}
}

void GameStateConfigBase::logicMods() {
	if (activemods_lstb->checkClick()) {
		//do nothing
	}
	else if (inactivemods_lstb->checkClick()) {
		//do nothing
	}
	else if (activemods_shiftup_btn->checkClick()) {
		activemods_lstb->shiftUp();
	}
	else if (activemods_shiftdown_btn->checkClick()) {
		activemods_lstb->shiftDown();
	}
	else if (activemods_deactivate_btn->checkClick()) {
		disableMods();
	}
	else if (inactivemods_activate_btn->checkClick()) {
		enableMods();
	}
}

void GameStateConfigBase::render() {
	if (requestedGameState != NULL) {
		// we're in the process of switching game states, so skip rendering
		return;
	}

	int tabheight = tab_control->getTabHeight();
	Rect	pos;
	pos.x = (settings->view_w - eset->resolutions.frame_w)/2;
	pos.y = (settings->view_h - eset->resolutions.frame_h)/2 + tabheight - tabheight/16;

	if (background) {
		background->setDestFromRect(pos);
		render_device->render(background);
	}

	tab_control->render();

	// render OK/Defaults/Cancel buttons
	ok_button->render();
	cancel_button->render();
	defaults_button->render();

	renderTabContents();
	renderDialogs();
}

void GameStateConfigBase::renderTabContents() {
	for (unsigned int i = 0; i < child_widget.size(); i++) {
		if (optiontab[i] == active_tab) child_widget[i]->render();
	}

}

void GameStateConfigBase::renderDialogs() {
	if (defaults_confirm->visible)
		defaults_confirm->render();
}

void GameStateConfigBase::placeLabeledWidget(WidgetLabel *lb, Widget *w, int x1, int y1, int x2, int y2, std::string const& str, int justify) {
	if (w) {
		w->setBasePos(x2, y2, Utils::ALIGN_TOPLEFT);
	}

	if (lb) {
		lb->setBasePos(x1, y1, Utils::ALIGN_TOPLEFT);
		lb->setText(str);
		lb->setJustify(justify);
	}
}

void GameStateConfigBase::refreshWidgets() {
	tab_control->setMainArea(((settings->view_w - eset->resolutions.frame_w)/2) + tab_offset.x, ((settings->view_h - eset->resolutions.frame_h)/2) + tab_offset.y);

	frame.x = ((settings->view_w - eset->resolutions.frame_w)/2) + frame_offset.x;
	frame.y = ((settings->view_h - eset->resolutions.frame_h)/2) + tab_control->getTabHeight() + frame_offset.y;

	for (unsigned i=0; i<child_widget.size(); ++i) {
		child_widget[i]->setPos(frame.x, frame.y);
	}

	ok_button->setPos(0, 0);
	defaults_button->setPos(0, 0);
	cancel_button->setPos(0, 0);

	defaults_confirm->align();
}

void GameStateConfigBase::addChildWidget(Widget *w, int tab) {
	child_widget.push_back(w);
	optiontab.push_back(tab);
}

void GameStateConfigBase::refreshLanguages() {
	language_ISO.clear();
	language_lstb->clear();

	FileParser infile;
	if (infile.open("engine/languages.txt", FileParser::MOD_FILE, FileParser::ERROR_NORMAL)) {
		int i = 0;
		while (infile.next()) {
			std::string key = infile.key;
			if (key != "") {
				language_ISO.push_back(key);
				language_lstb->append(infile.val, infile.val + " [" + infile.key + "]");

				if (language_ISO.back() == settings->language) {
					language_lstb->select(i);
				}

				i++;
			}
		}
		infile.close();
	}

	language_lstb->jumpToSelected();
}

void GameStateConfigBase::refreshFont() {
	delete font;
	font = getFontEngine();
	delete comb;
	comb = new CombatText();
}

void GameStateConfigBase::enableMods() {
	for (int i=0; i<inactivemods_lstb->getSize(); i++) {
		if (inactivemods_lstb->isSelected(i)) {
			activemods_lstb->append(inactivemods_lstb->getValue(i),inactivemods_lstb->getTooltip(i));
			inactivemods_lstb->remove(i);
			i--;
		}
	}
}

void GameStateConfigBase::disableMods() {
	for (int i=0; i<activemods_lstb->getSize(); i++) {
		if (activemods_lstb->isSelected(i) && activemods_lstb->getValue(i) != mods->FALLBACK_MOD) {
			inactivemods_lstb->append(activemods_lstb->getValue(i),activemods_lstb->getTooltip(i));
			activemods_lstb->remove(i);
			i--;
		}
	}
	inactivemods_lstb->sort();
}

bool GameStateConfigBase::setMods() {
	// Save new mods list and return true if modlist was changed. Else return false

	std::vector<Mod> temp_list = mods->mod_list;
	mods->mod_list.clear();
	mods->mod_list.push_back(mods->loadMod(mods->FALLBACK_MOD));

	for (int i=0; i<activemods_lstb->getSize(); i++) {
		if (activemods_lstb->getValue(i) != "")
			mods->mod_list.push_back(mods->loadMod(activemods_lstb->getValue(i)));
	}

	mods->applyDepends();

	if (mods->mod_list != temp_list) {
		mods->saveMods();
		return true;
	}
	else {
		return false;
	}
}

std::string GameStateConfigBase::createModTooltip(Mod *mod) {
	std::string ret = "";
	if (mod) {
		std::string mod_ver = (*mod->version == VersionInfo::MIN) ? "" : mod->version->getString();
		std::string engine_ver = VersionInfo::createVersionReqString(*mod->engine_min_version, *mod->engine_max_version);

		ret = mod->name + '\n';

		std::string mod_description = mod->getLocaleDescription(settings->language);
		if (!mod_description.empty()) {
			ret += '\n';
			ret += mod_description + '\n';
		}

		bool middle_section = false;
		if (!mod_ver.empty()) {
			middle_section = true;
			ret += '\n';
			ret += msg->get("Version:") + ' ' + mod_ver;
		}
		if (!mod->game.empty() && mod->game != mods->FALLBACK_GAME) {
			middle_section = true;
			ret += '\n';
			ret += msg->get("Game:") + ' ' + mod->game;
		}
		if (!engine_ver.empty()) {
			middle_section = true;
			ret += '\n';
			ret += msg->get("Engine version:") + ' ' + engine_ver;
		}

		if (middle_section)
			ret += '\n';

		if (!mod->depends.empty()) {
			ret += '\n';
			ret += msg->get("Requires mods:") + '\n';
			for (size_t i=0; i<mod->depends.size(); ++i) {
				ret += "-  " + mod->depends[i];
				std::string depend_ver = VersionInfo::createVersionReqString(*mod->depends_min[i], *mod->depends_max[i]);
				if (depend_ver != "")
					ret += " (" + depend_ver + ")";
				if (i < mod->depends.size()-1)
					ret += '\n';
			}
		}

		if (!ret.empty() && ret[ret.size() - 1] == '\n')
			ret.erase(ret.begin() + ret.size() - 1);
	}
	return ret;
}

void GameStateConfigBase::cleanup() {
	if (background) {
		delete background;
		background = NULL;
	}

	if (tab_control != NULL) {
		delete tab_control;
		tab_control = NULL;
	}

	if (ok_button != NULL) {
		delete ok_button;
		ok_button = NULL;
	}
	if (defaults_button != NULL) {
		delete defaults_button;
		defaults_button = NULL;
	}
	if (cancel_button != NULL) {
		delete cancel_button;
		cancel_button = NULL;
	}

	cleanupTabContents();
	cleanupDialogs();

	language_ISO.clear();
}

void GameStateConfigBase::cleanupTabContents() {
	for (std::vector<Widget*>::iterator iter = child_widget.begin(); iter != child_widget.end(); ++iter) {
		if (*iter != NULL) {
			delete (*iter);
			*iter = NULL;
		}
	}
	child_widget.clear();
}

void GameStateConfigBase::cleanupDialogs() {
	if (defaults_confirm != NULL) {
		delete defaults_confirm;
		defaults_confirm = NULL;
	}
}

