/* 
 * Oroborus Window Manager
 *
 * Copyright (C) 2001 Ken Lynch
 * Copyright (C) 2002 Stefan Pfetzing
 *
 * OroboROX Window Manager
 * 
 * Copyright (C) 2004 Guido Schimmels
 *
 * 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 "config.h"
#include <sys/param.h>			/* for MAXPATHLEN  */
#include <stdio.h>
#include <string.h>
#include <glib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xmd.h>
#ifdef HAVE_XF68VMLIB_H
#include <X11/extensions/xf86vmode.h>
#endif
#include <ft2build.h>
#include <X11/Xft/Xft.h>
#include "keyboard.h"
#include "pixmap.h"
#include "main.h"
#include "hints.h"
#include "client.h"

#include "frame.h"
#include "i18n.h"
#include "settings.h"
#include "workspaces.h"
#include "mouse.h"
#include "stacking.h"
#include "events.h"
#include "xerror.h"

extern XftFont *xftfont;

extern char *rcfile;
extern Window last_focus[9];

#define OPTION	0
#define VALUE	1



char *borderless_windows = NULL;
char *sticky_windows = NULL;
char *iconic_windows = NULL;
char *ontop_windows = NULL;

static GC createGC(Display * dpy, Colormap cmap, char *col, int func, int inc_sw)
{
	XGCValues gv;
	XColor xc1, xc2;
	GC gc;
	int mask;

#ifdef DEBUG
	fprintf(stderr, "entering createGC\n");
	fprintf(stderr, "color=%s\n", col);
#endif

	mask = GCForeground | GCFunction;
	XAllocNamedColor(dpy, cmap, col, &xc1, &xc2);
	gv.foreground = xc2.pixel;
	gv.function = func;

	if (inc_sw)
	{
		gv.subwindow_mode = IncludeInferiors;
		mask = mask | GCSubwindowMode;
	}
	gc = XCreateGC(dpy, XDefaultRootWindow(dpy), mask, &gv);

	return gc;
}

static int parseRc(char *file, gchar * dir, char *rc[][2])
{
	char filename[MAXPATHLEN], buf[MAXPATHLEN], *lvalue, *rvalue;
	FILE *fp;
	int i;

	dbg("entering parseRc");

	if (dir)
		g_snprintf(filename, sizeof(filename), "%s/%s", dir, file);
	else
		strncpy(filename, file, sizeof(filename));

	fp = fopen(filename, "r");
	if (!fp)
		return False;

	while (fgets(buf, sizeof(buf), fp))
	{
		lvalue = strtok(buf, "=");
		if (lvalue)
		{
			for (i = 0; rc[i][OPTION]; i++)
			{
				if (!strcmp(lvalue, rc[i][OPTION]))
				{
					rvalue = strtok(NULL, "\n");
					if (rvalue)
					{
						if (rc[i][VALUE])
							g_free(rc[i][VALUE]);
						rc[i][VALUE] = g_strdup(rvalue);
#ifdef DEBUG
						fprintf(stderr, "%s=%s\n", rc[i][OPTION], rc[i][VALUE]);
#endif
					}
				}
			}
		}
	}
	fclose(fp);
	return True;
}

static int checkRc(char *rc[][2])
{
	int i, rval = True;

	DBUG("entering checkRc");

	for (i = 0; rc[i][OPTION]; i++)
	{
		if (!rc[i][VALUE])
		{
			fprintf(stderr, "missing value for option %s\n", rc[i][OPTION]);
			rval = False;
		}
	}
	return rval;
}

static char *getValue(const char *option, char *rc[][2])
{
	int i;

	DBUG("entering getValue");

	for (i = 0; rc[i][OPTION]; i++)
		if (!strcmp(option, rc[i][OPTION]))
			return rc[i][VALUE];
	return NULL;
}

static void free_rc(char *rc[][2])
{
	int i;

	for (i = 0; rc[i][OPTION]; i++)
		if (rc[i][VALUE])
		{
			g_free(rc[i][VALUE]);
			rc[i][VALUE] = NULL;
		}
}


void *current_rc;
static gboolean option_string_equals(const char *option, const char *value)
{
	return (!strcmp(value, getValue(option, current_rc)));
}

