/**************************************************************

config.cpp (C-Munipack project)
Configuration file interface
Copyright (C) 2008 David Motl, dmotl@volny.cz

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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

**************************************************************/

#include <stdlib.h>
#include <string.h>

#include "configuration.h"
#include "utils.h"
#include "main.h"

// Static data
GKeyFile *CConfig::ms_KeyFile = NULL;
bool CConfig::ms_Modified = false;

static const struct tConfigParamDef {
	const char *group, *keyword;
	GType type;
	const char *defval;
} Params[CConfig::EndOfParameters] = {
	{ "Environment",	"ChartInvert",	G_TYPE_BOOLEAN },
	{ "Environment",	"NoPreviews",	G_TYPE_BOOLEAN },
	{ "Environment",    "EventSounds",  G_TYPE_BOOLEAN },
	{ "Environment",	"Debug",		G_TYPE_BOOLEAN },
	{ "Environment",	"Columns",		G_TYPE_INT, "53" },
	{ "GCVS",			"Enable",		G_TYPE_BOOLEAN },
	{ "GCVS",			"Path",			G_TYPE_STRING, "" },
	{ "NSV",			"Enable",		G_TYPE_BOOLEAN },
	{ "NSV",			"Path",			G_TYPE_STRING, "" },
	{ "NSVS",			"Enable",		G_TYPE_BOOLEAN },
	{ "NSVS",			"Path",			G_TYPE_STRING, "" },
	{ "Environment",    "UserProfiles", G_TYPE_STRING, "" },
	{ "Environment",    "RowsUpward",   G_TYPE_BOOLEAN },
	{ "Environment",    "DoNotMaximize", G_TYPE_BOOLEAN },
    { "VSX",            "Enable",       G_TYPE_BOOLEAN },
    { "VSX",            "Path",         G_TYPE_STRING, "" }
};

// Initialization
void CConfig::Init(void)
{
	ms_KeyFile = g_key_file_new();
	Load();
}

// Finalization
void CConfig::Free(void)
{
	Flush();
	g_key_file_free(ms_KeyFile);
}

// Lock data
void CConfig::Lock(void)
{
	gdk_threads_lock();
}

// Unlock data
void CConfig::Unlock(void)
{
	gdk_threads_unlock();
}

void CConfig::Load(void)
{
	char *cfg_path = g_build_filename(get_user_config_dir(), "config.ini", NULL);

	Lock();
	g_key_file_load_from_file(ms_KeyFile, cfg_path, 
		(GKeyFileFlags)(G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS), NULL);
	ms_Modified = false;
	Unlock();
	g_free(cfg_path);
}

void CConfig::Save(void)
{
	gsize datalen;
	char *data, *cfg_path = g_build_filename(get_user_config_dir(), "config.ini", NULL);

	Lock();
	data = g_key_file_to_data(ms_KeyFile, &datalen, NULL);
	if (data) {
		force_directory(get_user_config_dir());
		GIOChannel *pFile = g_io_channel_new_file(cfg_path, "w+", NULL);
		if (pFile) {
			g_io_channel_write_chars(pFile, data, datalen, NULL, NULL);
			g_io_channel_unref(pFile);
		}
		g_free(data);
	}
	ms_Modified = false;
	Unlock();
	g_free(cfg_path);
}

void CConfig::Flush(void)
{
	Lock();
	if (ms_Modified)
		Save();
	Unlock();
}

void CConfig::SetStr(const char *group, const char *key, const char *val)
{
	Lock();
	g_key_file_set_string(ms_KeyFile, group, key, (val!=NULL ? val : ""));
	ms_Modified = true;
	Unlock();
}

void CConfig::SetDbl(const char *group, const char *key, const double val)
{
	Lock();
	g_key_file_set_double(ms_KeyFile, group, key, val);
	ms_Modified = true;
	Unlock();
}

void CConfig::SetInt(const char *group, const char *key, const int val)
{
	Lock();
	g_key_file_set_integer(ms_KeyFile, group, key, val);
	ms_Modified = true;
	Unlock();
}

void CConfig::SetBool(const char *group, const char *key, bool val)
{
	Lock();
	g_key_file_set_boolean(ms_KeyFile, group, key, val);
	ms_Modified = true;
	Unlock();
}


char *CConfig::GetStr(const char *group, const char *key, const char *defval)
{
	char *res;
	GError *error = NULL;

	Lock();
	res = g_key_file_get_string(ms_KeyFile, group, key, &error);
	if (error) {
		res = (defval ? g_strdup(defval) : NULL);
		g_error_free(error);
	}
	Unlock();
	return res;
}

