/* ScummVM - Scumm Interpreter
 * Copyright (C) 2002-2006 The ScummVM project
 *
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL$
 * $Id$
 */

#include "common/stdafx.h"
#include "gui/browser.h"
#include "gui/chooser.h"
#include "gui/eval.h"
#include "gui/newgui.h"
#include "gui/options.h"
#include "gui/PopUpWidget.h"
#include "gui/TabWidget.h"

#include "backends/fs/fs.h"
#include "common/config-manager.h"
#include "graphics/scaler.h"
#include "common/system.h"
#include "sound/mididrv.h"
#include "sound/mixer.h"

#if (!( defined(PALMOS_MODE) || defined(__DC__) || defined(__GP32__) || defined(__amigaos4__) ) && !defined(_MSC_VER))
#include <sys/param.h>
#include <unistd.h>
#endif

#if !(defined(MAXPATHLEN))
#ifndef PALMOS_MODE
#define MAXPATHLEN 1024
#else
#define MAXPATHLEN 256
#endif
#endif

namespace GUI {

// TODO - allow changing options for:
// - the save path (use _dirBrowser!)
// - music & graphics driver (but see also the comments on EditGameDialog
//   for some techincal difficulties with this)
// - default volumes (sfx/speech/music)
// - aspect ratio, language, platform, subtitles, debug mode/level, cd drive, joystick, multi midi, native mt32

enum {
	kMusicVolumeChanged		= 'muvc',
	kSfxVolumeChanged		= 'sfvc',
	kSpeechVolumeChanged	= 'vcvc',
	kChooseSoundFontCmd		= 'chsf',
	kChooseSaveDirCmd		= 'chos',
	kChooseExtraDirCmd		= 'chex'
};

#ifdef SMALL_SCREEN_DEVICE
enum {
	kChooseKeyMappingCmd    = 'chma'
};
#endif


OptionsDialog::OptionsDialog(const String &domain, int x, int y, int w, int h)
	: Dialog(x, y, w, h), _domain(domain) {
	init();
}

OptionsDialog::OptionsDialog(const String &domain, String name)
	: Dialog(name), _domain(domain) {
	init();
}

void OptionsDialog::init() {
	_enableGraphicSettings = false;
	_gfxPopUp = 0;
	_renderModePopUp = 0;
	_fullscreenCheckbox = 0;
	_aspectCheckbox = 0;
	_enableAudioSettings = false;
	_subCheckbox = 0;
	_midiPopUp = 0;
	_enableMIDISettings = false;
	_multiMidiCheckbox = 0;
	_mt32Checkbox = 0;
	_enableGSCheckbox = 0;
	_enableVolumeSettings = false;
	_musicVolumeSlider = 0;
	_musicVolumeLabel = 0;
	_sfxVolumeSlider = 0;
	_sfxVolumeLabel = 0;
	_speechVolumeSlider = 0;
	_speechVolumeLabel = 0;
}

void OptionsDialog::open() {
	Dialog::open();

	// Reset result value
	setResult(0);

	if (_fullscreenCheckbox) {
		_gfxPopUp->setSelected(0);

		if (ConfMan.hasKey("gfx_mode", _domain)) {
			const OSystem::GraphicsMode *gm = g_system->getSupportedGraphicsModes();
			String gfxMode = ConfMan.get("gfx_mode", _domain);
			int gfxCount = 1;
			while (gm->name) {
				gfxCount++;

				if (scumm_stricmp(gm->name, gfxMode.c_str()) == 0)
					_gfxPopUp->setSelected(gfxCount);

				gm++;
			}
		}

		_renderModePopUp->setSelected(0);

		if (ConfMan.hasKey("render_mode", _domain)) {
			const Common::RenderModeDescription *p = Common::g_renderModes;
			const Common::RenderMode renderMode = Common::parseRenderMode(ConfMan.get("render_mode", _domain));
			int sel = 0;
			for (int i = 0; p->code; ++p, ++i) {
				if (renderMode == p->id)
					sel = i + 2;
			}
			_renderModePopUp->setSelected(sel);
		}

#ifndef SMALL_SCREEN_DEVICE
		// Fullscreen setting
		_fullscreenCheckbox->setState(ConfMan.getBool("fullscreen", _domain));

		// Aspect ratio setting
		_aspectCheckbox->setState(ConfMan.getBool("aspect_ratio", _domain));
#endif
	}

	if (_subCheckbox) {
		// Music driver
		const MidiDriverDescription *md = MidiDriver::getAvailableMidiDrivers();
		int i = 0;
		const int midiDriver =
			ConfMan.hasKey("music_driver", _domain)
				? MidiDriver::parseMusicDriver(ConfMan.get("music_driver", _domain))
				: MD_AUTO;
		while (md->name && md->id != midiDriver) {
			i++;
			md++;
		}
		_midiPopUp->setSelected(md->name ? i : 0);

		// Subtitles setting
		_subCheckbox->setState(ConfMan.getBool("subtitles", _domain));
	}

	if (_multiMidiCheckbox) {

		// Multi midi setting
		_multiMidiCheckbox->setState(ConfMan.getBool("multi_midi", _domain));

		// Native mt32 setting
		_mt32Checkbox->setState(ConfMan.getBool("native_mt32", _domain));

		// GS extensions setting
		_enableGSCheckbox->setState(ConfMan.getBool("enable_gs", _domain));
	}


	if (_musicVolumeSlider) {
		int vol;

		vol = ConfMan.getInt("music_volume", _domain);
		_musicVolumeSlider->setValue(vol);
		_musicVolumeLabel->setValue(vol);

		vol = ConfMan.getInt("sfx_volume", _domain);
		_sfxVolumeSlider->setValue(vol);
		_sfxVolumeLabel->setValue(vol);

		vol = ConfMan.getInt("speech_volume", _domain);
		_speechVolumeSlider->setValue(vol);
		_speechVolumeLabel->setValue(vol);
	}
}

void OptionsDialog::close() {
	if (getResult()) {
		if (_fullscreenCheckbox) {
			if (_enableGraphicSettings) {
				ConfMan.set("fullscreen", _fullscreenCheckbox->getState(), _domain);
				ConfMan.set("aspect_ratio", _aspectCheckbox->getState(), _domain);

				if ((int32)_gfxPopUp->getSelectedTag() >= 0)
					ConfMan.set("gfx_mode", _gfxPopUp->getSelectedString(), _domain);

				if ((int32)_renderModePopUp->getSelectedTag() >= 0)
					ConfMan.set("render_mode", Common::getRenderModeCode((Common::RenderMode)_renderModePopUp->getSelectedTag()), _domain);
			} else {
				ConfMan.removeKey("fullscreen", _domain);
				ConfMan.removeKey("aspect_ratio", _domain);
				ConfMan.removeKey("gfx_mode", _domain);
				ConfMan.removeKey("render_mode", _domain);
			}
		}

		if (_musicVolumeSlider) {
			if (_enableVolumeSettings) {
				ConfMan.set("music_volume", _musicVolumeSlider->getValue(), _domain);
				ConfMan.set("sfx_volume", _sfxVolumeSlider->getValue(), _domain);
				ConfMan.set("speech_volume", _speechVolumeSlider->getValue(), _domain);
			} else {
				ConfMan.removeKey("music_volume", _domain);
				ConfMan.removeKey("sfx_volume", _domain);
				ConfMan.removeKey("speech_volume", _domain);
			}
		}

		if (_subCheckbox) {
			if (_enableAudioSettings) {
				ConfMan.set("subtitles", _subCheckbox->getState(), _domain);
				const MidiDriverDescription *md = MidiDriver::getAvailableMidiDrivers();
				while (md->name && md->id != (int)_midiPopUp->getSelectedTag())
					md++;
				if (md->name)
					ConfMan.set("music_driver", md->name, _domain);
				else
					ConfMan.removeKey("music_driver", _domain);
			} else {
				ConfMan.removeKey("music_driver", _domain);
				ConfMan.removeKey("subtitles", _domain);
			}
		}

		if (_multiMidiCheckbox) {
			if (_enableMIDISettings) {
				ConfMan.set("multi_midi", _multiMidiCheckbox->getState(), _domain);
				ConfMan.set("native_mt32", _mt32Checkbox->getState(), _domain);
				ConfMan.set("enable_gs", _enableGSCheckbox->getState(), _domain);
			} else {
				ConfMan.removeKey("multi_midi", _domain);
				ConfMan.removeKey("native_mt32", _domain);
				ConfMan.removeKey("enable_gs", _domain);
			}
		}

		// Save config file
		ConfMan.flushToDisk();
	}

	Dialog::close();
}

void OptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
	switch (cmd) {
	case kMusicVolumeChanged:
		_musicVolumeLabel->setValue(_musicVolumeSlider->getValue());
		_musicVolumeLabel->draw();
		break;
	case kSfxVolumeChanged:
		_sfxVolumeLabel->setValue(_sfxVolumeSlider->getValue());
		_sfxVolumeLabel->draw();
		break;
	case kSpeechVolumeChanged:
		_speechVolumeLabel->setValue(_speechVolumeSlider->getValue());
		_speechVolumeLabel->draw();
		break;
	case kOKCmd:
		setResult(1);
		close();
		break;
	default:
		Dialog::handleCommand(sender, cmd, data);
	}
}

