//
// To compile use:
// 		g++ -L/usr/X11R6/lib64 -pthread -lX11 testX11.cpp
//

/* first include the standard headers that we're likely to need */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include <iostream>
#include <pthread.h>

class threadDrawing
{
public:
	Display* d;
	Drawable w;
	Pixmap p;
	GC gc;
	pthread_t t;

	threadDrawing()
		:
			d( 0 ),
			w( 0 ),
			p( 0 ),
			gc( 0 )
	{
	}

	~threadDrawing()
	{
		deinit();
	}

	void init( const char * dname )
	{
		d = XOpenDisplay( dname );
	
		int screen_num = XDefaultScreen(d);
		unsigned long bg = XBlackPixel(d, screen_num);
		unsigned long fg = XWhitePixel(d, screen_num);
		w = XCreateSimpleWindow(d, XDefaultRootWindow(d),
								0, 0, 1, 1,
								0, fg, bg );

		p = XCreatePixmap( d, w, 50, 50, 
							XDefaultDepth( d, XDefaultScreen( d ) ) );
		gc = XCreateGC( d, w, 0, 0 );
		XSetForeground( d, gc, fg );
		XSetBackground( d, gc, bg );
	}

	void deinit()
	{
		if ( gc )
		{
			XFreeGC( d, gc );
			gc = 0;
		}
		if ( p )
		{
			XFreePixmap( d, p );
			p = 0;
		}
		if ( w )
		{
			XDestroyWindow( d, w );
			w = 0;
		}
		if ( d )
		{
			XCloseDisplay( d );
			d = 0;
		}
	}

	static void* entry_point( void * pData)
	{
		threadDrawing* pThis = reinterpret_cast<threadDrawing*>(pData);
		pThis->run();
		return 0;
	}

	void start()
	{
		int rc = pthread_create( &t, 0, &entry_point, (void*)this );
		std::cout << "Thread " << t << " created." << std::endl ;
	}

	void join()
	{
		void * s;
		pthread_join( t, &s );
		std::cout << "Thread " << t << " joined." << std::endl ;
	}

	void run()
	{
//		p = XCreatePixmap( d, w, 50, 50, 
//							XDefaultDepth( d, XDefaultScreen( d ) ) );
//		GC gc = XCreateGC( d, w, 0, 0 );
		XFillRectangle( d, p, gc, 0, 0, 50, 50 );
		for (; true ; )
		{
			XDrawLine( d, p, gc, 0, 0, 50, 50 );
			XDrawLine( d, p, gc, 0, 50, 50, 0 );
		}
		XFlush( d );
		XSync( d, True );
	}
};


void createThreads( Display* d, Drawable w )
{
	const int tsize = 16;
	typedef std::vector<threadDrawing> threads_type;
	threads_type threads( tsize );
		
	const char * dname = XDisplayString( d );

	threads_type::iterator it = threads.begin();
	threads_type::iterator itEnd = threads.end();
	for ( ; it != itEnd; ++it )
	{
		it->init( dname );
		it->start();
	}

	for ( it = threads.begin(); it != itEnd; ++it )
	{
		it->join();
	}
}

int error_handler( Display* d, XErrorEvent* err )
{
	puts( "error." );
}

int io_error_handler( Display* d )
{
	puts( "io error." );
}

int main(int argc, char ** argv)
{
	int screen_num, width, height;
	unsigned long background, border;
	Window win;
	XEvent ev;
	Display *dpy;
		
	XSetErrorHandler( error_handler );
	XSetIOErrorHandler( io_error_handler );

	/* First connect to the display server, as specified in the DISPLAY 
	environment variable. */
	dpy = XOpenDisplay(NULL);
	if (!dpy)
	{
		fprintf(stderr, "unable to connect to display");
		return 7;
	}

	/* these are macros that pull useful data out of the display object */
	/* we use these bits of info enough to want them in their own variables */
	screen_num = DefaultScreen(dpy);
	background = BlackPixel(dpy, screen_num);
	border = WhitePixel(dpy, screen_num);

	width = 400; /* start with a small window */
	height = 400;

	win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), /* display, parent */
		0,0, /* x, y: the window manager will place the window elsewhere */
		width, height, /* width, height */
		2, border, /* border width & colour, unless you have a window manager */
		background); /* background colour */

	/* tell the display server what kind of events we would like to see */
	XSelectInput(dpy, win, ButtonPressMask|StructureNotifyMask );

	/* okay, put the window on the screen, please */
	XMapWindow(dpy, win);

	//
	//
	//
	createThreads( dpy, win );

	/* as each event that we asked about occurs, we respond.  In this
	 * case we note if the window's shape changed, and exit if a button
	 * is pressed inside the window */
	while(1)
	{
		XNextEvent(dpy, &ev);
		switch(ev.type)
		{
		case ConfigureNotify:
			if (width != ev.xconfigure.width
					|| height != ev.xconfigure.height) 
			{
				width = ev.xconfigure.width;
				height = ev.xconfigure.height;
				printf("Size changed to: %d by %d\n", width, height);
			}
			break;
		case ButtonPress:
			XCloseDisplay(dpy);
			return 0;
		}
	}
}

