/*
	Create a window of a particular size and title
*/
int CreateXlibWindow(XLIBWindow *win,int width,int height,char *label)
{
	int blackColor,whiteColor;
	XEvent anevent;
	XTextProperty textproperty;

	win->thedisplay  = XOpenDisplay(NULL);
	win->thescreen   = DefaultScreen(win->thedisplay);
	win->thecolormap = DefaultColormap(win->thedisplay,win->thescreen);
	win->thedepth    = DefaultDepth(win->thedisplay,win->thescreen);
	blackColor  = BlackPixel(win->thedisplay,DefaultScreen(win->thedisplay));
	whiteColor  = WhitePixel(win->thedisplay,DefaultScreen(win->thedisplay));

	/* Create the window */
	win->thewindow = XCreateSimpleWindow(win->thedisplay, 
		DefaultRootWindow(win->thedisplay),0,0, 
		width,height,0,blackColor,blackColor);
	XSelectInput(win->thedisplay,win->thewindow,StructureNotifyMask);
	XMapWindow(win->thedisplay,win->thewindow);

	/* Label the window */
	XStringListToTextProperty(&label,1,&textproperty);
	XSetWMName(win->thedisplay,win->thewindow,&textproperty);
	XSetStandardProperties(win->thedisplay,win->thewindow,label,label,
		None,NULL,0,NULL);

	/* Get the context */
	win->thecontext = XCreateGC(win->thedisplay,win->thewindow,0,NULL);
	XSetBackground(win->thedisplay,win->thecontext,whiteColor);
	XSetForeground(win->thedisplay,win->thecontext,blackColor);

	/* Wait for the MapNotify event */
	for (;;) {
		XNextEvent(win->thedisplay,&anevent);
		if (anevent.type == MapNotify)
			break;
	}
	
   /* Set event types */
   XSelectInput(win->thedisplay,win->thewindow,
      ExposureMask | ButtonPressMask | KeyPressMask);

	/* Erase the display (In the background colour), bring to top */
	XClearWindow(win->thedisplay,win->thewindow);
	XMapRaised(win->thedisplay,win->thewindow);

	return(TRUE);
}

/*
	Release memory, destroy the window
*/
int DestroyXlibWindow(XLIBWindow *win)
{
	XFreeGC(win->thedisplay,win->thecontext);
	XUnmapWindow(win->thedisplay,win->thewindow);
	XDestroyWindow(win->thedisplay,win->thewindow);
	XCloseDisplay(win->thedisplay);

	return(TRUE);
}

/*
	Flush the output buffer
*/
int XlibFlush(XLIBWindow *win)
{
	XFlush(win->thedisplay);

	return(TRUE);
}

/*
	Draw a line between two points
	Specify the colour and the line width
*/
int XlibLine(XLIBWindow *win,Point p1,Point p2,COLOUR colour,int width)
{
	XlibColour(win,colour);

	if (width > 0) {
   	XSetLineAttributes(win->thedisplay,win->thecontext,(unsigned int)width,
      	LineSolid,CapRound,JoinRound);
		XDrawLine(win->thedisplay,win->thewindow,win->thecontext,
			(int)p1.h,(int)p1.v,(int)p2.h,(int)p2.v);
	}

	return(TRUE);
}

/*
	Draw a rectangle
	If width is <= 0 then draw a filled box
	Correct for incorrectly ordered box corners
*/
int XlibRect(XLIBWindow *win,Point p1,Point p2,COLOUR colour,int width)
{
	int tmp;

	XlibColour(win,colour); 

	/* Correct for badly specified rectangles */
	if (p1.h > p2.h) {
		tmp = p2.h;
		p2.h = p1.h;
		p1.h = tmp;
	}
	if (p1.v > p2.v) {
		tmp = p2.v;
		p2.v = p1.v;
		p1.v = tmp;
	}

	if (width <= 0) {
		XFillRectangle(win->thedisplay,win->thewindow,win->thecontext,
			(int)p1.h,(int)p1.v,(int)(p2.h-p1.h),(int)(p2.v-p1.v));
	}
	if (width > 0) { 
      XSetLineAttributes(win->thedisplay,win->thecontext,(unsigned int)width,
         LineSolid,CapRound,JoinRound);
      XDrawRectangle(win->thedisplay,win->thewindow,win->thecontext,
         (int)p1.h,(int)p1.v,(int)(p2.h-p1.h),(int)(p2.v-p1.v));
	}

	return(TRUE);
}

/*
   Draw a polygon
	If width is <= 0 then draw a filled polygon
	The border is drawn as a series of line segments
*/
int XlibPoly(XLIBWindow *win,Point *p,int n,COLOUR colour,int width)
{
	int i;
	XPoint *v;

	XlibColour(win,colour); 

   v = (XPoint *)malloc(n * sizeof(XPoint));
   if (v == NULL)
      return(FALSE);
   for (i=0;i<n;i++) {
      v[i].x = p[i].h;
      v[i].y = p[i].v;
   }

   if (width <= 0) {
		XFillPolygon(win->thedisplay,win->thewindow,win->thecontext,v,3,
			Complex,CoordModeOrigin);
	}
   if (width > 0) {                         
		for (i=0;i<n;i++)
      	XlibLine(win,p[i],p[(i+1)%n],colour,width);
   }
	free(v);

	return(TRUE);
}