void OptionsDialog::setGraphicSettingsState(bool enabled) {
	_enableGraphicSettings = enabled;

	_gfxPopUp->setEnabled(enabled);
	_renderModePopUp->setEnabled(enabled);
#ifndef SMALL_SCREEN_DEVICE
	_fullscreenCheckbox->setEnabled(enabled);
	_aspectCheckbox->setEnabled(enabled);
#endif
}

void OptionsDialog::setAudioSettingsState(bool enabled) {
	_enableAudioSettings = enabled;

	_midiPopUp->setEnabled(enabled);
	_subCheckbox->setEnabled(enabled);
}

void OptionsDialog::setMIDISettingsState(bool enabled) {
	_enableMIDISettings = enabled;

	_soundFontButton->setEnabled(enabled);
	_soundFont->setEnabled(enabled);
	_multiMidiCheckbox->setEnabled(enabled);
	_mt32Checkbox->setEnabled(enabled);
	_enableGSCheckbox->setEnabled(enabled);
}

void OptionsDialog::setVolumeSettingsState(bool enabled) {
	_enableVolumeSettings = enabled;

	_musicVolumeSlider->setEnabled(enabled);
	_musicVolumeLabel->setEnabled(enabled);
	_sfxVolumeSlider->setEnabled(enabled);
	_sfxVolumeLabel->setEnabled(enabled);
	_speechVolumeSlider->setEnabled(enabled);
	_speechVolumeLabel->setEnabled(enabled);
}

