/*
** 1998-05-31 -	After a long wait, here it finally is - the gdtool GUI config
**		module! This will be *big*, I can feel it.
** 1998-06-16 -	IQ:ed up the window handling. Now the config window is only ever
**		built (created) once. It is then reused! Environmentally safe.
**		It also allows me to use the config GUI to keep all the state,
**		although I'm not exactly convinced that's what I want to do...
** 1998-06-22 -	Redesigned. Cut away all old notebook-page-creation code (~140
**		lines) and implemented a new, more modular way of doing it.
** 1998-07-26 -	Added a global cache of page descriptors, avoiding having to
**		repeat the describe-calls all the time.
** 1998-08-25 -	Implemented a slim way of having the individual page modules
**		notify this main module of program-wide things they want done when
**		config closes. As an example, it is important to rescan the directories
**		if the styles or types change.
** 1998-08-30 -	Fixed version handling in config file, at least somewhat. Also added
**		a system-wide config, which is loaded if the user doesn't have one.
** 1998-10-16 -	System-wide config path now configurable via a symbol.
*/

#include "gentoo.h"

#include <stdlib.h>

#include "dirpane.h"
#include "dialog.h"
#include "fileutil.h"
#include "xmlutil.h"
#include "iconutil.h"
#include "cmdseq.h"
#include "mount.h"

#include "cfg_gui.h"
#include "cfg_module.h"
#include "cfg_dirpane.h"
#include "cfg_cmdseq.h"
#include "cfg_buttons.h"
#include "cfg_shortcuts.h"
#include "cfg_types.h"
#include "cfg_styles.h"
#include "cfg_paths.h"
#include "cfg_mount.h"
#include "cfg_possize.h"
#include "cfg_controls.h"
#include "cfg_cmdcfg.h"

/* This should be set in the Makefile, and passed along using the -D
** compiler option, If not, let's default to nice old Slackware style.
*/
#if !defined(PTH_CFG)
#define	PTH_CFG	"/usr/local/etc/"
#endif

/* ----------------------------------------------------------------------------------------- */

typedef struct {
	MainInfo	*min;
	GtkWidget	*dlg;
	GtkWidget	*nbook;		/* A notebook widget holding all the config pages. */
	GtkWidget	*ok, *save, *cancel;
} CfgGui;

/* A global (yuck!) vector of page descriptor functions. This is where pointers to
** new pages get added.
*/
static cfg_describe	describe_page[] = {	cdp_describe,
						ccs_describe, ccc_describe,
						cst_describe, ctp_describe,
						cbt_describe,
						csc_describe, cpt_describe, cmn_describe,
						cps_describe, cct_describe,
					};

#define	CFG_PAGES	(sizeof describe_page / sizeof describe_page[0])

/* A global vector of the resulting page descriptors. */
static CfgPage		*cfg_page[CFG_PAGES] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };

/* Flags for things that need to be done when config closes. Set on request from modules.
** Example of stuff: rescan dirs, rebuild GUI. Useful because although any amount of
** modules might request e.g. a dir rescan, the flag will only be set once.
*/
static guint32		gui_flags = 0;

/* ----------------------------------------------------------------------------------------- */

void	rebuild_middle(MainInfo *min);
void	rebuild_bottom(MainInfo *min);

/* ----------------------------------------------------------------------------------------- */