static gboolean option_bool(const char *option)
{
	return (!strcmp("true", getValue(option, current_rc)));
}

static int option_int(const char *option)
{
	return atoi(getValue(option, current_rc));
}


static void loadSimple(char *fn, char *dest)
{
	FILE *fp;
	char filename[MAXPATHLEN];

	dest[0] = '\0';
	snprintf(filename, sizeof(filename), "%s/%s", g_getenv("OROBOROX_CONFIG_HOME"), fn);
	fp = fopen(filename, "r");
	if (fp)
	{
		fgets(dest, MAXPATHLEN, fp);
		fclose(fp);
	}
	if (dest[0])
	{
		if (dest[strlen(dest) - 1] == '\n')
			dest[strlen(dest) - 1] = '\0';
	}
}





static char *title_pixmaps[5][2] = {
	{"title-1-active", "title-1-inactive"},
	{"title-2-active", "title-2-inactive"},
	{"title-3-active", "title-3-inactive"},
	{"title-4-active", "title-4-inactive"},
	{"title-5-active", "title-5-inactive"}
};

static char *button_pixmaps[BUTTON_COUNT][7] = {
	{"hide-active", "hide-inactive",  "hide-pressed", 
	"hide-disabled-active", "hide-disabled-inactive", 
	NULL, NULL},
	{"shade-active", "shade-inactive",  "shade-pressed", 
	 "shade-disabled-active", "shade-disabled-inactive",
	 "shade-toggled-active", "shade-toggled-inactive"},
	{"maximize-active", "maximize-inactive",  "maximize-pressed", 
	 "maximize-disabled-active", "maximize-disabled-inactive",
	 "maximize-toggled-active", "maximize-toggled-inactive"},
	{"close-active", "close-inactive",  "close-pressed", 
	 "close-disabled-active", "close-disabled-inactive",
	  NULL, NULL},
	{"stick-active", "stick-inactive",  "stick-pressed",
	 "stick-disabled-active", "stick-disabled-inactive",	
	 "stick-toggled-active", "stick-toggled-inactive"}
};

static char *corner_pixmaps[4][2] = {
	{"top-left-active", "top-left-inactive"},
	{"top-right-active", "top-right-inactive"},
	{"bottom-left-active", "bottom-left-inactive"},
	{"bottom-right-active", "bottom-right-inactive"}
};

static char *side_pixmaps[3][2] = {
	{"left-active", "left-inactive"},
	{"right-active", "right-inactive"},
	{"bottom-active", "bottom-inactive"}
};