void OptionsDialog::addGraphicControls(GuiObject *boss, String prefix) {
	const OSystem::GraphicsMode *gm = g_system->getSupportedGraphicsModes();

	int labelWidth = g_gui.evaluator()->getVar("tabPopupsLabelW");

	// The GFX mode popup
	_gfxPopUp = new PopUpWidget(boss, prefix + "grModePopup", "Graphics mode: ", labelWidth);

	_gfxPopUp->appendEntry("<default>");
	_gfxPopUp->appendEntry("");
	while (gm->name) {
		_gfxPopUp->appendEntry(gm->description, gm->id);
		gm++;
	}

	// RenderMode popup
	_renderModePopUp = new PopUpWidget(boss, prefix + "grRenderPopup", "Render mode: ", labelWidth);
	_renderModePopUp->appendEntry("<default>", Common::kRenderDefault);
	_renderModePopUp->appendEntry("");
	const Common::RenderModeDescription *rm = Common::g_renderModes;
	for (; rm->code; ++rm) {
		_renderModePopUp->appendEntry(rm->description, rm->id);
	}

	// Fullscreen checkbox
	_fullscreenCheckbox = new CheckboxWidget(boss, prefix + "grFullscreenCheckbox", "Fullscreen mode", 0, 0);

	// Aspect ratio checkbox
	_aspectCheckbox = new CheckboxWidget(boss, prefix + "grAspectCheckbox", "Aspect ratio correction", 0, 0);

#ifdef SMALL_SCREEN_DEVICE
	_fullscreenCheckbox->setState(true);
	_fullscreenCheckbox->setEnabled(false);
	_aspectCheckbox->setEnabled(false);
#endif

	_enableGraphicSettings = true;
}