bool CConfig::TryGetStr(const char *group, const char *key, char **value)
{
	bool retval;
	char *res;
	GError *error = NULL;

	Lock();
	res = g_key_file_get_string(ms_KeyFile, group, key, &error);
	if (error) {
		if (value)
			*value = NULL;
		retval = false;
		g_error_free(error);
	} else {
		if (value)
			*value = g_strdup(res);
		retval = true;
	}
	Unlock();
	return retval;
}

double CConfig::GetDbl(const char *group, const char *key, double defval, double minval, double maxval)
{
	double res;
	GError *error = NULL;

	Lock();
	res = g_key_file_get_double(ms_KeyFile, group, key, &error);
	if (error) {
		res = defval;
		g_error_free(error);
	}
	Unlock();
	return LimitValue(res, minval, maxval);
}

bool CConfig::TryGetDbl(const char *group, const char *key, double *value)
{
	bool retval;
	double res;
	GError *error = NULL;

	Lock();
	res = g_key_file_get_double(ms_KeyFile, group, key, &error);
	if (error) {
		if (value)
			*value = 0;
		g_error_free(error);
		retval = false;
	} else {
		if (value)
			*value = res;
		retval = true;
	}
	Unlock();
	return retval;
}

int CConfig::GetInt(const char *group, const char *key, int defval, int minval, int maxval)
{
	int res;
	GError *error = NULL;

	Lock();
	res = g_key_file_get_integer(ms_KeyFile, group, key, &error);
	if (error) {
		res = defval;
		g_error_free(error);
	}
	Unlock();
	return LimitValue(res, minval, maxval);
}

bool CConfig::TryGetInt(const char *group, const char *key, int *value)
{
	bool retval;
	int res;
	GError *error = NULL;

	Lock();
	res = g_key_file_get_integer(ms_KeyFile, group, key, &error);
	if (error) {
		if (value)
			*value = 0;
		retval = false;
		g_error_free(error);
	} else {
		if (value)
			*value = res;
		retval = true;
	}
	Unlock();
	return retval;
}

bool CConfig::GetBool(const char *group, const char *key, const bool defval)
{
	bool res;
	GError *error = NULL;

	Lock();
	res = g_key_file_get_boolean(ms_KeyFile, group, key, &error)!=0;
	if (error) {
		res = defval;
		g_error_free(error);
	}
	Unlock();
	return res;
}

bool CConfig::TryGetBool(const char *group, const char *key, bool *value)
{
	bool retval, res;
	GError *error = NULL;

	Lock();
	res = g_key_file_get_boolean(ms_KeyFile, group, key, &error)!=0;
	if (error) {
		if (value)
			*value = false;
		retval = false;
		g_error_free(error);
	} else {
		if (value)
			*value = res;
		retval = true;
	}
	Unlock();
	return retval;
}

void CConfig::SetStr(tParameter param, const gchar *value)
{
	g_return_if_fail(param>=0 && param<EndOfParameters);
	g_return_if_fail(Params[param].type == G_TYPE_STRING);

	const tConfigParamDef *p = &Params[param];
	SetStr(p->group, p->keyword, value);
}

void CConfig::SetInt(tParameter param, int value)
{
	g_return_if_fail(param>=0 && param<EndOfParameters);
	g_return_if_fail(Params[param].type == G_TYPE_INT);

	const tConfigParamDef *p = &Params[param];
	SetInt(p->group, p->keyword, value);
}

void CConfig::SetBool(tParameter param, bool value)
{
	g_return_if_fail(param>=0 && param<EndOfParameters);
	g_return_if_fail(Params[param].type == G_TYPE_BOOLEAN);

	const tConfigParamDef *p = &Params[param];
	SetBool(p->group, p->keyword, value);
}

char *CConfig::GetStr(tParameter param)
{
	char *value;

	g_return_val_if_fail(param>=0 && param<EndOfParameters, NULL);
	g_return_val_if_fail(Params[param].type == G_TYPE_STRING, NULL);

	const tConfigParamDef *p = &Params[param];
	if (TryGetStr(p->group, p->keyword, &value)) 
		return value;
	else if (p->defval)
		return g_strdup(p->defval);
	else
		return g_strdup("");
}

int CConfig::GetInt(tParameter param)
{
	int value;

	g_return_val_if_fail(param>=0 && param<EndOfParameters, 0);
	g_return_val_if_fail(Params[param].type == G_TYPE_INT, 0);

	const tConfigParamDef *p = &Params[param];
	if (TryGetInt(p->group, p->keyword, &value)) 
		return value;
	else if (p->defval)
		return atoi(p->defval);
	else
		return 0;
}