/* 1998-06-26 -	Do the work of hiding the config GUI. */
static void hide_config(CfgGui *cgu)
{
	guint	i;

	for(i = 0; i < CFG_PAGES; i++)
	{
		if(cfg_page[i]->hide != NULL)
			cfg_page[i]->hide(cgu->min);
	}
	if(gui_flags & CFLG_RESET_KEYBOARD)
		kbd_context_clear(cgu->min->gui->kbd_ctx);

	if(gui_flags & CFLG_REBUILD_MIDDLE)
		rebuild_middle(cgu->min);
	if(gui_flags & CFLG_REBUILD_BOTTOM)
	{
		rebuild_bottom(cgu->min);
		csq_execute(cgu->min, "ActivateOther");
		csq_execute(cgu->min, "ActivateOther");
	}

	if(gui_flags & CFLG_FLUSH_ICONS)
		ico_flush(cgu->min);

	if(gui_flags & CFLG_RESCAN_LEFT)
		dp_rescan_dir(&cgu->min->gui->pane[0]);
	else if(gui_flags & CFLG_REDISP_LEFT)
		dp_redisplay_preserve(&cgu->min->gui->pane[0]);

	if(gui_flags & CFLG_RESCAN_RIGHT)
		dp_rescan_dir(&cgu->min->gui->pane[1]);
	else if(gui_flags & CFLG_REDISP_RIGHT)
		dp_redisplay_preserve(&cgu->min->gui->pane[1]);

	if(gui_flags & CFLG_RESET_MOUNT)
		mnt_init(cgu->min);
	if(gui_flags & CFLG_RESET_KEYBOARD)
		ctrl_keys_install(cgu->min->cfg.ctrlinfo, cgu->min->gui->kbd_ctx);

	gtk_grab_remove(cgu->dlg);
	gtk_widget_hide(cgu->dlg);
}

/* 1998-06-26 -	The user just clicked the OK button. Let all page modules know, then hide the
**		GUI.
*/
static gint evt_ok_clicked(GtkWidget *wid, gpointer data)
{
	CfgGui		*cgu = (CfgGui *) data;
	MainInfo	*min = cgu->min;
	guint		i;

	cfg_modified_set(min);

	for(i = 0; i < CFG_PAGES; i++)
	{
		if(cfg_page[i]->accept != NULL)
			cfg_page[i]->accept(min);
	}
	hide_config(cgu);
	return TRUE;
}

/* 1998-09-18 -	Broke the saving code out of the button handler, and made it globally
**		accessible.
*/
void cfg_save_all(MainInfo *min)
{
	char	*root = "GentooConfig", *home, rcname[PATH_MAX];
	FILE	*out;
	guint	i;
	CfgPage	*page;

	if((home = getenv("HOME")) != NULL)
	{
		strcpy(rcname, home);
		strcat(rcname, "/" RCNAME);
	}
	else
		return;

	cfg_modified_clear(min);
	if((out = xml_put_open(rcname)) != NULL)
	{
		xml_put_node_open(out, root);
		xml_put_text(out, "version", VERSION);
		for(i = 0; i < CFG_PAGES; i++)
		{
			if((page = describe_page[i](min)) != NULL && (page->save != NULL))
				page->save(min, out);
		}
		xml_put_node_close(out, root);
		xml_put_close(out);
	}
	else
		dlg_dialog_simple("Couldn't open configuration\nfile for output!", "Error", "Pity", NULL, NULL);
}

/* 1998-07-25 -	I guess it's becoming time to grow up and start outputting a config file.
**		XML seems to be the format of the week, so I'll just go for something like
**		that.
** 1998-09-18 -	Broke out the actual saving code and put in in a function of its own.
*/
static gint evt_save_clicked(GtkWidget *wid, gpointer data)
{
	CfgGui		*cgu = (CfgGui *) data;
	MainInfo	*min = cgu->min;
	guint		i;

	cfg_modified_clear(min);
	for(i = 0; i < CFG_PAGES; i++)
	{
		if(cfg_page[i]->accept != NULL)
			cfg_page[i]->accept(min);
	}
	cfg_save_all(min);
	hide_config(cgu);

	return TRUE;
}

/* 1998-07-12 -	User clicked the cancel button. Hide the GUI. */
static gint evt_cancel_clicked(GtkWidget *wid, gpointer data)
{
	hide_config((CfgGui *) data);
	return TRUE;
}

