/*
    Copyright (C) 2008-2010 Stefan Haller

    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 <http://www.gnu.org/licenses/>.
*/

#include <config.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include "xml.h"
#include "general.h"

gchar * xmlCharToGChar(xmlChar * str, gboolean dofree)
{
	if (str == NULL)
	{
		return NULL;
	}
	else
	{
		gchar * result = g_strdup((gchar *)str);
		if (dofree)
			xmlFree(str);
		return result;
	}
}

gboolean xmlCharToBoolean(xmlChar * str, gboolean dofree)
{
	if (str == NULL)
	{
		return FALSE;
	}
	else
	{
		gboolean result = xmlStrcmp(str, (const xmlChar *)"yes") == 0;
		if (dofree)
			xmlFree(str);
		return result;
	}
}

const xmlChar * booleanToXmlChar(gboolean value)
{
	if (value)
		return (const xmlChar *)"yes";
	else
		return (const xmlChar *)"no";
}

struct profile_node * parse_profiles(xmlNodePtr node)
{
	struct profile_node * profiles = NULL;
	struct profile_node * profile = NULL;
	
	while (node != NULL)
	{
		if ((!xmlStrcmp(node->name, (const xmlChar *)"profile")))
		{
			if (profile == NULL)
			{
				profile = g_new(struct profile_node, 1);
				profiles = profile;
			}
			else
			{
				profile->next = g_new(struct profile_node, 1);
				profile = profile->next;
			}

			profile->name = xmlCharToGChar(xmlGetProp(node, (const xmlChar *)"name"), TRUE);
			profile->next = NULL;
			profile->files = NULL;

			struct file_node * file = NULL;

			xmlNodePtr child = node->children;

			while (child != NULL)
			{
				if ((!xmlStrcmp(child->name, (const xmlChar *)"file")))
				{
					if (file == NULL)
					{
						file = g_new(struct file_node, 1);
						profile->files = file;
					}
					else
					{
						file->next = g_new(struct file_node, 1);
						file = file->next;
					}

					file->filename = xmlCharToGChar(xmlGetProp(child, (const xmlChar *)"name"), TRUE);
					file->is_dir = xmlCharToBoolean(xmlGetProp(child, (const xmlChar *)"isdir"), TRUE);
					file->use_subfolders = xmlCharToBoolean(xmlGetProp(child, (const xmlChar *)"usesubfolders"), TRUE);

					file->next = NULL;
				}
				child = child->next;
			}
		}
		node = node->next;
	}

	return profiles;
}

void parse_settings(struct settings * set, xmlNodePtr node)
{
	while (node != NULL)
	{
		if ((!xmlStrcmp(node->name, (const xmlChar *)"setting")))
		{
			xmlChar * name = xmlGetProp(node, (const xmlChar *)"name");
			xmlChar * value = xmlGetProp(node, (const xmlChar *)"value");

			if ((name != NULL) && (value != NULL))
			{
				if ((!xmlStrcmp(name, (const xmlChar *)"interval")))
				{
					if (atoi((char *)value))
					{
						set->interval = atoi((char *)value);
					}
				}
				if ((!xmlStrcmp(name, (const xmlChar *)"changebyinterval")))
				{
					set->changebyinterval = xmlCharToBoolean(value, FALSE);
				}
				if ((!xmlStrcmp(name, (const xmlChar *)"changeeverylaunch")))
				{
					set->changeeverylaunch = xmlCharToBoolean(value, FALSE);
				}
				#ifdef CACHE_FILE_LIST
				if ((!xmlStrcmp(name, (const xmlChar *)"cachefilelist")))
				{
					set->cachefilelist = xmlCharToBoolean(value, FALSE);
				}
				#endif
				if ((!xmlStrcmp(name, (const xmlChar *)"mousewheel")))
				{
					set->mouse_wheel = xmlCharToBoolean(value, FALSE);
				}
				if ((!xmlStrcmp(name, (const xmlChar *)"defaultprofile")))
				{
					set->default_profile = xmlCharToGChar(value, FALSE);
				}
				if ((!xmlStrcmp(name, (const xmlChar *)"filters")))
				{
					set->filters = xmlCharToGChar(value, FALSE);
				}
			}
			xmlFree(name);
			xmlFree(value);
		}
		node = node->next;
	}
}

