/*
 *  Copyright (C) 2001 Matthew Mueller
 *            (C) 2002 Jorn Baayen <jorn@nl.linux.org>
 *	      (C) 2003 Marco Pesenti Gritti <mpeseng@tin.it>
 *
 *  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, 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 "ephy-state.h"
#include "ephy-file-helpers.h"
#include "ephy-node-db.h"
#include "ephy-types.h"
#include "ephy-node-common.h"

#include <string.h>
#include <gtk/gtkwindow.h>
#include <gtk/gtkpaned.h>

#define STATES_FILE "states.xml"
#define EPHY_STATES_XML_VERSION "1.0"

enum
{
	EPHY_NODE_STATE_PROP_NAME = 2,
	EPHY_NODE_STATE_PROP_WIDTH = 3,
	EPHY_NODE_STATE_PROP_HEIGHT = 4,
	EPHY_NODE_STATE_PROP_MAXIMIZE = 5,
	EPHY_NODE_STATE_PROP_POSITION_X = 6,
	EPHY_NODE_STATE_PROP_POSITION_Y = 7,
	EPHY_NODE_STATE_PROP_SIZE = 8,
	EPHY_NODE_STATE_PROP_POSITION = 9
};

static EphyNode *states = NULL;
static EphyNodeDb *states_db = NULL;

static void
ephy_states_load (void)
{
	xmlDocPtr doc;
	xmlNodePtr root, child;
	char *xml_file;
	char *tmp;

	xml_file = g_build_filename (ephy_dot_dir (),
                                     STATES_FILE,
                                     NULL);

	if (g_file_test (xml_file, G_FILE_TEST_EXISTS) == FALSE)
		return;

	doc = xmlParseFile (xml_file);
	if (doc == NULL)
	{
		g_warning ("Failed to load state file.\n");
		return;
	}

	root = xmlDocGetRootElement (doc);

	tmp = xmlGetProp (root, "version");
	if (tmp != NULL && strcmp (tmp, EPHY_STATES_XML_VERSION) == 0)
	{
		child = root->children;
	}
	else
	{
		g_warning ("Old version of states.xml discarded");
		child = NULL;
	}
	g_free (tmp);

	for (; child != NULL; child = child->next)
	{
		EphyNode *node;

		node = ephy_node_new_from_xml (states_db, child);
	}

	xmlFreeDoc (doc);

	g_free (xml_file);
}

static void
ephy_states_save (void)
{
	xmlDocPtr doc;
	xmlNodePtr root;
	GPtrArray *children;
	int i;
	char *xml_file;

	if (states == NULL) return;

	xml_file = g_build_filename (ephy_dot_dir (),
                                     STATES_FILE,
                                     NULL);

	/* save nodes to xml */
	xmlIndentTreeOutput = TRUE;
	doc = xmlNewDoc ("1.0");

	root = xmlNewDocNode (doc, NULL, "ephy_states", NULL);
	xmlSetProp (root, "version", EPHY_STATES_XML_VERSION);
	xmlDocSetRootElement (doc, root);

	children = ephy_node_get_children (states);
	for (i = 0; i < children->len; i++)
	{
		EphyNode *kid;

		kid = g_ptr_array_index (children, i);

		ephy_node_save_to_xml (kid, root);
	}
	ephy_node_thaw (states);

	ephy_file_save_xml (xml_file, doc);
	g_free (xml_file);
}

static EphyNode *
find_by_name (const char *name)
{
	EphyNode *result = NULL;
	GPtrArray *children;
	int i;

	children = ephy_node_get_children (states);
	for (i = 0; i < children->len; i++)
	{
		EphyNode *kid;
		const char *node_name;

		kid = g_ptr_array_index (children, i);

		node_name = ephy_node_get_property_string
			(kid, EPHY_NODE_STATE_PROP_NAME);
		if (strcmp (node_name, name) == 0)
		{
			result = kid;
		}
	}
	ephy_node_thaw (states);

	return result;
}

static void
ensure_states (void)
{
	if (states == NULL)
	{
		states_db = ephy_node_db_new (EPHY_NODE_DB_STATES);
		states = ephy_node_new_with_id (states_db, STATES_NODE_ID);
		ephy_states_load ();
	}
}

static void
ephy_state_window_set_size (GtkWidget *window, EphyNode *node)
{
	int width;
	int height;
	gboolean maximize, size;

	width = ephy_node_get_property_int (node, EPHY_NODE_STATE_PROP_WIDTH);
	height = ephy_node_get_property_int (node, EPHY_NODE_STATE_PROP_HEIGHT);
	maximize = ephy_node_get_property_boolean (node, EPHY_NODE_STATE_PROP_MAXIMIZE);
	size = ephy_node_get_property_boolean (node, EPHY_NODE_STATE_PROP_SIZE);

	if (size)
	{
		gtk_window_set_default_size
			(GTK_WINDOW (window), width, height);
	}

	if (maximize)
	{
		gtk_window_maximize (GTK_WINDOW (window));
	}
}