/* 1998-05-31 -	Build the buttons at the bottom of the config window (OK, Save, Cancel).
**		Very gimp.
*/
static int build_buttons(CfgGui *cgu)
{
	if((cgu->ok = gtk_button_new_with_label("OK")) != NULL)
	{
		if((cgu->save = gtk_button_new_with_label("Save")) != NULL)
		{
			if((cgu->cancel = gtk_button_new_with_label("Cancel")) != NULL)
			{
				gtk_signal_connect(GTK_OBJECT(cgu->ok), "clicked", GTK_SIGNAL_FUNC(evt_ok_clicked), cgu);
				gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cgu->dlg)->action_area), cgu->ok, TRUE, TRUE, 0);
				gtk_signal_connect(GTK_OBJECT(cgu->save), "clicked", GTK_SIGNAL_FUNC(evt_save_clicked), cgu);
				gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cgu->dlg)->action_area), cgu->save, TRUE, TRUE, 0);
				gtk_signal_connect(GTK_OBJECT(cgu->cancel), "clicked", GTK_SIGNAL_FUNC(evt_cancel_clicked), cgu);
				gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cgu->dlg)->action_area), cgu->cancel, TRUE, TRUE, 0);
				GTK_WIDGET_SET_FLAGS(cgu->ok, GTK_CAN_DEFAULT);
				GTK_WIDGET_SET_FLAGS(cgu->save, GTK_CAN_DEFAULT);
				GTK_WIDGET_SET_FLAGS(cgu->cancel, GTK_CAN_DEFAULT);
				gtk_widget_grab_default(cgu->ok);
				gtk_widget_show(cgu->ok);
				gtk_widget_show(cgu->save);
				gtk_widget_show(cgu->cancel);

				return 1;
			}
			gtk_widget_destroy(cgu->save);
		}
		gtk_widget_destroy(cgu->ok);
	}
	return 0;
}

/* ----------------------------------------------------------------------------------------- */

/* 1998-06-16 -	This gets called as the user clicks the close button of the config GUI.
**		Unlike what the functio name might lead you to expect, we don't destroy the
**		GUI we so laborously (sp?) created. We just hide it so we can use it again
**		later. Neat for several reasons.
*/
static gint evt_cfg_delete(GtkWidget *wid, GdkEvent *evt, gpointer data)
{
	CfgGui	*cgu = (CfgGui *) data;

	hide_config(cgu);
	
	return TRUE;
}

/* ----------------------------------------------------------------------------------------- */

/* 1998-06-16 -	Cooled up (?) this routine a lot. It is now recursive, in a rather
**		interesting manner. The point is not to reconstruct the entire config-
**		GUI each time it is needed, but rather to just build it once and then
**		hide/show it as needed.
*/
int cfg_gui(MainInfo *min)
{
	static CfgGui	cgu = { NULL, NULL };
	gchar		*name = NULL;
	guint		i;
	GtkWidget	*page_root;

	gui_flags = 0UL;

	if(cgu.dlg != NULL)
	{
		gint	page;

		page = gtk_notebook_get_current_page(GTK_NOTEBOOK(cgu.nbook));
		gtk_notebook_set_page(GTK_NOTEBOOK(cgu.nbook), 0);
		for(i = 0; i < CFG_PAGES; i++)
		{
			if(cfg_page[i]->update != NULL)
				cfg_page[i]->update(min);
		}
		gtk_widget_show(cgu.dlg);
		gtk_notebook_set_page(GTK_NOTEBOOK(cgu.nbook), page);
		gtk_grab_add(cgu.dlg);
		return 1;
	}

	/* Initialize the cache of page descriptors. */
	for(i = 0; i < CFG_PAGES; i++)
		cfg_page[i] = describe_page[i](min);

	cgu.min = min;
	cgu.dlg = gtk_dialog_new();
	gtk_widget_set_usize(cgu.dlg, -1, 464);
	gtk_signal_connect(GTK_OBJECT(cgu.dlg), "delete_event", GTK_SIGNAL_FUNC(evt_cfg_delete), &cgu);
	gtk_window_set_title(GTK_WINDOW(cgu.dlg), "Configure gentoo");
	cgu.nbook = gtk_notebook_new();
	gtk_notebook_set_tab_pos(GTK_NOTEBOOK(cgu.nbook), GTK_POS_LEFT);
	gtk_notebook_set_scrollable(GTK_NOTEBOOK(cgu.nbook), TRUE);
	gtk_widget_show(cgu.nbook);

	for(i = 0; i < CFG_PAGES; i++)
	{
		if(cfg_page[i]->init != NULL)
		{
			if((page_root = cfg_page[i]->init(min, &name)) != NULL)
			{
				GtkWidget	*hbox;

				hbox = gtk_hbox_new(FALSE, 0);
				gtk_box_pack_end(GTK_BOX(hbox), page_root, TRUE, TRUE, 5);
				gtk_widget_show(hbox);
				gtk_notebook_append_page(GTK_NOTEBOOK(cgu.nbook), hbox, gtk_label_new(name));
			}
			else
				fprintf(stderr, "**USELESS CONFIG PAGE ENCOUNTERED (index %d)\n", i);
		}
	}
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cgu.dlg)->vbox), cgu.nbook, TRUE, TRUE, 0);
	if(build_buttons(&cgu))
		return cfg_gui(min);		/* Recursion! */
	return 0;
}