struct module_node * parse_modules(xmlNodePtr node)
{
	struct module_node * modules = NULL;
	struct module_node * module = NULL;
	
	while (node != NULL)
	{
		if ((!xmlStrcmp(node->name, (const xmlChar *)"module")))
		{
			if (module == NULL)
			{
				module = g_new(struct module_node, 1);
				modules = module;
			}
			else
			{
				module->next = g_new(struct module_node, 1);
				module = module->next;
			}
			module->name = xmlCharToGChar(xmlGetProp(node, (const xmlChar *)"name"), TRUE);
			module->next = NULL;
		}
		node = node->next;
	}

	return modules;
}

struct settings * load_settings()
{
	struct settings * set = g_new(struct settings, 1);

	set->interval = 5;
	set->changebyinterval = FALSE;
	set->changeeverylaunch = FALSE;
	#ifdef CACHE_FILE_LIST
	set->cachefilelist = FALSE;
	#endif
	set->mouse_wheel = TRUE;
	set->default_profile = NULL;
	set->profiles = NULL;
	set->modules = NULL;
	set->filters = NULL;

	gchar * filename = get_setting_file();

	xmlDocPtr doc;
	xmlNodePtr root, node;

	doc = xmlParseFile(filename);

	if (doc != NULL)
	{
		root = xmlDocGetRootElement(doc);
		if (root != NULL)
		{
			node = root->children;

			while (node != NULL)
			{
				if ((!xmlStrcmp(node->name, (const xmlChar *)"profiles")))
				{
					set->profiles = parse_profiles(node->children);
				}
				if ((!xmlStrcmp(node->name, (const xmlChar *)"settings")))
				{
					parse_settings(set, node->children);
				}
				if ((!xmlStrcmp(node->name, (const xmlChar *)"modules")))
				{
					set->modules = parse_modules(node->children);
				}
				node = node->next;
			}
		}
		xmlFreeDoc(doc);
	}

	xmlCleanupParser();
	#ifdef DEBUG
	xmlMemoryDump();
	#endif

	g_free(filename);

	return set;
}

void write_profiles(xmlNodePtr root, struct profile_node * profiles)
{
	struct profile_node * profile = profiles;
	while (profile != NULL)
	{
		xmlNodePtr node = xmlNewChild(root, NULL, (const xmlChar *)"profile", NULL);
		xmlSetProp(node, (const xmlChar *)"name", (xmlChar *)profile->name);

		struct file_node * file = profile->files;
		while (file != NULL)
		{
			xmlNodePtr node2 = xmlNewChild(node, NULL, (const xmlChar *)"file", NULL);
			xmlSetProp(node2, (const xmlChar *)"name", (xmlChar *)file->filename);
			xmlSetProp(node2, (const xmlChar *)"isdir", booleanToXmlChar(file->is_dir));
			xmlSetProp(node2, (const xmlChar *)"usesubfolders", booleanToXmlChar(file->use_subfolders));
			file = file->next;
		}
		profile = profile->next;
	}
}