void loadSettings()
{
	char theme[MAXPATHLEN];
	char scheme[MAXPATHLEN];
	XpmColorSymbol colsym[16];
	gchar *app_dir;

	/* this array must match the sequence of KeySettings enum type */
	static char *keyboardrc[][2] = {
		{"move_window_up_key", NULL},
		{"move_window_down_key", NULL},
		{"move_window_left_key", NULL},
		{"move_window_right_key", NULL},
		{"resize_window_up_key", NULL},
		{"resize_window_down_key", NULL},
		{"resize_window_left_key", NULL},
		{"resize_window_right_key", NULL},
		{"cycle_windows_key", NULL},
		{"cycle_app_key", NULL},
		{"close_window_key", NULL},
		{"hide_window_key", NULL},
		{"toggle_fullscreen_window_key", NULL},
		{"maximize_window_key", NULL},
		{"maximize_vert_key", NULL},
		{"maximize_horiz_key", NULL},
		{"toggle_fullscreen_window_all_key", NULL},
		{"maximize_window_all_key", NULL},
		{"maximize_vert_all_key", NULL},
		{"maximize_horiz_all_key", NULL},
		{"shade_window_key", NULL},
		{"raise_window_layer_key", NULL},
		{"lower_window_layer_key", NULL},
		{"next_workspace_key", NULL},
		{"prev_workspace_key", NULL},
		{"add_workspace_key", NULL},
		{"del_workspace_key", NULL},
		{"stick_window_key", NULL},
		{"workspace_1_key", NULL},
		{"workspace_2_key", NULL},
		{"workspace_3_key", NULL},
		{"workspace_4_key", NULL},
		{"workspace_5_key", NULL},
		{"workspace_6_key", NULL},
		{"workspace_7_key", NULL},
		{"workspace_8_key", NULL},
		{"workspace_9_key", NULL},
		{"move_window_next_workspace_key", NULL},
		{"move_window_prev_workspace_key", NULL},
		{"move_window_workspace_1_key", NULL},
		{"move_window_workspace_2_key", NULL},
		{"move_window_workspace_3_key", NULL},
		{"move_window_workspace_4_key", NULL},
		{"move_window_workspace_5_key", NULL},
		{"move_window_workspace_6_key", NULL},
		{"move_window_workspace_7_key", NULL},
		{"move_window_workspace_8_key", NULL},
		{"move_window_workspace_9_key", NULL},
		{"move_top_left_key", NULL},
		{"move_top_right_key", NULL},
		{"move_bottom_left_key", NULL},
		{"move_bottom_right_key", NULL},
		{"window_ops_modifiers", NULL},
		{"toggle_borders_key", NULL},
		{"raise_window_key", NULL},
		{"show_desktop_key", NULL},
		{"quit_key", NULL},
		{NULL, NULL}
	};

	static char *schemerc[16 + 1][2] = {
		{"active_text_color", NULL},
		{"inactive_text_color", NULL},
		{"active_border_color", NULL},
		{"inactive_border_color", NULL},
		{"active_color_1", NULL},
		{"inactive_color_1", NULL},
		{"active_shadow_1", NULL},
		{"inactive_shadow_1", NULL},
		{"active_hilight_1", NULL},
		{"inactive_hilight_1", NULL},
		{"active_color_2", NULL},
		{"inactive_color_2", NULL},
		{"active_shadow_2", NULL},
		{"inactive_shadow_2", NULL},
		{"active_hilight_2", NULL},
		{"inactive_hilight_2", NULL},
		{NULL, NULL}
	};


	static char *rc[][2] = {
		{"font", NULL},
		{"title_alignment", NULL},
		{"full_width_title", NULL},
		{"button_layout", NULL},
		{"button_spacing", NULL},
		{"button_offset", NULL},
		{"double_click_action", NULL},
		{"box_move", NULL},
		{"box_resize", NULL},
		{"focus_policy", NULL},
		{"raise_policy", NULL},
		{"focus_new", NULL},
		{"raise_delay", NULL},
		{"window_placement", NULL},
		{"snap_to_border", NULL},
		{"snap_to_windows", NULL},
		{"snap_width", NULL},
		{"borderless_windows", NULL},
		{"sticky_windows", NULL},
		{"iconic_windows", NULL},
		{"ontop_windows", NULL},
		{"warp_mouse", NULL},
		{"border_drag", NULL},
		{"button2_frame_click", NULL},
		{"button3_frame_click", NULL},
		{"button2_frame_drag", NULL},
		{"button3_frame_drag", NULL},
		{"maximize_state", NULL},
		{"switch_to_active_win", NULL},
		{"buttons_show_state", NULL},
		{"button_cursors", NULL},
		{"button_filter", NULL},
		{"cycle_infobox", NULL},
		{"geometry_infobox", NULL},
		{"warp_after_cycle", NULL},
		{NULL, NULL}
	};

	static char *workspacerc[][2] = {
		{"workspace_count", NULL},
		{"wrap_workspaces", NULL},
		{"cycle_workspaces", NULL},
		{NULL, NULL}
	};

	DBUG("entering settingsLoad");

	init_cursors(dpy);

	//app_dir = g_strdup(g_getenv("APP_DIR"));
	app_dir = g_get_current_dir();

	if (!(parseRc("defaults", app_dir, rc)
		  && parseRc("defaults", app_dir, workspacerc)
		  && parseRc("defaults", app_dir, schemerc) && parseRc("defaults", app_dir, keyboardrc)))
		g_error("Missing defaults file\n");

	if (!checkRc(rc))
		g_error("Missing settings in defaults file\n");
	if (!checkRc(workspacerc))
		g_error("Missing workspace settings in defaults file\n");
	if (!checkRc(schemerc))
		g_error("Missing scheme settings in defaults file\n");
	if (!checkRc(keyboardrc))
		g_error("Missing keyboard shortcuts in defaults file\n");


	loadSimple("current_theme", theme);
	parseRc("schemerc", theme, rc);
	parseRc("themerc", theme, schemerc);

	loadSimple("current_scheme", scheme);
	parseRc(scheme, NULL, schemerc);

	if (!parseRc("themerc", theme, rc))
	{
		g_snprintf(theme, sizeof(theme), "%s/%s", app_dir, "Configure/Themes/MicroGUI");
		if (!parseRc("themerc", theme, rc))
			g_error("OroboROX Error: could not open default theme: %s\n", theme);
	}

	G_CONST_RETURN gchar *config_home = g_getenv("OROBOROX_CONFIG_HOME");

	parseRc("windowrc", (gchar *) config_home, rc);
	parseRc("workspacerc", (gchar *) config_home, workspacerc);
	//parseRc("themerc", config_home, themerc);
	parseRc("keyboardrc", (gchar *) config_home, keyboardrc);

	if (rcfile)
		parseRc(rcfile, NULL, rc);

	current_rc = rc;			/* so we don't have to pass it every time */

	/* color scheme settings */
	{
		int i = 16;
		char *fg = schemerc[0][VALUE], *bg = schemerc[4][VALUE];
		XColor dummy;
		int ok;

		while (i--)
		{
			colsym[i].name = schemerc[i][OPTION];
			colsym[i].value = schemerc[i][VALUE];
		}

		if (!XAllocNamedColor(dpy, cmap, fg, &dummy, &info_fg))
			info_fg.pixel = BlackPixel(dpy, screen);
		if (!XAllocNamedColor(dpy, cmap, bg, &dummy, &info_bg))
			info_bg.pixel = WhitePixel(dpy, screen);
	}

	box_gc = createGC(dpy, cmap, "#FFFFFF", GXxor, True);
	XftColorAllocName(dpy, visual, cmap, schemerc[0][VALUE], &title_font.xft_detail[ACTIVE]);
	XftColorAllocName(dpy, visual, cmap, schemerc[1][VALUE], &title_font.xft_detail[INACTIVE]);
	XftColorAllocName(dpy, visual, cmap, "#000000", &textcolor);

	/* theme settings */

	{
		int i;

		i = 4;
		while (i--)
		{						/* corner images */
			loadPixmap(dpy, &corners[i][ACTIVE], theme, corner_pixmaps[i][ACTIVE], colsym, 16);
			loadPixmap(dpy, &corners[i][INACTIVE], theme, corner_pixmaps[i][INACTIVE], colsym, 16);
		}
		i = 3;
		while (i--)
		{						/* side images */
			loadPixmap(dpy, &sides[i][ACTIVE], theme, side_pixmaps[i][ACTIVE], colsym, 16);
			loadPixmap(dpy, &sides[i][INACTIVE], theme, side_pixmaps[i][INACTIVE], colsym, 16);
		}
		i = 5;
		while (i--)
		{						/* load title images */
			loadPixmap(dpy, &title[i][ACTIVE], theme, title_pixmaps[i][ACTIVE], colsym, 16);
			loadPixmap(dpy, &title[i][INACTIVE], theme, title_pixmaps[i][INACTIVE], colsym, 16);
		}
		i = BUTTON_COUNT;		/* load button images */
		while (i--)
		{
			int j = 7; while (j--)
				loadPixmap(dpy, &buttons[i][j], theme, button_pixmaps[i][j], colsym, 16);
		}
	}

		
	if (!getFont(dpy, &title_font, getValue("font", rc)))
		g_error("Cannot load default font\n");

	{
		char *talign = getValue("title_alignment", rc);

		if (!strcmp("left", talign))
			title_alignment = ALIGN_LEFT;
		else if (!strcmp("right", talign))
			title_alignment = ALIGN_RIGHT;
		else
			title_alignment = ALIGN_CENTER;
	}

	buttons_show_state = option_bool("buttons_show_state");
	button_cursors = option_bool("button_cursors");
	full_width_title = option_bool("full_width_title");
	strncpy(button_layout, getValue("button_layout", rc), BUTTON_COUNT + 1);
	strncpy(button_filter, getValue("button_filter", rc), BUTTON_COUNT + 1);

	{
		int i, j;
		char c, tmp[BUTTON_COUNT + 2];

		strcpy(tmp, button_layout);
		for (i = 0, j = 0; i < BUTTON_COUNT + 1 && (c = tmp[i]) != 0; i++)
		{
			if (!strchr(button_filter, c))
				button_layout[j++] = c;
		}
		button_layout[j] = 0;
	}

	//fprintf(stderr, "Mixed settings\n");
	button_spacing = option_int("button_spacing");
	button_offset = option_int("button_offset");

	box_resize = option_bool("box_resize");
	box_move = option_bool("box_move");

	focus_policy = option_int("focus_policy");
	raise_policy = option_int("raise_policy");
	focus_new = option_bool("focus_new");
	raise_delay = option_int("raise_delay");
	//raise_delay_timeout_source = g_timeout_source_new(raise_delay);	
	//g_source_set_callback(raise_delay_timeout_source, delayed_raise, NULL, NULL);
                                             
	window_placement = PLACEMENT_MOUSE;
	if (option_string_equals("window_placement", "center_root"))
		window_placement = PLACEMENT_ROOT;

	snap_to_border = option_bool("snap_to_border");
	snap_width = abs(option_int("snap_width"));
	snap_to_windows = option_bool("snap_to_windows");

	double_click_action = WINDOW_ACTION_NONE;
	if (option_string_equals("double_click_action", "shade"))
		double_click_action = WINDOW_ACTION_SHADE;
	else if (option_string_equals("double_click_action", "hide"))
		double_click_action = WINDOW_ACTION_HIDE;
	else if (option_string_equals("double_click_action", "maximize"))
		double_click_action = WINDOW_ACTION_MAXIMIZE;

	warp_mouse = option_bool("warp_mouse");
	cycle_infobox = option_bool("cycle_infobox");
	geometry_infobox = option_bool("geometry_infobox");
	warp_after_cycle = option_bool("warp_after_cycle");

	border_drag = 0;
	if (option_string_equals("border_drag", "move"))
		border_drag = DRAG_MOVE;
	if (option_string_equals("border_drag", "resize"))
		border_drag = DRAG_RESIZE;

	button2_frame_drag = 0;
	if (option_string_equals("button2_frame_drag", "move"))
		button2_frame_drag = DRAG_MOVE;
	if (option_string_equals("button2_frame_drag", "resize"))
		button2_frame_drag = DRAG_RESIZE;

	button3_frame_drag = 0;
	if (option_string_equals("button3_frame_drag", "move"))
		button3_frame_drag = DRAG_MOVE;
	if (option_string_equals("button3_frame_drag", "resize"))
		button3_frame_drag = DRAG_RESIZE;

	button2_frame_click = 0;
	if (option_string_equals("button2_frame_click", "lower"))
		button2_frame_click = LAYER_LOWER;
	if (option_string_equals("button2_frame_click", "raise"))
		button2_frame_click = LAYER_RAISE;

	button3_frame_click = 0;
	if (option_string_equals("button3_frame_click", "lower"))
		button3_frame_click = LAYER_LOWER;
	if (option_string_equals("button3_frame_click", "raise"))
		button3_frame_click = LAYER_RAISE;

	maximize_state = LOCK_SIZE;	// terry blunt 8/3/5 maximize behaviour control
	if (option_string_equals("maximize_state", "locked"))
		maximize_state = LOCK_SIZE;
	if (option_string_equals("maximize_state", "unlocked"))
		maximize_state = UNLOCK_SIZE;
	if (option_string_equals("maximize_state", "remax"))
		maximize_state = REMAX_SIZE;

	switch_to_active_win = option_bool("switch_to_active_win");

	borderless_windows = g_strdup(getValue("borderless_windows", rc));
	sticky_windows = g_strdup(getValue("sticky_windows", rc));
	iconic_windows = g_strdup(getValue("iconic_windows", rc));
	ontop_windows = g_strdup(getValue("ontop_windows", rc));

	/* Workspace settings */
	current_rc = workspacerc;
	//fprintf(stderr, "Workspace settings\n");

	workspace_count = -1;
	workspaceSetCount(abs(option_int("workspace_count")));
	wrap_workspaces = option_bool("wrap_workspaces");
	cycle_workspaces = option_bool("cycle_workspaces");

	//fprintf(stderr, "Keyboard settings\n");
	/* Keyboard settings */
	{
		int i;
		char grabbed_keys[] = {
			KEY_CYCLE_WINDOWS, KEY_NEXT_WORKSPACE, KEY_PREV_WORKSPACE,
			KEY_ADD_WORKSPACE, KEY_DEL_WORKSPACE, KEY_SHOW_DESKTOP,
			KEY_WORKSPACE_1, KEY_WORKSPACE_2, KEY_WORKSPACE_3, KEY_WORKSPACE_4, KEY_WORKSPACE_5,
			KEY_WORKSPACE_6, KEY_WORKSPACE_7, KEY_WORKSPACE_8, KEY_WORKSPACE_9
		};

		i = KEY_COUNT;
		while (i--)
			parseKeyString(dpy, &keys[i], keyboardrc[i][VALUE]);

		ungrabKeys(dpy, root);

		i = sizeof(grabbed_keys);
		while (i--)
			grabKey(dpy, &keys[(int) (grabbed_keys[i])], root);
	}


	free_rc(rc);
	free_rc(schemerc);
	free_rc(keyboardrc);
	free_rc(workspacerc);
	g_free(app_dir);
}