/* ----------------------------------------------------------------------------------------- */

static void load_node(XmlNode *node, gpointer data)
{
	MainInfo	*min = (MainInfo *) data;
	guint		i;

	for(i = 0; i < CFG_PAGES; i++)
	{
		if(xml_node_has_name(node, cfg_page[i]->node) && cfg_page[i]->load != NULL)
		{
			cfg_page[i]->load(min, node);
			return;
		}
	}
}

/* 1998-07-26 -	Load the entire program configuration. Pretty complex stuff, made considerably
**		less so by the modularization and tree organization.
** 1998-08-30 -	Now keeps knowledge about config file name to itself. First checks if there
**		is a local config; if so, it is loaded. If not, the system-wide default
**		config from /etc/local/etc/ is used. If that fails, whine.
** 1998-10-21 -	Now returns a set of flags, rather than the single first boolean.
*/
guint32 cfg_load_config(MainInfo *min)
{
	XmlNode	*tree;
	char	name[PATH_MAX], *hpath;
	guint	i;
	guint32	flags = 0UL;

	if((hpath = getenv("HOME")) != NULL)
		sprintf(name, "%s/%s", hpath, RCNAME);
	else
		name[0] = '\0';

	/* Does the user seem to have a local config? */
	if(!fut_can_read_named(name))
		sprintf(name, PTH_CFG "%s", RCNAME + 1);	/* Nope, check for global one. */

	/* Initialize the cache of page descriptors. */
	for(i = 0; i < CFG_PAGES; i++)
		cfg_page[i] = describe_page[i](min);

	if((tree = xml_tree_load(name)) != NULL)
	{
		char	*fver;

		if(xml_get_text(tree, "version", &fver) && strcmp(fver, VERSION))
			fprintf(stderr, "**Warning: Version of config file (%s) doesn't match program version (%s)\n", fver, VERSION);
		xml_node_visit_children(tree, load_node, (gpointer) min);
		xml_tree_destroy(tree);
		if((min->cfg.wininfo.pos.flags & PSPF_SET) || (min->cfg.wininfo.size.flags & PSPF_SET))
			flags |= CLDF_WINDOW_POSSIZE;
	}
	else
	{
		dlg_dialog_simple("Couldn't find any configuration file.\n"\
			    "Using built-in minimal configuration.", "Notice", "OK", NULL, NULL);
		flags |= CLDF_NONE_FOUND;
	}
	return flags;
}

/* ----------------------------------------------------------------------------------------- */

/* 1998-08-25 -	Log a request to get something done. */
void cfg_set_flags(guint32 flags)
{
	gui_flags |= flags;
}

/* 1999-04-09 -	Set the 'configuration modified' flag. */
void cfg_modified_set(MainInfo *min)
{
	min->cfg.flags |= CFLG_CHANGED;	
}

/* 1999-04-09 -	Clear the 'configuration modified' flag. Use with care. */
void cfg_modified_clear(MainInfo *min)
{
	min->cfg.flags &= ~CFLG_CHANGED;
}
