#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
#include <X11/xpm.h>
#include <X11/extensions/shape.h>

#include <pthread.h>
#include <errno.h>

#include "globals.h"
#include "gui.h"

#define LEFTBUTTON	1	/* Left mousebutton id */
#define MIDDLEBUTTON	2	/* Middle mousebutton id */
#define RIGHTBUTTON	3	/* Right mousebutton id */

#include "pixmaps/frame.xpm"
#include "pixmaps/newmail.xpm"
#include "pixmaps/oldmail.xpm"
#include "pixmaps/nomail.xpm"
#include "pixmaps/newmail_s.xpm"
#include "pixmaps/oldmail_s.xpm"
#include "pixmaps/nomail_s.xpm"
#define XPMCLOSENESS 32768
struct XpmIcon {
	Pixmap pixmap;
	Pixmap mask;
	XpmAttributes attributes;
	struct XpmIcon *next;
};
struct XpmIcon *Frame = NULL, 
	*NoMail = NULL, 
	*OldMail = NULL, 
	*NewMail = NULL, 
	*Current = NULL;

pthread_mutex_t x11_mutex = PTHREAD_MUTEX_INITIALIZER;
Widget MainWin;
GC WinGC;
XtAppContext AsmailApp;
Display *Disp = 0;
Window Root;
Atom wm_delete_win;
void Quit(Widget, XEvent *, String *, Cardinal *);
XtActionsRec asmail_actions[] = { { "Quit", Quit } };

Pixel GetColor(const char * ColorName) {
	XColor Color;
	XWindowAttributes Attributes;

	XGetWindowAttributes(Disp, Root, &Attributes);
	Color.pixel = 0;
	if (!XParseColor(Disp, Attributes.colormap, ColorName, &Color))
		printf("asmail: get_color: can't parse color %s\n", ColorName);
	else if (!XAllocColor(Disp, Attributes.colormap, &Color))
		printf("asmail: get_color: can't allocate color %s\n", ColorName);
	return Color.pixel;
}   

void XPMError(int Code) {
	switch (Code) {
	case 0:
		break;
	case 1:
	case -4:
		printf("asmail: not enough free color cells\n");
		break;
	case -1:
	case -2:
		printf("asmail: could not load xpm\n");
		break;
	case -3:
		printf("asmail: not enough memory free\n");
		break;
	default:
		printf("asmail: unknown xpm-error\n");
		break;
	}
	if (Code != 0)
		exit(1);
}

struct XpmIcon *GetXPM(char **data) {
	struct XpmIcon *icon = 
		(struct XpmIcon *) calloc(1, sizeof(struct XpmIcon));

	icon->attributes.valuemask |= XpmCloseness;
	icon->attributes.closeness = XPMCLOSENESS;

	XPMError(XpmCreatePixmapFromData(Disp, Root,
					 data,
					 &icon->pixmap,
					 &icon->mask,
					 &icon->attributes));

	icon->next = (struct XpmIcon *) icon;

	return icon;
}

void execute_on_new() {
	if ( x11_set.beep )
		XBell(Disp, 0);
	if ( strlen(x11_set.on_new_mail) )
		system(x11_set.on_new_mail);
}

int update_window() {
	struct mbox_struct * mb;
	int o = 0;
	int n = 0;
	int x = 0;
	int y = 0;
	XSizeHints SizeHints;

	mb = mbox;
	while (mb) {
		if ( mb->ctotal )
			o |= 1;
		if ( mb->cnew )
			n |= 1;
		if ( mb->flags & FLAG_ARRIVED ) {
			pthread_mutex_lock(&mb->mutex);
			mb->flags &= ! FLAG_ARRIVED;
			pthread_mutex_unlock(&mb->mutex);
			execute_on_new();
		}
		mb = mb->next;
	}

	if ( n )
		Current = NewMail;
	else if (o)
		Current = OldMail;
	else
		Current = NoMail;

	if ( x11_set.shape ) {
		SizeHints.width = Current->attributes.width;
		SizeHints.height = Current->attributes.height;
		XtResizeWidget(MainWin, SizeHints.width, SizeHints.height, 1);
		SizeHints.flags = USPosition | PSize | PMinSize | PMaxSize;
		SizeHints.min_width = SizeHints.max_width = SizeHints.width;
		SizeHints.min_height = SizeHints.max_height = SizeHints.height;
		XSetWMNormalHints(Disp, XtWindow(MainWin), &SizeHints);
		XShapeCombineMask(Disp, XtWindow(MainWin), ShapeBounding, 0, 0,
				Current->mask, ShapeSet);
	} else {
		XCopyArea(Disp, Frame->pixmap, XtWindow(MainWin), WinGC, 0, 0,
				Frame->attributes.width, 
				Frame->attributes.height, 0, 0);
		x = (Frame->attributes.width - Current->attributes.width) / 2;
		y = (Frame->attributes.height - Current->attributes.height) / 2;
	}
	XCopyArea(Disp, Current->pixmap, XtWindow(MainWin), WinGC, 0, 0,
			Current->attributes.width, Current->attributes.height, 
			x, y);

	XFlush(Disp);

	return 0;
}