/*
	Write a text string
*/
int XlibText(XLIBWindow *win,Point p,char *s,COLOUR colour,int size)
{
	Font thefont;
	XTextItem titem;

	if (strlen(s) <= 0)
		return(TRUE);

	XlibColour(win,colour); 

	if (size <= 5)
   	thefont = XLoadFont(win->thedisplay,"5x8");
	else if (size == 6)
		thefont = XLoadFont(win->thedisplay,"6x10");
   else if (size == 7)
      thefont = XLoadFont(win->thedisplay,"7x13");
   else if (size == 8)
      thefont = XLoadFont(win->thedisplay,"8x13");
   else if (size == 9)
      thefont = XLoadFont(win->thedisplay,"9x15");
   else if (size == 10)
      thefont = XLoadFont(win->thedisplay,"10x20");
   else
      thefont = XLoadFont(win->thedisplay,"12x24");

	titem.chars = s;
	titem.nchars = strlen(s);
	titem.delta = 0;
	titem.font = thefont;
	XDrawText(win->thedisplay,win->thewindow,win->thecontext,
		(int)p.h,(int)p.v,&titem,1);
/*
   XDrawString(win->thedisplay,win->thewindow,win->thecontext,
		(int)p.h,(int)p.v,s,strlen(s));
*/

	return(TRUE);
}

/*
	Draw an arc
	Angles measured in radians
	If width is <= 0 then draw a filled polygon
*/
int XlibArc(XLIBWindow *win,Point upperleft,Point diagonal,
	double theta1,double theta2,COLOUR colour,int width)
{
	XlibColour(win,colour); 

	if (width <= 0) {
		XFillArc(win->thedisplay,win->thewindow,win->thecontext,
			(int)upperleft.h,(int)upperleft.v,(int)diagonal.h,(int)diagonal.v,
			(int)(theta1*RTOD*64),(int)(theta2*RTOD*64));
	}
	if (width > 0) {
      XSetLineAttributes(win->thedisplay,win->thecontext,(unsigned int)width,
         LineSolid,CapRound,JoinRound);
      XDrawArc(win->thedisplay,win->thewindow,win->thecontext,
         (int)upperleft.h,(int)upperleft.v,(int)diagonal.h,(int)diagonal.v,
         (int)(theta1*RTOD*64),(int)(theta2*RTOD*64));
	}	

	return(TRUE);
}

/*
	Draw a single point
*/
int XlibPoint(XLIBWindow *win,Point p,COLOUR colour)
{
	XlibColour(win,colour); 

	XDrawPoint(win->thedisplay,win->thewindow,win->thecontext,
		(int)p.h,(int)p.v);

	return(TRUE);
}

/*
   Draw a marker
*/
int XlibMarker(XLIBWindow *win,Point p,COLOUR colour,int size,int width)
{
	XlibColour(win,colour); 

	if (width > 0) {
   	XSetLineAttributes(win->thedisplay,win->thecontext,(unsigned int)width,
      	LineSolid,CapRound,JoinRound);
   	XDrawLine(win->thedisplay,win->thewindow,win->thecontext,
      	(int)(p.h-size/2),(int)(p.v),
      	(int)(p.h+size/2),(int)(p.v));
   	XDrawLine(win->thedisplay,win->thewindow,win->thecontext,
      	(int)(p.h),(int)(p.v-size/2),
      	(int)(p.h),(int)(p.v+size/2));
	}

	return(TRUE);
}

/*
   Erase the window
	This is done by drawing a window size rectangle
*/
int XlibErase(XLIBWindow *win,COLOUR colour)
{
	Point p1={0,0},p2;

   p2.h = DisplayWidth(win->thedisplay,win->thescreen);
   p2.v = DisplayHeight(win->thedisplay,win->thescreen);    

	XlibRect(win,p1,p2,colour,-1);

	return(TRUE);
}

/*
	Set the foreground colour
	Note: in general only 256 colours are available, if more than this
   are used the resulting colours are undetermined. One solution is to
   quantise all colours, as per
   red   = (int)(red   * 6) / 6.0
   green = (int)(green * 6) / 6.0
	blue  = (int)(blue  * 6) / 6.0
*/
int XlibColour(XLIBWindow *win,COLOUR colour)
{
	XColor xcolour;

   xcolour.red   = colour.r * 65535;
   xcolour.green = colour.g * 65535;
   xcolour.blue  = colour.b * 65535;
   xcolour.flags = DoRed | DoGreen | DoBlue;
   XAllocColor(win->thedisplay,win->thecolormap,&xcolour);
   XSetForeground(win->thedisplay,win->thecontext,xcolour.pixel);

	return(TRUE);
}

/*
	Handle events
	0 - NULL event
	1 - update
   2 - mouse     data1 and data2 are the x and y positions
   3 - keyboard  data1 and possibly data2 is the key pressed
*/
int XlibEvent(XLIBWindow *win,int *data1,int *data2)
{
	int n;
	XEvent event;
	KeySym key;
	long mask;
	char text[8];

	*data1 = -1;
	*data2 = -1;

	/* Get the next event 
	XNextEvent(win->thedisplay,&event); */
	mask = ExposureMask | ButtonPressMask | KeyPressMask;
	if (!XCheckWindowEvent(win->thedisplay,win->thewindow,mask,&event))
		return(0);

	/* Is it an expose event */
	if (event.type == Expose && event.xexpose.count == 0) {
		return(1);
	}

	/* Is it a mouse press */
	if (event.type == ButtonPress) {
		*data1 = event.xbutton.x;
		*data2 = event.xbutton.y;
		return(2);
	}

	/* Is it a keypress event */
	if (event.type == KeyPress &&
       (n = XLookupString(&event.xkey,text,8,&key,0)) == 1 || n == 2) {
		*data1 = text[0];
		if (n == 2)
			*data2 = text[1];
		return(3);
	}

	return(0);
}

int XlibFlushEvent(XLIBWindow *win)
{
	XEvent event;

	while (XEventsQueued(win->thedisplay,QueuedAlready) > 0)
		XNextEvent(win->thedisplay,&event);

	return(TRUE);
}


