//+----------------------------------------------------------------------------+
//| Description:  Magic Set Editor - Program to make Magic (tm) cards          |
//| Copyright:    (C) 2001 - 2010 Twan van Laarhoven and Sean Hunt             |
//| License:      GNU General Public License 2 or later (see file COPYING)     |
//+----------------------------------------------------------------------------+

// ----------------------------------------------------------------------------- : Includes

#include <util/prec.hpp>
#include <data/settings.hpp>
#include <data/installer.hpp>
#include <data/game.hpp>
#include <data/stylesheet.hpp>
#include <data/field.hpp>
#include <data/export_template.hpp>
#include <data/word_list.hpp>
#include <util/reflect.hpp>
#include <util/platform.hpp>
#include <util/io/reader.hpp>
#include <util/io/writer.hpp>
#include <util/delayed_index_maps.hpp>
#include <wx/filename.h>
#include <wx/wfstream.h>
#include <wx/stdpaths.h>

DECLARE_TYPEOF_COLLECTION(AutoReplaceP);

// ----------------------------------------------------------------------------- : Extra types

IMPLEMENT_REFLECTION_ENUM(CheckUpdates) {
	VALUE_N("if connected", CHECK_IF_CONNECTED); //default
	VALUE_N("always",       CHECK_ALWAYS);
	VALUE_N("never",        CHECK_NEVER);
}

IMPLEMENT_REFLECTION_ENUM(InstallType) {
	VALUE_N("default",	INSTALL_DEFAULT); //default
	VALUE_N("local",	INSTALL_LOCAL);
	VALUE_N("global",	INSTALL_GLOBAL);
}

bool is_install_local(InstallType type) {
	#ifdef __WXMSW__
		#define DEFAULT_INSTALL_LOCAL false
	#else
		#define DEFAULT_INSTALL_LOCAL true
	#endif
	return type == INSTALL_DEFAULT ? DEFAULT_INSTALL_LOCAL : type == INSTALL_LOCAL;
}

IMPLEMENT_REFLECTION_ENUM(FilenameConflicts) {
	VALUE_N("keep old",			CONFLICT_KEEP_OLD);
	VALUE_N("overwrite",		CONFLICT_OVERWRITE);
	VALUE_N("number",			CONFLICT_NUMBER);
	VALUE_N("number overwrite",	CONFLICT_NUMBER_OVERWRITE);
}

const int COLUMN_NOT_INITIALIZED = -100000;

ColumnSettings::ColumnSettings()
	: width(100), position(COLUMN_NOT_INITIALIZED), visible(false)
{}

// dummy for ColumnSettings reflection
ScriptValueP to_script(const ColumnSettings&) { return script_nil; }

IMPLEMENT_REFLECTION_NO_SCRIPT(ColumnSettings) {
	REFLECT(width);
	REFLECT(position);
	REFLECT(visible);
}

GameSettings::GameSettings()
	: sort_cards_ascending(true)
	, images_export_filename(_("{card.name}.jpg"))
	, images_export_conflicts(CONFLICT_NUMBER_OVERWRITE)
	, use_auto_replace(true)
	, pack_seed_random(true)
	, pack_seed(123456)
	, initialized(false)
{}

void GameSettings::initDefaults(const Game& game) {
	// Defer initialization until the game is fully loaded.
	// This prevents data that needs to be initialized from
	// being accessed from the new set window, but removes
	// the need to load the entire file, which takes too long.
	if (initialized || !game.isFullyLoaded()) return;
	initialized = true;
	// init auto_replaces, copy from game file
	FOR_EACH_CONST(ar, game.auto_replaces) {
		// do we have this one?
		bool already_have = false;
		FOR_EACH(ar2, auto_replaces) {
			if (ar->match == ar2->match) {
				ar2->custom = false;
				already_have = true;
				break;
			}
		}
		if (!already_have) {
			// TODO: when we start saving games, clone here
			ar->custom = false;
			auto_replaces.push_back(ar);
		}
	}
}