void OptionsDialog::addAudioControls(GuiObject *boss, String prefix) {
	int labelWidth = g_gui.evaluator()->getVar("tabPopupsLabelW");

	// The MIDI mode popup & a label
	_midiPopUp = new PopUpWidget(boss, prefix + "auMidiPopup", "Music driver: ", labelWidth);

	// Populate it
	const MidiDriverDescription *md = MidiDriver::getAvailableMidiDrivers();
	while (md->name) {
		_midiPopUp->appendEntry(md->description, md->id);
		md++;
	}

	// Subtitles on/off
	_subCheckbox = new CheckboxWidget(boss, prefix + "auSubtitlesCheckbox", "Display subtitles", 0, 0);

	_enableAudioSettings = true;
}

void OptionsDialog::addMIDIControls(GuiObject *boss, String prefix) {
	// SoundFont
	_soundFontButton = new ButtonWidget(boss, prefix + "mcFontButton", "SoundFont:", kChooseSoundFontCmd, 0);
	_soundFont = new StaticTextWidget(boss, prefix + "mcFontPath", "None");

	// Multi midi setting
	_multiMidiCheckbox = new CheckboxWidget(boss, prefix + "mcMixedCheckbox", "Mixed Adlib/MIDI mode", 0, 0);

	// Native mt32 setting
	_mt32Checkbox = new CheckboxWidget(boss, prefix + "mcMt32Checkbox", "True Roland MT-32 (disable GM emulation)", 0, 0);

	// GS Extensions setting
	_enableGSCheckbox = new CheckboxWidget(boss, prefix + "mcGSCheckbox", "Enable Roland GS Mode", 0, 0);

	_enableMIDISettings = true;
}

void OptionsDialog::addVolumeControls(GuiObject *boss, String prefix) {
	const char *slider_labels[] = {
		"Music volume:",
		"SFX volume:",
		"Speech volume:"
	};

	// Volume controllers
	new StaticTextWidget(boss, prefix + "vcMusicText", slider_labels[0]);
	_musicVolumeSlider = new SliderWidget(boss, prefix + "vcMusicSlider", kMusicVolumeChanged);
	_musicVolumeLabel = new StaticTextWidget(boss, prefix + "vcMusicLabel", "100%");
	_musicVolumeSlider->setMinValue(0);
	_musicVolumeSlider->setMaxValue(Audio::Mixer::kMaxMixerVolume);
	_musicVolumeLabel->setFlags(WIDGET_CLEARBG);

	new StaticTextWidget(boss, prefix + "vcSfxText", slider_labels[1]);
	_sfxVolumeSlider = new SliderWidget(boss, prefix + "vcSfxSlider", kSfxVolumeChanged);
	_sfxVolumeLabel = new StaticTextWidget(boss, prefix + "vcSfxLabel", "100%");
	_sfxVolumeSlider->setMinValue(0);
	_sfxVolumeSlider->setMaxValue(Audio::Mixer::kMaxMixerVolume);
	_sfxVolumeLabel->setFlags(WIDGET_CLEARBG);

	new StaticTextWidget(boss, prefix + "vcSpeechText" , slider_labels[2]);
	_speechVolumeSlider = new SliderWidget(boss, prefix + "vcSpeechSlider", kSpeechVolumeChanged);
	_speechVolumeLabel = new StaticTextWidget(boss, prefix + "vcSpeechLabel", "100%");
	_speechVolumeSlider->setMinValue(0);
	_speechVolumeSlider->setMaxValue(Audio::Mixer::kMaxMixerVolume);
	_speechVolumeLabel->setFlags(WIDGET_CLEARBG);

	_enableVolumeSettings = true;
}

#pragma mark -