void write_settings(xmlNodePtr root, struct settings * set)
{
	xmlNodePtr node;
	
	node = xmlNewChild(root, NULL, (const xmlChar *)"setting", NULL);
	xmlSetProp(node, (const xmlChar *)"name", (const xmlChar *)"interval");
	gchar * str = g_strdup_printf("%i", set->interval);
	xmlSetProp(node, (const xmlChar *)"value", (xmlChar *)str);
	g_free(str);

	node = xmlNewChild(root, NULL, (const xmlChar *)"setting", NULL);
	xmlSetProp(node, (const xmlChar *)"name", (const xmlChar *)"changebyinterval");
	xmlSetProp(node, (const xmlChar *)"value", booleanToXmlChar(set->changebyinterval));

	node = xmlNewChild(root, NULL, (const xmlChar *)"setting", NULL);
	xmlSetProp(node, (const xmlChar *)"name", (const xmlChar *)"changeeverylaunch");
	xmlSetProp(node, (const xmlChar *)"value", booleanToXmlChar(set->changeeverylaunch));

	#ifdef CACHE_FILE_LIST
	node = xmlNewChild(root, NULL, (const xmlChar *)"setting", NULL);
	xmlSetProp(node, (const xmlChar *)"name", (const xmlChar *)"cachefilelist");
	xmlSetProp(node, (const xmlChar *)"value", booleanToXmlChar(set->cachefilelist));
	#endif

	node = xmlNewChild(root, NULL, (const xmlChar *)"setting", NULL);
	xmlSetProp(node, (const xmlChar *)"name", (const xmlChar *)"mousewheel");
	xmlSetProp(node, (const xmlChar *)"value", booleanToXmlChar(set->mouse_wheel));

	node = xmlNewChild(root, NULL, (const xmlChar *)"setting", NULL);
	xmlSetProp(node, (const xmlChar *)"name", (const xmlChar *)"defaultprofile");
	xmlSetProp(node, (const xmlChar *)"value", (xmlChar *)set->default_profile);

	node = xmlNewChild(root, NULL, (const xmlChar *)"setting", NULL);
	xmlSetProp(node, (const xmlChar *)"name", (const xmlChar *)"filters");
	xmlSetProp(node, (const xmlChar *)"value", (xmlChar *)set->filters);
}

void write_modules(xmlNodePtr root, struct module_node * modules)
{
	struct module_node * module = modules;
	while (module != NULL)
	{
		xmlNodePtr node = xmlNewChild(root, NULL, (const xmlChar *)"module", NULL);
		xmlSetProp(node, (const xmlChar *)"name", (xmlChar *)module->name);
		module = module->next;
	}
}

void save_settings(struct settings * set)
{
	xmlDocPtr doc = xmlNewDoc((const xmlChar *) "1.0");
	xmlNodePtr root, node;
	
	root = xmlNewNode(NULL, (const xmlChar *)"desktopnova");
	xmlDocSetRootElement(doc, root);

	node = xmlNewChild(root, NULL, (const xmlChar *)"profiles", NULL);
	write_profiles(node, set->profiles);

	node = xmlNewChild(root, NULL, (const xmlChar *)"settings", NULL);
	write_settings(node, set);

	node = xmlNewChild(root, NULL, (const xmlChar *)"modules", NULL);
	write_modules(node, set->modules);

	gchar * filename = get_setting_file();
	FILE * f = fopen(filename, "w");
	xmlDocFormatDump(f, doc, TRUE);
	fclose(f);
	g_free(filename);

	xmlFreeDoc(doc);

	xmlCleanupParser();
	#ifdef DEBUG
	xmlMemoryDump();
	#endif
}

void free_settings(struct settings * set)
{
	struct file_node * file;
	struct file_node * file_old;
	struct profile_node * profile;
	struct profile_node * profile_old;
	struct module_node * module;
	struct module_node * module_old;

	profile = set->profiles;

	while (profile != NULL)
	{
		file = profile->files;
		while (file != NULL)
		{
			g_free(file->filename);
			file_old = file;
			file = file->next;
			g_free(file_old);
		}
		g_free(profile->name);
		profile_old = profile;
		profile = profile->next;
		g_free(profile_old);
	}

	module = set->modules;
	while (module != NULL)
	{
		g_free(module->name);
		module_old = module;
		module = module->next;
		g_free(module_old);
	}

	g_free(set->default_profile);
	g_free(set->filters);

	g_free(set);
}