IMPLEMENT_REFLECTION_NO_SCRIPT(GameSettings) {
	REFLECT(default_stylesheet);
	REFLECT(default_export);
	REFLECT_N("cardlist columns",     columns);
	REFLECT(sort_cards_by);
	REFLECT(sort_cards_ascending);
	REFLECT(images_export_filename);
	REFLECT(images_export_conflicts);
	REFLECT(use_auto_replace);
	REFLECT(auto_replaces);
	REFLECT(pack_amounts);
	REFLECT(pack_seed_random);
	REFLECT(pack_seed);
}


StyleSheetSettings::StyleSheetSettings()
	: card_zoom              (1.0,  true)
	, card_angle             (0,    true)
	, card_anti_alias        (true, true)
	, card_borders           (true, true)
	, card_draw_editing      (true, true)
	, card_normal_export     (true, true)
	, card_spellcheck_enabled(true, true)
{}

void StyleSheetSettings::useDefault(const StyleSheetSettings& ss) {
	if (card_zoom              .isDefault()) card_zoom              .assignDefault(ss.card_zoom);
	if (card_angle             .isDefault()) card_angle             .assignDefault(ss.card_angle);
	if (card_anti_alias        .isDefault()) card_anti_alias        .assignDefault(ss.card_anti_alias);
	if (card_borders           .isDefault()) card_borders           .assignDefault(ss.card_borders);
	if (card_draw_editing      .isDefault()) card_draw_editing      .assignDefault(ss.card_draw_editing);
	if (card_normal_export     .isDefault()) card_normal_export     .assignDefault(ss.card_normal_export);
	if (card_spellcheck_enabled.isDefault()) card_spellcheck_enabled.assignDefault(ss.card_spellcheck_enabled);
}

IMPLEMENT_REFLECTION_NO_SCRIPT(StyleSheetSettings) {
	REFLECT(card_zoom);
	REFLECT(card_angle);
	REFLECT(card_anti_alias);
	REFLECT(card_borders);
	REFLECT(card_draw_editing);
	REFLECT(card_normal_export);
	REFLECT(card_spellcheck_enabled);
}

// ----------------------------------------------------------------------------- : Printing

IMPLEMENT_REFLECTION_ENUM(PageLayoutType) {
	VALUE_N("no space",    LAYOUT_NO_SPACE);
	VALUE_N("equal space", LAYOUT_EQUAL_SPACE);
}

// ----------------------------------------------------------------------------- : Settings

Settings settings;

Settings::Settings()
	: locale               (_("en"))
	, set_window_maximized (false)
	, set_window_width     (790)
	, set_window_height    (300)
	, card_notes_height    (40)
	, open_sets_in_new_window(true)
	, symbol_grid_size     (30)
	, symbol_grid          (true)
	, symbol_grid_snap     (false)
	, print_layout         (LAYOUT_NO_SPACE)
	#if USE_OLD_STYLE_UPDATE_CHECKER
	, updates_url          (_("http://magicseteditor.sourceforge.net/updates"))
	#endif
	, package_versions_url (_("http://magicseteditor.sourceforge.net/packages"))
	, installer_list_url   (_("http://magicseteditor.sourceforge.net/installers"))
	, check_updates        (CHECK_IF_CONNECTED)
	, check_updates_all    (true)
	, website_url          (_("http://magicseteditor.sourceforge.net/"))
	, install_type         (INSTALL_DEFAULT)
{}

void Settings::addRecentFile(const String& filename) {
	// get absolute path
	wxFileName fn(filename);
	fn.Normalize();
	String filenameAbs = fn.GetFullPath();
	// remove duplicates
	recent_sets.erase(
		remove(recent_sets.begin(), recent_sets.end(), filenameAbs),
		recent_sets.end()
	);
	// add to front of list
	recent_sets.insert(recent_sets.begin(), filenameAbs);
	// enforce size limit
	if (recent_sets.size() > max_recent_sets) recent_sets.resize(max_recent_sets);
}