void ButtonHandler(Widget W, XtPointer P, XEvent * E) {
	printf("asmail: ButtonHandler was called\n");
	if (E->xbutton.button == LEFTBUTTON) {
		printf("asmail: left button pressed.\n");
		if (strlen(x11_set.on_left))
			system(x11_set.on_left);
	} else if (E->xbutton.button == MIDDLEBUTTON) {
		printf("asmail: middle button pressed.\n");
		if (strlen(x11_set.on_middle))
			system(x11_set.on_middle);
	} else if (E->xbutton.button == RIGHTBUTTON) {
		printf("asmail: right button pressed.\n");
		if (strlen(x11_set.on_right))
			system(x11_set.on_right);
	}
	/* signal_xkick(); */
}

void ExposeHandler(Widget W, XtPointer P, XEvent * E) {
	printf("asmail: ExposeHandler was called\n");
	signal_xkick();
}

void StructureNotifyHandler(Widget W, XtPointer P, XEvent * E) {
	printf("asmail: structure notification %s\n", 
			E->type == MapNotify ? "MAP" :
			E->type == UnmapNotify ? "UNMAP" : "OTHER");
	if ( MapNotify == E->type ) {
		signal_xkick();
	}
}

void Quit(Widget w, XEvent *event, String *params, Cardinal *num_params) {
	if (event->type == ClientMessage &&
			event->xclient.data.l[0] != wm_delete_win) {
		XBell(Disp, 0);
		return;
	}
	XFreeGC(Disp, WinGC);
	XCloseDisplay(Disp);
	exit (0);
}

void main_x_loop(void * ptr) {
	XtAppMainLoop(AsmailApp);
	exit(0);
}

void startx(void * ptr) {
	XSizeHints SizeHints;
	XGCValues gcv;
	unsigned long gcm;
	int kicks;

	if ( pthread_mutex_trylock(&x11_mutex) == EBUSY ) {
		printf("asmail: startx: non-reentrant function!\n");
		exit(0);
	}

	printf("asmail: startx: X11 interface is starting up...\n");
	MainWin = XtAppInitialize(&AsmailApp, x11_set.title, NULL, 0, 
			&x11_set.argc, x11_set.argv, NULL, NULL, 0);
	Disp = XtDisplay(MainWin);
	Root = RootWindow(Disp, DefaultScreen(Disp));

	NoMail = GetXPM((x11_set.shape) ? nomail_s : nomail);
	NoMail->next = NoMail;
	OldMail = GetXPM((x11_set.shape) ? oldmail_s : oldmail);
	OldMail->next = OldMail;
	NewMail = GetXPM((x11_set.shape) ? newmail_s : newmail);
	NewMail->next = NewMail;
	Frame = GetXPM(frame);
	Current = NoMail;

	SizeHints.width = Frame->attributes.width;
	SizeHints.height = Frame->attributes.height;
	XtResizeWidget(MainWin, SizeHints.width, SizeHints.height, 1);
	XtRealizeWidget(MainWin);
	SizeHints.flags = USPosition | PSize | PMinSize | PMaxSize;
	SizeHints.min_width = SizeHints.max_width = SizeHints.width;
	SizeHints.min_height = SizeHints.max_height = SizeHints.height;
	XSetWMNormalHints(Disp, XtWindow(MainWin), &SizeHints);

	gcm = GCForeground | GCBackground | GCGraphicsExposures;
	gcv.foreground = GetColor("white");
	gcv.background = GetColor("white");
	gcv.graphics_exposures = False;
	WinGC = XCreateGC(Disp, Root, gcm, &gcv);

	XtAddEventHandler(MainWin, ExposureMask, FALSE, (XtEventHandler) ExposeHandler, NULL);
	XtAddEventHandler(MainWin, ButtonPressMask, FALSE, (XtEventHandler) ButtonHandler, NULL);
	XtAddEventHandler(MainWin, StructureNotifyMask, FALSE, (XtEventHandler) StructureNotifyHandler, NULL);
	XtAppAddActions(AsmailApp, asmail_actions, XtNumber(asmail_actions));
	XtOverrideTranslations(MainWin,
		XtParseTranslationTable("<Message>WM_PROTOCOLS: Quit()"));
	wm_delete_win = XInternAtom(Disp, "WM_DELETE_WINDOW", False);
	(void) XSetWMProtocols(Disp, XtWindow(MainWin), &wm_delete_win, 1);

	XtMapWidget(MainWin);

	pthread_create( &x11_set.main_x_thread, pthread_attr_default,
		(void*)&main_x_loop, (void*) &x11_set);
	pthread_detach( x11_set.main_x_thread );

	while ( 1 ) {
		kicks = sleep_xkick();
		printf("asmail: signal kicked X update (%d kicks).\n", kicks);
		update_window();
	}

	XFreeGC(Disp, WinGC);
	XCloseDisplay(Disp);
	pthread_exit(NULL);
}