bool CConfig::GetBool(tParameter param)
{
	bool value;

	g_return_val_if_fail(param>=0 && param<EndOfParameters, false);
	g_return_val_if_fail(Params[param].type == G_TYPE_BOOLEAN, false);

	const tConfigParamDef *p = &Params[param];
	if (TryGetBool(p->group, p->keyword, &value)) 
		return value;
	else if (p->defval)
		return atoi(p->defval)!=0;
	else
		return false;
}

bool CConfig::GetDefaultBool(tParameter param)
{
	g_return_val_if_fail(param>=0 && param<EndOfParameters, false);
	g_return_val_if_fail(Params[param].type == G_TYPE_BOOLEAN, false);

	const tConfigParamDef *p = &Params[param];
	if (p->defval)
		return atoi(p->defval)!=0;
	else
		return false;
}

int CConfig::GetDefaultInt(tParameter param)
{
	g_return_val_if_fail(param>=0 && param<EndOfParameters, 0);
	g_return_val_if_fail(Params[param].type == G_TYPE_INT, 0);

	const tConfigParamDef *p = &Params[param];
	if (p->defval)
		return atoi(p->defval);
	else
		return 0;
}

const gchar *CConfig::GetDefaultStr(tParameter param)
{
	g_return_val_if_fail(param>=0 && param<EndOfParameters, 0);
	g_return_val_if_fail(Params[param].type == G_TYPE_STRING, 0);

	const tConfigParamDef *p = &Params[param];
	return (p->defval ? p->defval : "");
}

void CConfig::AddProjectToRecentList(tProjectType type, const gchar *fpath)
{
	const gchar *groups[3];

	Lock();
	groups[0] = RECENT_GROUP_PROJECT;
	groups[1] = (gchar*)ProjectTypeRecentGroup(type);
	groups[2] = NULL;
	if (groups[0]) {
		GtkRecentData *recent_data = g_new0(GtkRecentData, 1);
		gchar *basename = g_path_get_basename(fpath);
		recent_data->display_name = StripFileExtension(basename);
		recent_data->description = g_strdup(RECENT_GROUP_PROJECT);
		recent_data->mime_type = g_strdup("text/plain");
		recent_data->app_exec = g_strjoin(" ", g_get_prgname (), "%u", NULL);
		recent_data->app_name = (gchar*)g_get_application_name();
		recent_data->groups = (gchar**)groups;
		recent_data->is_private = TRUE;
		GtkRecentManager *rc = gtk_recent_manager_get_default();
		gchar *uri = g_filename_to_uri(fpath, NULL, NULL);
		gtk_recent_manager_add_full(rc, uri, recent_data);
		g_free(basename);
		g_free(uri);
		g_free(recent_data->description);
		g_free(recent_data->mime_type);
		g_free(recent_data->app_exec); 
		g_free(recent_data->display_name);
		g_free(recent_data);
	}
	Unlock();
}

void CConfig::RenameProjectInRecentList(tProjectType type, const gchar *fpath1, const gchar *fpath2)
{

}

void CConfig::AddFileToRecentList(tFileType type, const gchar *fpath)
{
	const gchar *groups[3];

	Lock();
	groups[0] = "C-Munipack file";
	groups[1] = (gchar*)FileTypeRecentGroup(type);
	groups[2] = NULL;
	if (groups[0]) {
		GtkRecentData *recent_data = g_new0 (GtkRecentData, 1);
		recent_data->display_name = g_path_get_basename(fpath);
		recent_data->description = g_strdup("C-Munipack file");
		switch (type)
		{
		case TYPE_TABLE:
		case TYPE_PROJECT:
		case TYPE_PROFILE:
			recent_data->mime_type = g_strdup("text/plain");
			break;
		case TYPE_CAT:
		case TYPE_VARFIND:
			recent_data->mime_type = g_strdup("application/xml");
			break;
		case TYPE_IMAGE:
		case TYPE_PHOT:
			recent_data->mime_type = g_strdup("application/octet-stream");
			break;
		default:
			break;
		}
		recent_data->app_exec = g_strjoin(" ", g_get_prgname (), "%u", NULL);
		recent_data->app_name = (gchar*)g_get_application_name();
		recent_data->groups = (gchar**)groups;
		recent_data->is_private = TRUE;
		GtkRecentManager *rc = gtk_recent_manager_get_default();
		gchar *uri = g_filename_to_uri(fpath, NULL, NULL);
		gtk_recent_manager_add_full(rc, uri, recent_data);
		g_free(uri);
		g_free(recent_data->description);
		g_free(recent_data->mime_type);
		g_free(recent_data->app_exec); 
		g_free(recent_data->display_name);
		g_free (recent_data);
	}
	Unlock();
}