static void
ephy_state_window_set_position (GtkWidget *window, EphyNode *node)
{
	GdkScreen *screen;
	int x, y;
	int screen_width, screen_height;
	gboolean maximize, size;

	g_return_if_fail (GTK_IS_WINDOW (window));

	/* Setting the default size doesn't work when the window is already showing. */
	g_return_if_fail (!GTK_WIDGET_VISIBLE (window));

	maximize = ephy_node_get_property_boolean (node, EPHY_NODE_STATE_PROP_MAXIMIZE);
	size = ephy_node_get_property_boolean (node, EPHY_NODE_STATE_PROP_POSITION);

	/* Don't set the position of the window if it is maximized */
	if (!maximize & size)
	{
		x = ephy_node_get_property_int (node, EPHY_NODE_STATE_PROP_POSITION_X);
		y = ephy_node_get_property_int (node, EPHY_NODE_STATE_PROP_POSITION_Y);

		screen = gtk_window_get_screen (GTK_WINDOW (window));
		screen_width  = gdk_screen_get_width  (screen);
		screen_height = gdk_screen_get_height (screen);

		if ((x <= screen_width) && (y <= screen_height) &&
		    (x >= 0) && (y >= 0))
		{
			gtk_window_move (GTK_WINDOW (window), x, y);
		}
	}
}


static void
ephy_state_window_save_size (GtkWidget *window, EphyNode *node)
{
	int width, height;
	gboolean maximize;
	GdkWindowState state;
	GValue value = { 0, };

	state = gdk_window_get_state (GTK_WIDGET (window)->window);
	maximize = ((state & GDK_WINDOW_STATE_MAXIMIZED) > 0);

	gtk_window_get_size (GTK_WINDOW(window),
			     &width, &height);

	if (!maximize)
	{
		g_value_init (&value, G_TYPE_INT);
		g_value_set_int (&value, width);
		ephy_node_set_property (node, EPHY_NODE_STATE_PROP_WIDTH,
				        &value);
		g_value_unset (&value);

		g_value_init (&value, G_TYPE_INT);
		g_value_set_int (&value, height);
		ephy_node_set_property (node, EPHY_NODE_STATE_PROP_HEIGHT,
				        &value);
		g_value_unset (&value);

		g_value_init (&value, G_TYPE_BOOLEAN);
		g_value_set_boolean (&value, TRUE);
		ephy_node_set_property (node, EPHY_NODE_STATE_PROP_SIZE,
				        &value);
		g_value_unset (&value);
	}

	g_value_init (&value, G_TYPE_BOOLEAN);
	g_value_set_boolean (&value, maximize);
	ephy_node_set_property (node, EPHY_NODE_STATE_PROP_MAXIMIZE,
			        &value);
	g_value_unset (&value);
}

static void
ephy_state_window_save_position (GtkWidget *window, EphyNode *node)
{
	int x,y;
	gboolean maximize;
	GdkWindowState state;
	GValue value = { 0, };

	state = gdk_window_get_state (GTK_WIDGET (window)->window);
	maximize = ((state & GDK_WINDOW_STATE_MAXIMIZED) > 0);

	/* Don't save the position if maximized */
	if (!maximize)
	{
		gtk_window_get_position (GTK_WINDOW (window), &x, &y);

		g_value_init (&value, G_TYPE_INT);
		g_value_set_int (&value, x);
		ephy_node_set_property (node, EPHY_NODE_STATE_PROP_POSITION_X,
				        &value);
		g_value_unset (&value);

		g_value_init (&value, G_TYPE_INT);
		g_value_set_int (&value, y);
		ephy_node_set_property (node, EPHY_NODE_STATE_PROP_POSITION_Y,
				        &value);
		g_value_unset (&value);

		g_value_init (&value, G_TYPE_BOOLEAN);
		g_value_set_boolean (&value, TRUE);
		ephy_node_set_property (node, EPHY_NODE_STATE_PROP_POSITION,
				        &value);
		g_value_unset (&value);
	}
}

static void
ephy_state_window_save (GtkWidget *widget, EphyNode *node)
{
	EphyStateWindowFlags flags;

	flags = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "state_flags"));

	if (flags & EPHY_STATE_WINDOW_SAVE_SIZE)
	{
		ephy_state_window_save_size (widget, node);
	}

	if (flags & EPHY_STATE_WINDOW_SAVE_POSITION)
	{
		ephy_state_window_save_position (widget, node);
	}
}