GlobalOptionsDialog::GlobalOptionsDialog()
	: OptionsDialog(Common::ConfigManager::kApplicationDomain, "globaloptions") {

	// The tab widget
	TabWidget *tab = new TabWidget(this, "globaloptions_tabwidget");
	tab->setHints(THEME_HINT_FIRST_DRAW | THEME_HINT_SAVE_BACKGROUND);

	//
	// 1) The graphics tab
	//
	tab->addTab("Graphics");
	addGraphicControls(tab, "globaloptions_");

	//
	// 2) The audio tab
	//
	tab->addTab("Audio");
	addAudioControls(tab, "globaloptions_");
	addVolumeControls(tab, "globaloptions_");
	// TODO: cd drive setting

	//
	// 3) The MIDI tab
	//
	tab->addTab("MIDI");
	addMIDIControls(tab, "globaloptions_");

	//
	// 4) The miscellaneous tab
	//
	tab->addTab("Paths");

#if !( defined(__DC__) || defined(__GP32__) )
	// These two buttons have to be extra wide, or the text will be
	// truncated in the small version of the GUI.

	// Save game path
	new ButtonWidget(tab, "globaloptions_savebutton", "Save Path: ", kChooseSaveDirCmd, 0);
	_savePath = new StaticTextWidget(tab, "globaloptions_savepath", "/foo/bar");

	new ButtonWidget(tab, "globaloptions_extrabutton", "Extra Path:", kChooseExtraDirCmd, 0);
	_extraPath = new StaticTextWidget(tab, "globaloptions_extrapath", "None");
#endif

#ifdef SMALL_SCREEN_DEVICE
	new ButtonWidget(tab, "globaloptions_keysbutton", "Keys", kChooseKeyMappingCmd, 0);
#endif

	// TODO: joystick setting


	// Activate the first tab
	tab->setActiveTab(0);

	// Add OK & Cancel buttons
	new ButtonWidget(this, "globaloptions_cancel", "Cancel", kCloseCmd, 0);
	new ButtonWidget(this, "globaloptions_ok", "OK", kOKCmd, 0);

#ifdef SMALL_SCREEN_DEVICE
	_keysDialog = new KeysDialog();
#endif
}

GlobalOptionsDialog::~GlobalOptionsDialog() {
#ifdef SMALL_SCREEN_DEVICE
	delete _keysDialog;
#endif
}

void GlobalOptionsDialog::open() {
	OptionsDialog::open();

#if !( defined(__DC__) || defined(__GP32__) || defined(__PLAYSTATION2__) )
	// Set _savePath to the current save path
	Common::String dir(ConfMan.get("savepath", _domain));
	Common::String extraPath(ConfMan.get("extrapath", _domain));
	Common::String soundFont(ConfMan.get("soundfont", _domain));

	if (!dir.empty()) {
		_savePath->setLabel(dir);
	} else {
		// Default to the current directory...
		char buf[MAXPATHLEN];
		getcwd(buf, sizeof(buf));
		_savePath->setLabel(buf);
	}

	if (extraPath.empty() || !ConfMan.hasKey("extrapath", _domain)) {
		_extraPath->setLabel("None");
	} else {
		_extraPath->setLabel(extraPath);
	}

	if (soundFont.empty() || !ConfMan.hasKey("soundfont", _domain)) {
		_soundFont->setLabel("None");
	} else {
		_soundFont->setLabel(soundFont);
	}
#endif
}

void GlobalOptionsDialog::close() {
	if (getResult()) {
		// Savepath
		ConfMan.set("savepath", _savePath->getLabel(), _domain);

		String extraPath = _extraPath->getLabel();
		if (!extraPath.empty() && (extraPath != "None"))
			ConfMan.set("extrapath", extraPath, _domain);

		String soundFont = _soundFont->getLabel();
		if (!soundFont.empty() && (soundFont != "None"))
			ConfMan.set("soundfont", soundFont, _domain);
	}
	OptionsDialog::close();
}

void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
	switch (cmd) {
	case kChooseSaveDirCmd: {
		BrowserDialog browser("Select directory for savegames", true);
		if (browser.runModal() > 0) {
			// User made his choice...
			FilesystemNode dir(browser.getResult());
			_savePath->setLabel(dir.path());
			draw();
			// TODO - we should check if the directory is writeable before accepting it
		}
		break;
	}
	case kChooseExtraDirCmd: {
		BrowserDialog browser("Select directory for extra files", true);
		if (browser.runModal() > 0) {
			// User made his choice...
			FilesystemNode dir(browser.getResult());
			_extraPath->setLabel(dir.path());
			draw();
		}
		break;
	}
	case kChooseSoundFontCmd: {
		BrowserDialog browser("Select SoundFont", false);
		if (browser.runModal() > 0) {
			// User made his choice...
			FilesystemNode file(browser.getResult());
			_soundFont->setLabel(file.path());
			draw();
		}
		break;
	}
#ifdef SMALL_SCREEN_DEVICE
	case kChooseKeyMappingCmd:
		_keysDialog->runModal();
		break;
#endif
	default:
		OptionsDialog::handleCommand(sender, cmd, data);
	}
}

} // End of namespace GUI