GameSettings& Settings::gameSettingsFor(const Game& game) {
	GameSettingsP& gs = game_settings[game.name()];
	if (!gs) gs = intrusive(new GameSettings);
	gs->initDefaults(game);
	return *gs;
}
ColumnSettings& Settings::columnSettingsFor(const Game& game, const Field& field) {
	// Get game info
	GameSettings& gs = gameSettingsFor(game);
	// Get column info
	ColumnSettings& cs = gs.columns[field.name];
	if (cs.position == COLUMN_NOT_INITIALIZED) {
		// column info not set, initialize based on the game
		cs.visible  = field.card_list_visible;
		cs.position = field.card_list_column;
		cs.width    = field.card_list_width;
	}
	return cs;
}
StyleSheetSettings& Settings::stylesheetSettingsFor(const StyleSheet& stylesheet) {
	StyleSheetSettingsP& ss = stylesheet_settings[stylesheet.name()];
	if (!ss) ss = intrusive(new StyleSheetSettings);
	ss->useDefault(default_stylesheet_settings); // update default settings
	return *ss;
}

IndexMap<FieldP,ValueP>& Settings::exportOptionsFor(const ExportTemplate& export_template) {
	return export_options.get(export_template.name(), export_template.option_fields);
}

/// Retrieve the directory to use for settings and other data files
String user_settings_dir() {
	String dir = wxStandardPaths::Get().GetUserDataDir();
	if (!wxDirExists(dir)) wxMkdir(dir);
	return dir + _("/");
}

String Settings::settingsFile() {
//	return user_settings_dir() + _("mse.config");
	return user_settings_dir() + _("mse8.config"); // use different file during development of C++ port
}

IMPLEMENT_REFLECTION_NO_SCRIPT(Settings) {
	REFLECT_ALIAS(300,         "style settings",         "stylesheet settings");
	REFLECT_ALIAS(300, "default style settings", "default stylesheet settings");
	REFLECT(locale);
	REFLECT(recent_sets);
	REFLECT(default_set_dir);
	REFLECT(default_image_dir);
	REFLECT(default_symbol_dir);
	REFLECT(default_export_dir);
	REFLECT(set_window_maximized);
	REFLECT(set_window_width);
	REFLECT(set_window_height);
	REFLECT(card_notes_height);
	REFLECT(open_sets_in_new_window);
	REFLECT(symbol_grid_size);
	REFLECT(symbol_grid);
	REFLECT(symbol_grid_snap);
	REFLECT(default_game);
	REFLECT(print_layout);
	REFLECT(apprentice_location);
	#if USE_OLD_STYLE_UPDATE_CHECKER
		REFLECT(updates_url);
	#else
		REFLECT_IGNORE(306,"updates url");
	#endif
	REFLECT(package_versions_url);
	REFLECT(installer_list_url);
	REFLECT(check_updates);
	REFLECT(check_updates_all);
	REFLECT(install_type);
	REFLECT(website_url);
	REFLECT(game_settings);
	REFLECT(stylesheet_settings);
	REFLECT(default_stylesheet_settings);
	REFLECT(export_options);
}

void Settings::clear() {
	recent_sets.clear();
	game_settings.clear();
	stylesheet_settings.clear();
	default_stylesheet_settings = StyleSheetSettings();
	export_options.clear();
}

void Settings::read() {
	// clear current settings, otherwise we duplicate vector elements
	clear();
	// (re)load settings
	String filename = settingsFile();
	if (wxFileExists(filename)) {
		// settings file not existing is not an error
		shared_ptr<wxFileInputStream> file = shared(new wxFileInputStream(filename));
		if (!file->Ok()) return; // failure is not an error
		Reader reader(file, nullptr, filename);
		reader.handle_greedy(*this);
	}
}

void Settings::write() {
	Writer writer(shared(new wxFileOutputStream(settingsFile())), app_version);
	writer.handle(*this);
}