static gboolean
window_configure_event_cb (GtkWidget *widget,
			   GdkEventConfigure *event,
			   EphyNode *node)
{
	GdkWindowState state;

	state = gdk_window_get_state (widget->window);

	if (!(state & GDK_WINDOW_STATE_FULLSCREEN))
	{
		ephy_state_window_save (widget, node);
	}

	return FALSE;
}

static gboolean
window_state_event_cb (GtkWidget *widget,
		       GdkEventWindowState *event,
		       EphyNode *node)
{
	if (!(event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN))
	{
		ephy_state_window_save (widget, node);
	}

	return FALSE;
}

static EphyNode *
create_window_node (const char *name,
		    int default_width,
		    int default_height,
		    EphyStateWindowFlags flags)
{
	EphyNode *node;
	GValue value = { 0, };

	node = ephy_node_new (states_db);
	ephy_node_add_child (states, node);

	g_value_init (&value, G_TYPE_STRING);
	g_value_set_string (&value, name);
	ephy_node_set_property (node, EPHY_NODE_STATE_PROP_NAME,
			        &value);
	g_value_unset (&value);

	g_value_init (&value, G_TYPE_BOOLEAN);
	g_value_set_boolean (&value, FALSE);
	ephy_node_set_property (node, EPHY_NODE_STATE_PROP_MAXIMIZE,
			        &value);
	g_value_unset (&value);

	if (flags & EPHY_STATE_WINDOW_SAVE_SIZE)
	{
		g_value_init (&value, G_TYPE_BOOLEAN);
		g_value_set_boolean (&value, TRUE);
		ephy_node_set_property (node, EPHY_NODE_STATE_PROP_SIZE,
				        &value);
		g_value_unset (&value);

		g_value_init (&value, G_TYPE_INT);
		g_value_set_int (&value, default_width);
		ephy_node_set_property (node, EPHY_NODE_STATE_PROP_WIDTH,
				        &value);
		g_value_unset (&value);

		g_value_init (&value, G_TYPE_INT);
		g_value_set_int (&value, default_height);
		ephy_node_set_property (node, EPHY_NODE_STATE_PROP_HEIGHT,
				        &value);
		g_value_unset (&value);
	}

	return node;
}

void
ephy_state_add_window (GtkWidget *window,
		       const char *name,
		       int default_width,
		       int default_height,
		       EphyStateWindowFlags flags)
{
	EphyNode *node;

	ensure_states ();

	node = find_by_name (name);
	if (node == NULL)
	{
		node = create_window_node (name, default_width, default_height, flags);
	}

	ephy_state_window_set_size (window, node);
	ephy_state_window_set_position (window, node);

	g_object_set_data (G_OBJECT (window), "state_flags", GINT_TO_POINTER (flags));

	g_signal_connect (window, "configure_event",
			  G_CALLBACK (window_configure_event_cb), node);
	g_signal_connect (window, "window_state_event",
			  G_CALLBACK (window_state_event_cb), node);
}

static gboolean
paned_size_allocate_cb (GtkWidget *paned,
			GtkAllocation *allocation,
			EphyNode *node)
{
	int width;
	GValue value = { 0, };

	width = gtk_paned_get_position (GTK_PANED (paned));

	g_value_init (&value, G_TYPE_INT);
	g_value_set_int (&value, width);
	ephy_node_set_property (node, EPHY_NODE_STATE_PROP_WIDTH,
			        &value);
	g_value_unset (&value);

	return FALSE;
}

void
ephy_state_add_paned (GtkWidget *paned,
		      const char *name,
		      int default_width)
{
	EphyNode *node;
	int width;

	ensure_states ();

	node = find_by_name (name);
	if (node == NULL)
	{
		GValue value = { 0, };

		node = ephy_node_new (states_db);
		ephy_node_add_child (states, node);

		g_value_init (&value, G_TYPE_STRING);
		g_value_set_string (&value, name);
		ephy_node_set_property (node, EPHY_NODE_STATE_PROP_NAME,
				        &value);
		g_value_unset (&value);

		g_value_init (&value, G_TYPE_INT);
		g_value_set_int (&value, default_width);
		ephy_node_set_property (node, EPHY_NODE_STATE_PROP_WIDTH,
				        &value);
		g_value_unset (&value);
	}

	width = ephy_node_get_property_int (node, EPHY_NODE_STATE_PROP_WIDTH);
	gtk_paned_set_position (GTK_PANED (paned), width);

	g_signal_connect (paned, "size_allocate",
			  G_CALLBACK (paned_size_allocate_cb), node);
}

void
ephy_state_save (void)
{
	if (states)
	{
		ephy_states_save ();
		ephy_node_unref (states);
		g_object_unref (states_db);
		states = NULL;
		states_db = NULL;
	}
}
