#include "global.hh"

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xproto.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <locale.h>
#include <libintl.h>
#include <X11/Xlocale.h>

#include "main.hh"
#include "config.hh"
#include "windows.hh"
#include "menus.hh"
#include "style.hh"
#include "bindings.hh"
#include "events.hh"
#include "pager.hh"
#include "log.hh"
#include "misc.hh"
#include "anim.hh"

void sig_done (int);
void sig_chld (int);
int usage_print ();
int atoms_init ();
XErrorHandler catch_redirect_error (Display *, XErrorEvent*);
XErrorHandler error_handler (Display *, XErrorEvent*);
int swm_done ();

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

Display		*dpy;
unsigned long	screen;
Window		root_window;
Rect		root;
int		root_depth;	
Colormap	root_colormap;
Visual*		root_visual;

int swm_mode = MODE_RUN;
char *home_dir = NULL;
char *restart_cmd = NULL;
int g_argc;
char **g_argv;
int swm_flags = 0;
Cursor cursor;
int origin_stick;
int border_stick;
int gest_stick;
char *root_xf = NULL;

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

int
main (int argc, char **argv)
{
	g_argc = argc;
	g_argv = argv;

	setlocale (LC_ALL, ""); // read locale
	if (!XSupportsLocale()) fprintf(stderr, "Xlib does not support locale");
	else if (XSetLocaleModifiers("") == NULL) fprintf (stderr, "Xlib cannot set locale modifiers");

	home_dir = getenv ("HOME");

	char *config_file = NULL;
	char *dpy_name = NULL;

	// parse command line
	for (int i = 1; i < argc; i++)
	{
		if (strncasecmp (argv[i], "-v", 2) == 0 || strncasecmp (argv[i], "--version", 9) == 0) {
			printf ("SWM ver. %s\n", VERSION);
			return 0;
		}
		else if (strncasecmp (argv[i], "-h", 2) == 0 || strncasecmp (argv[i], "--help", 6) == 0) {
			usage_print ();
			return 0;
		}
		else if (strncasecmp (argv[i], "-d", 2) == 0) {
			i++;
			if (i >= argc) usage_print (); else dpy_name = argv[i];
		}
		else if (strncasecmp (argv[i], "-f", 2) == 0) {
			i++;
			if (i >= argc) usage_print (); else config_file = argv[i];
		}
		else if (strncasecmp (argv[i], "--unlock", 7) == 0) {
			// useful when swm crashes and you have some windows unreachable
			// on the desktop above or on the left from top left corner
			i++;
			dsk.lock = 0;
		}
		else
			usage_print ();
	};

	// connect to X server
	dpy = XOpenDisplay (dpy_name);
	if (!dpy) {
		fprintf (stderr, "swm fatal: can't open display %s\n", XDisplayName (dpy_name));
		exit (1);
	};

	int i = XConnectionNumber (dpy);
	if (fcntl (i, F_SETFD, 1) == -1)
	{
		fprintf (stderr, "swm fatal: close-on-exec failed\n");
		exit (1);
	};
	 
	screen = DefaultScreen (dpy);
	root.wd = DisplayWidth (dpy, screen);
	root.ht = DisplayHeight (dpy, screen);
	
	root_window = RootWindow (dpy, screen);
	if (!root_window) {
		fprintf (stderr, "swm fatal: screen %ld is not a valid screen\n", screen);
		exit (1);
	};

	if (XSetLocaleModifiers ("") == NULL)
		fprintf (stderr, "swm warning: X server doesn't support locale\n");

	// default config - for other defaults see constructors of WinStyle, AnimStyle etc.
	//root_mycolor = black;
	root_mycolor = NULL; // leave intact
	pointer_fore = black;
	pointer_back = white;

	int stickiness = (root.wd > root.ht ? root.wd : root.ht)/100;
	origin_stick = stickiness;
	border_stick = stickiness;
	gest_stick = 2*stickiness;

	win_list.menu = new SwitchToMenu;
	win_list.menu->name = (char *) "winlist";
	menu_list.add (win_list.menu);

	win_list.hidden_menu = new HiddenWindowsMenu;
	win_list.hidden_menu->name = (char *) "hiddenlist";
	menu_list.add (win_list.hidden_menu);

	double_click_ms = 300;

	// parse config file
	ConfigParser *cps;
	lps = cps = new ConfigParser;


	// first try specified
	if (!config_file || !cps->parse (config_file)) {
		// config file either unspecified or its reading failed
		// quietly try home config, then system wide config
		if (!cps->parse ((char *) HOME_CONFIG, 1))
			if (!cps->parse ((char *) SYS_CONFIG)) {
				fprintf (stderr, "swm fatal: system wide config file \"%s\" not found\n", SYS_CONFIG);
				exit (1);
			};
	};

	// install signal handlers
	signal (SIGINT, sig_done);
	signal (SIGHUP, sig_done);
	signal (SIGQUIT, sig_done);
	signal (SIGTERM, sig_done);
	signal (SIGCHLD, sig_chld);

	// try to install WM
	XSetErrorHandler ((XErrorHandler) catch_redirect_error);

	XSelectInput (dpy, root_window, KeyPressMask | KeyReleaseMask | ButtonPressMask
		| EnterWindowMask | SubstructureRedirectMask | SubstructureNotifyMask);
	XSync (dpy, 0);

	XSetErrorHandler ((XErrorHandler) error_handler);

	// we don't reparent and some apps inherits cursor from parent
	// (=root), so change the root cursor (it also looks better)
	pointer_fore.init ();
	pointer_back.init ();
	cursor = XCreateFontCursor (dpy, XC_left_ptr);
	XRecolorCursor (dpy, cursor, &pointer_fore.xcolor, &pointer_back.xcolor);
	XSetWindowAttributes at;
	at.cursor = cursor;
	XChangeWindowAttributes (dpy, root_window, CWCursor, &at);

	// set root background
	XWindowAttributes wat;
	XGetWindowAttributes(dpy, root_window, &wat);
	//root.wd = at.width;
	//root.ht = at.height;
	root_depth = wat.depth;
	root_colormap = wat.colormap;
	root_visual = wat.visual;

	root_mypixmap = new MyPixmap ();
	
	if (root_mypixmap->load (root_xf))
		root_mypixmap->set (root_window);
	else if (root_mycolor.name) {
		root_mycolor.init ();
		XSetWindowBackground (dpy, root_window, root_mycolor.pixel);
		XClearWindow (dpy, root_window);
	};

	// init subsystems (order is important!)
	fake_win_init ();
	timer_init ();
	atoms_init ();
	swm_font.init ();
	my_pix_init ();
	xft_draw=XftDrawCreate (dpy, root_window, root_visual, root_colormap);
	
	menu_style.init ();
	menu_list.init ();
	dwin_style.init ();
	as.init ();
	info_window.init ();
	log_window.init ();
	dsk.init ();
	pager.init ();
	active_list.init ();
	buttons_init ();

	win_list.init (); // add frame to all windows etc.

	//  XSetInputFocus (dpy, PointerRoot, RevertToParent, CurrentTime);

	event = new Event;

	// handle events
	while (1) {
		/*
		if (!event->check_mask (SubstructureRedirectMask | SubstructureNotifyMask
				 | EnterWindowMask | LeaveWindowMask | ExposureMask
				 | KeyPressMask | KeyReleaseMask
				 | PointerMotionMask | ButtonMotionMask
				 | ButtonPressMask | ButtonReleaseMask))
			cq.exec ();
		else
		*/
		event->next ();
		event->handle ();
	};
}

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