static void free_pixmap_pair(MyPixmap pixmap[][2], int i)
{
	while (i--)
	{
		freePixmap(dpy, &pixmap[i][ACTIVE]);
		freePixmap(dpy, &pixmap[i][INACTIVE]);
	}
}

void unloadSettings()
{
#ifdef DEBUG
	printf("entering unloadSettings\n");
#endif

	free_pixmap_pair(sides, 3);
	free_pixmap_pair(corners, 4);
	free_pixmap_pair(title, 5);

	int i = BUTTON_COUNT;

	while (i--)
	{
		int j = 7;

		while (j--)
			freePixmap(dpy, &buttons[i][j]);
			
	}

	XFreeGC(dpy, box_gc);

	XftColorFree(dpy, visual, cmap, &title_font.xft_detail[ACTIVE]);
	XftColorFree(dpy, visual, cmap, &title_font.xft_detail[INACTIVE]);
	XftColorFree(dpy, visual, cmap, &textcolor);

	g_free(borderless_windows);
	g_free(sticky_windows);
	g_free(iconic_windows);
	g_free(ontop_windows);

	free_cursors();
}

void reloadSettings()
{
#ifdef DEBUG
	printf("entering reloadSettings\n");
#endif

	GList *l;

	for (l = clients; l != NULL; l = l->next)
	{
		Client *c = (Client *) l->data;

		client_ungravitate(c);
		clientUngrabKeys(c);
	}

	unloadSettings();
	loadSettings();


	for (l = clients; l != NULL; l = l->next)
	{
		Client *c = (Client *) l->data;

		ping_timeout_remove(c);
		client_gravitate(c);
		client_configure_xywh(c);
		clientGrabKeys(c);

		{
			int i = BUTTON_COUNT; do
			{
			if (button_cursors)
				XDefineCursor(dpy, c->buttons[BUTTON_COUNT - i], button_cursor[BUTTON_COUNT - i]);
			else
				XUndefineCursor(dpy, c->buttons[BUTTON_COUNT - i]);
			} while (--i > 0);
		}

	
		int j;

		for (j = 0; j < 4; j++)
			XDefineCursor(dpy, c->corners[j], resize_cursor[j]);
		for (j = 0; j < 3; j++)
			XDefineCursor(dpy, c->sides[j], border_drag == DRAG_MOVE ? None : resize_cursor[j + 4]);

		frameDraw(c);
	}

	set_input_focus(last_focus[workspace]);
	Client *c = client_of_window(last_focus[workspace]);
	if (c && c->has_decor) decorate(c, ACTIVE);
}