int
swm_done ()
{
	win_list.done ();
	pager.done ();
	info_window.done ();
	log_window.done ();
	timer_done ();
	menu_list.done ();

	delete event;

	// restore default cursor
	XSetWindowAttributes at;
	at.cursor = None;
	XChangeWindowAttributes (dpy, root_window, CWCursor, &at);

	XSelectInput (dpy, root_window, NoEventMask);

	XSetInputFocus (dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
	
	XCloseDisplay (dpy);

	if (swm_mode == MODE_RESTART) {
		if (restart_cmd) {
			execvp (restart_cmd, g_argv);
			fprintf (stderr, "swm warning: call of \"%s\" failed!\n", restart_cmd);
		};
		execvp (g_argv[0], g_argv);
		fprintf (stderr, "swm fatal: attempt to restart failed!\n");
		exit (1);
	}
	else
		exit (0);
}


void
sig_done (int nonsense)
{
	swm_mode = MODE_DONE;
	swm_done ();
}


void
sig_chld (int nonsense)
{
	wait (NULL);
}


int usage_printed = 0;

int
usage_print ()
{
	if (!usage_printed)
	{
		usage_printed = 1;
		printf ("SWM ver. %s\n", VERSION);
		printf ("usage: swm [-d display_name] [-f config_file] [--version] [-v] [--help] [-h]\n\n");
	};
}


Atom _XA_WM_CHANGE_STATE; Atom _XA_WM_STATE; Atom _XA_WM_PROTOCOLS; Atom _XA_WM_TAKE_FOCUS;
Atom _XA_WM_DELETE_WINDOW; Atom _XA_WM_DESKTOP; Atom _XA_WM_COMMAND; Atom _XA_MOTIF_WM_HINTS;
Atom _XA_SWM_HINTS; Atom _XA_SWM_PAGER;
Atom _XA_NET_WM_NAME; Atom _XA_NET_WM_ICON_NAME; Atom UTF8_STRING;
 
struct AtomItem {
	const char *name;
	Atom *atom;
};

AtomItem atom_info[] = {
	{ "WM_PROTOCOLS",	&_XA_WM_PROTOCOLS },
	{ "WM_TAKE_FOCUS",	&_XA_WM_TAKE_FOCUS },
	{ "WM_CHANGE_STATE",	&_XA_WM_CHANGE_STATE },
	{ "WM_STATE",		&_XA_WM_STATE },
	{ "WM_DELETE_WINDOW",	&_XA_WM_DELETE_WINDOW },
	{ "WM_DESKTOP",		&_XA_WM_DESKTOP },
	{ "WM_COMMAND",		&_XA_WM_COMMAND },
	{ "_MOTIF_WM_HINTS",	&_XA_MOTIF_WM_HINTS },

	{ "_SWM_HINTS",		&_XA_SWM_HINTS },
	{ "_SWM_PAGER",		&_XA_SWM_PAGER },

	{ "_NET_WM_NAME",	&_XA_NET_WM_NAME },
	{ "_NET_WM_ICON_NAME",	&_XA_NET_WM_ICON_NAME },

	{ "UTF8_STRING",	&UTF8_STRING }
};

int
atoms_init ()
{
	int n = sizeof (atom_info)/sizeof (AtomItem);
	for (int i=0; i<n; i++)
		*(atom_info[i].atom) = XInternAtom (dpy, atom_info[i].name, FALSE);
}


XErrorHandler 
catch_redirect_error (Display *dpy, XErrorEvent *ev)
{
	fprintf (stderr, "swm fatal: another window manager already running\n");
	exit (1);
}


XErrorHandler 
error_handler (Display *dpy, XErrorEvent *ev)
{
	// accept BadWindows
	if (ev->error_code == BadWindow) return 0;

	fprintf (stderr, "swm: internal error\n");
	fprintf (stderr, "request %d, error %d\n", ev->request_code, ev->error_code);
	fprintf (stderr, "event type: %d\n", ev->type);
	return 0;
}
