//////////////////////////////////////////////////////////////////////
//
//                             Pixie
//
// Copyright © 1999 - 2003, Okan Arikan
//
// Contact: okan@cs.berkeley.edu
//
// This library 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 of the License, or (at your option) any later version.
// 
// This library 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 library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//
//  File				:	fbx.cpp
//  Classes				:	CXDisplay
//  Description			:	X Windows image displaying class
//
////////////////////////////////////////////////////////////////////////
#include "common/global.h"
#include "framebuffer.h"
#include "fbx.h"

#define	color16(r,g,b,a)	((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)
#define	color(r,g,b,a)		((a << 24) | (r << 16) | (g << 8) | b)
#define	color16rev(r,g,b,a)	((g >> 2) << 13) | ((b >> 3) << 8) | ((r >> 3) << 3) | (g >> 5)
#define	colorrev(r,g,b,a)	((b << 24) | (g << 16) | (r << 8) | a)
#define	get_b(col)			((col) & 255)
#define	get_a(col)			(((col) >> 24) & 255)
#define	get_r(col)			(((col) >> 16) & 255)
#define	get_g(col)			(((col) >> 8) & 255)


///////////////////////////////////////////////////////////////////////
// Function				:	displayThread
// Description			:	Starts the display thread
// Return Value			:
// Comments				:
// Date last edited		:	11/28/2001
void		*displayThread(void *w) {
	CXDisplay	*cDisplay		=	(CXDisplay *) w;

	// The thread main loop
	cDisplay->main();

	return NULL;
}


///////////////////////////////////////////////////////////////////////
// Class				:	CXDisplay
// Method				:	CXDisplay
// Description			:	Ctor
// Return Value			:	-
// Comments				:
// Date last edited		:	8/8/2002
CXDisplay::CXDisplay(const char *name,const char *samples,int width,int height,int numSamples)
: CDisplay(name,samples,width,height,numSamples) {
	int				x,y;
	unsigned int	*dest;
	unsigned short	*dests;
	
	XInitThreads();
	display				=	XOpenDisplay (NULL);

	if (display == NULL) failure	=	TRUE;
	else {
		byteOrder			=	ImageByteOrder(display);
		WM_DELETE_WINDOW	=	XInternAtom(display, "WM_DELETE_WINDOW",	0);
		WM_PROTOCOLS		=	XInternAtom(display, "WM_PROTOCOLS",		0);

		screen				=	DefaultScreen (display);
		imageDepth			=	DefaultDepth (display, screen);

		// Create a checkerboard pattern
		switch(imageDepth) {
		case 16:
			imageData			=	malloc(width*height*sizeof(unsigned short));
			dests				=	(unsigned short *) imageData;

			if(byteOrder){
				for (y=0;y<height;y++) {
					for (x=0;x<width;x++) {
						int	t	=	0;

						if ((x & 63) < 32)	t ^=	1;
						if ((y & 63) < 32)	t ^=	1;

						if (t) {
							*dests++	=	color16(128,128,128,128);
						} else {
							*dests++	=	color16(255,255,255,255);
						}

					}
				}
			}
			else{
				for (y=0;y<height;y++) {
					for (x=0;x<width;x++) {
						int	t	=	0;

						if ((x & 63) < 32)	t ^=	1;
						if ((y & 63) < 32)	t ^=	1;

						if (t) {
							*dests++	=	color16rev(128,128,128,128);
						} else {
							*dests++	=	color16rev(255,255,255,255);
						}

					}
				}
			}

			break;
		case 24:
		case 32:
			imageData			=	malloc(width*height*sizeof(unsigned int));
			dest				=	(unsigned int *) imageData;
			
			if(byteOrder){
				for (y=0;y<height;y++) {
					for (x=0;x<width;x++) {
						int	t	=	0;

						if ((x & 63) < 32)	t ^=	1;
						if ((y & 63) < 32)	t ^=	1;

						if (t) {
							*dest++	=	color(128,128,128,128);
						} else {
							*dest++	=	color(255,255,255,255);
						}

					}
				}
			}
			else{
				for (y=0;y<height;y++) {
					for (x=0;x<width;x++) {
						int	t	=	0;

						if ((x & 63) < 32)	t ^=	1;
						if ((y & 63) < 32)	t ^=	1;

						if (t) {
							*dest++	=	colorrev(128,128,128,128);
						} else {
							*dest++	=	colorrev(255,255,255,255);
						}

					}
				}
			}

			break;
		default:
			imageData	=	NULL;
			break;
		}


		if (imageData != NULL) {
			pthread_create(&thread, NULL , displayThread, this);
		}
	}
}

///////////////////////////////////////////////////////////////////////
// Class				:	CXDisplay
// Method				:	~CXDisplay
// Description			:	Dtor
// Return Value			:	-
// Comments				:
// Date last edited		:	8/8/2002
CXDisplay::~CXDisplay() {
}


///////////////////////////////////////////////////////////////////////
// Class				:	CXDisplay
// Method				:	main
// Description			:	The main display loop
// Return Value			:	-
// Comments				:
// Date last edited		:	8/8/2002
void	CXDisplay::main() {
	int			i,j;

	assert(display != NULL);
	assert(failure == FALSE);

	windowUp	=	FALSE;
	windowDown	=	FALSE;

	XSetWindowAttributes	window_attributes;
	unsigned long			window_mask;
	XSizeHints				window_hints;
	int						running	=	TRUE;

	window_attributes.border_pixel		= BlackPixel (display, screen);
	window_attributes.background_pixel	= BlackPixel (display, screen);
	window_attributes.override_redirect = 0;
	window_mask							= CWBackPixel | CWBorderPixel;
	xcanvas								= XCreateWindow (display,
														DefaultRootWindow (display),
														0,
														0,
														width,height,
														0,
														imageDepth,
														InputOutput,
														CopyFromParent,
														window_mask,
														&window_attributes);


	int	scanSize;

	switch(imageDepth) {
	case 16:
		xim = XCreateImage (
			display,
			CopyFromParent,
			imageDepth,
			ZPixmap,
			0,
			(char *) imageData,
			width,
			height,
			imageDepth,
			width*2);
		break;
	case 24:
	case 32:
		xim = XCreateImage (
			display,
			CopyFromParent,
			imageDepth,
			ZPixmap,
			0,
			(char *) imageData,
			width,
			height,
			32,
			width*4);
		break;
	}

	image_gc = XCreateGC (display, xcanvas, 0, 0);

	XMapWindow (display, xcanvas);

	XChangeProperty(display,xcanvas, WM_PROTOCOLS,XA_ATOM, 32, 0, (unsigned char *)&WM_DELETE_WINDOW, 1);

	XSelectInput(display, xcanvas, ExposureMask | StructureNotifyMask | KeyPressMask);

	windowUp	=	TRUE;

	while (running)	{
		XEvent x_event;

		XNextEvent(display, &x_event);
		switch  (x_event.type) {
   		case Expose:
			XPutImage (display, xcanvas, image_gc, xim, 0, 0, 0, 0, width, height);
			XFlush(display);
		break;            
 		case KeyPress:
 			{
 				KeySym key = XLookupKeysym(&x_event.xkey, 0);
				if(key == XK_Escape || key == XK_q)	{
					running	= FALSE;
				}
			}
		break;
		case DestroyNotify:
			running	=	FALSE;
		break;
		case ClientMessage:
			{
				const long* data = x_event.xclient.data.l;
				if ((Atom)(data[0]) == WM_DELETE_WINDOW) {
					running	=FALSE;
 	  			}
			}
		break;
		}
 	}

	windowDown	=	TRUE;

	XUnmapWindow(display,xcanvas);
	XFreeGC(display,image_gc);
	XDestroyImage(xim);
	XDestroyWindow(display,xcanvas);
	XCloseDisplay(display);
}


///////////////////////////////////////////////////////////////////////
// Class				:	CWinDisplay
// Method				:	finish
// Description			:	Finish displaying data
// Return Value			:	-
// Comments				:
// Date last edited		:	8/8/2002
void		CXDisplay::finish() {
	XPutImage (display, xcanvas, image_gc, xim, 0, 0, 0, 0, width, height);
	XFlush(display);

	pthread_join(thread,NULL);
}

///////////////////////////////////////////////////////////////////////
// Class				:	CWinDisplay
// Method				:	data
// Description			:	Commit the image data
// Return Value			:	-
// Comments				:
// Date last edited		:	8/8/2002
int		CXDisplay::data(int x,int y,int w,int h,float *d) {
	int	i,j;

	if (windowDown)	return FALSE;

	clampData(w,h,d);

	if ((imageDepth == 24) || (imageDepth == 32)) {
		for (i=0;i<h;i++) {
			const float		*src	=	&d[i*w*numSamples];
			unsigned int	*dest	=	&((unsigned int *) imageData)[((i+y)*width+x)];

			switch(numSamples) {
			case 0:
				break;
			case 1:
				if(byteOrder){
					for (j=0;j<w;j++) {
						unsigned char	d	=	(unsigned char) (src[0]*255);

						*dest++				=	color(d,d,d,d);
						src++;
					}
				}
				else{
					for (j=0;j<w;j++) {
						unsigned char	d	=	(unsigned char) (src[0]*255);

						*dest++				=	colorrev(d,d,d,d);
						src++;
					}
				}
				break;
			case 2:
				if(byteOrder){
					for (j=0;j<w;j++) {
						float			r	=	src[0]*src[1]*255	+ (1-src[1])*get_r(dest[0]);
						float			a	=	src[1]*255			+ (1-src[1])*get_a(dest[0]);
						unsigned char	dr	=	(unsigned char) r;
						unsigned char	dg	=	(unsigned char) r;
						unsigned char	db	=	(unsigned char) r;
						unsigned char	da	=	(unsigned char) a;

						*dest++				=	color(dr,dg,db,da);

						src					+=	2;
					}
				}
				else{
					for (j=0;j<w;j++) {
						float			r	=	src[0]*src[1]*255	+ (1-src[1])*get_r(dest[0]);
						float			a	=	src[1]*255			+ (1-src[1])*get_a(dest[0]);
						unsigned char	dr	=	(unsigned char) r;
						unsigned char	dg	=	(unsigned char) r;
						unsigned char	db	=	(unsigned char) r;
						unsigned char	da	=	(unsigned char) a;

						*dest++				=	colorrev(dr,dg,db,da);

						src					+=	2;
					}
				}
				break;
			case 3:
				if(byteOrder){
					for (j=0;j<w;j++) {
						unsigned char	dr	=	(unsigned char) (src[0]*255);
						unsigned char	dg	=	(unsigned char) (src[1]*255);
						unsigned char	db	=	(unsigned char) (src[2]*255);

						*dest++				=	color(dr,dg,db,(unsigned char) 0);

						src					+=	3;
					}
				}
				else{
					for (j=0;j<w;j++) {
						unsigned char	dr	=	(unsigned char) (src[0]*255);
						unsigned char	dg	=	(unsigned char) (src[1]*255);
						unsigned char	db	=	(unsigned char) (src[2]*255);

						*dest++				=	colorrev(dr,dg,db,(unsigned char) 0);

						src					+=	3;
					}
				}
				break;
			case 4:
				if(byteOrder){
					for (j=0;j<w;j++) {
						float			r	=	src[0]*src[3]*255	+ (1-src[3])*get_r(dest[0]);
						float			g	=	src[1]*src[3]*255	+ (1-src[3])*get_g(dest[0]);
						float			b	=	src[2]*src[3]*255	+ (1-src[3])*get_b(dest[0]);
						float			a	=	src[3]*255			+ (1-src[3])*get_a(dest[0]);

						unsigned char	dr	=	(unsigned char) r;
						unsigned char	dg	=	(unsigned char) g;
						unsigned char	db	=	(unsigned char) b;
						unsigned char	da	=	(unsigned char) a;

						*dest++				=	color(dr,dg,db,da);

						src					+=	4;
					}
				}
				else{
					for (j=0;j<w;j++) {
						float			r	=	src[0]*src[3]*255	+ (1-src[3])*get_r(dest[0]);
						float			g	=	src[1]*src[3]*255	+ (1-src[3])*get_g(dest[0]);
						float			b	=	src[2]*src[3]*255	+ (1-src[3])*get_b(dest[0]);
						float			a	=	src[3]*255			+ (1-src[3])*get_a(dest[0]);

						unsigned char	dr	=	(unsigned char) r;
						unsigned char	dg	=	(unsigned char) g;
						unsigned char	db	=	(unsigned char) b;
						unsigned char	da	=	(unsigned char) a;

						*dest++				=	colorrev(dr,dg,db,da);

						src					+=	4;
					}
				}
				break;
			default:
				if(byteOrder){
					for (j=0;j<w;j++) {
						float			r	=	src[0]*src[3]*255	+ (1-src[3])*get_r(*dest);
						float			g	=	src[1]*src[3]*255	+ (1-src[3])*get_g(*dest);
						float			b	=	src[2]*src[3]*255	+ (1-src[3])*get_b(*dest);
						float			a	=	src[3]*255			+ (1-src[3])*get_a(*dest);
						unsigned char	dr	=	(unsigned char) r;
						unsigned char	dg	=	(unsigned char) g;
						unsigned char	db	=	(unsigned char) b;
						unsigned char	da	=	(unsigned char) a;

						*dest++				=	color(dr,dg,db,da);

						src					+=	numSamples;
					}
				}
				else{
					for (j=0;j<w;j++) {
						float			r	=	src[0]*src[3]*255	+ (1-src[3])*get_r(*dest);
						float			g	=	src[1]*src[3]*255	+ (1-src[3])*get_g(*dest);
						float			b	=	src[2]*src[3]*255	+ (1-src[3])*get_b(*dest);
						float			a	=	src[3]*255			+ (1-src[3])*get_a(*dest);
						unsigned char	dr	=	(unsigned char) r;
						unsigned char	dg	=	(unsigned char) g;
						unsigned char	db	=	(unsigned char) b;
						unsigned char	da	=	(unsigned char) a;

						*dest++				=	colorrev(dr,dg,db,da);

						src					+=	numSamples;
					}
				}
				break;
			}
		}
	} else {
		for (i=0;i<h;i++) {
			float			*src	=	&d[i*w*numSamples];
			unsigned short	*dest	=	&((unsigned short *) imageData)[(i+y)*width+x];
			switch(numSamples) {
			case 0:
				break;
			case 1:
				if(byteOrder)
				{
					for (j=0;j<w;j++) {
						unsigned char	d	=	(unsigned char) (src[0]*255);

						*dest++				=	color16(d,d,d,d);
						src++;
					}
				}
				else{
					for (j=0;j<w;j++) {
						unsigned char	d	=	(unsigned char) (src[0]*255);

						*dest++				=	color16rev(d,d,d,d);
						src++;
					}
				}
				break;
			case 2:
				if(byteOrder){
					for (j=0;j<w;j++) {
						float			r	=	src[0]*src[1]*255;
						float			g	=	src[0]*src[1]*255;
						float			b	=	src[0]*src[1]*255;
						float			a	=	src[1]*255;
						unsigned char	dr	=	(unsigned char) r;
						unsigned char	dg	=	(unsigned char) g;
						unsigned char	db	=	(unsigned char) b;
						unsigned char	da	=	(unsigned char) a;

						*dest++				=	color16(dr,dg,db,da);

						src					+=	2;
					}
				}
				else{
					for (j=0;j<w;j++) {
						float			r	=	src[0]*src[1]*255;
						float			g	=	src[0]*src[1]*255;
						float			b	=	src[0]*src[1]*255;
						float			a	=	src[1]*255;
						unsigned char	dr	=	(unsigned char) r;
						unsigned char	dg	=	(unsigned char) g;
						unsigned char	db	=	(unsigned char) b;
						unsigned char	da	=	(unsigned char) a;

						*dest++				=	color16rev(dr,dg,db,da);

						src					+=	2;
					}
				}
				break;
			case 3:
				if(byteOrder){
					for (j=0;j<w;j++) {
						unsigned char	dr	=	(unsigned char) (src[0]*255);
						unsigned char	dg	=	(unsigned char) (src[1]*255);
						unsigned char	db	=	(unsigned char) (src[2]*255);

						*dest++				=	color16(dr,dg,db,(unsigned char) 255);

						src					+=	3;
					}
				}
				else{
					for (j=0;j<w;j++) {
						unsigned char	dr	=	(unsigned char) (src[0]*255);
						unsigned char	dg	=	(unsigned char) (src[1]*255);
						unsigned char	db	=	(unsigned char) (src[2]*255);

						*dest++				=	color16rev(dr,dg,db,(unsigned char) 255);

						src					+=	3;
					}
				}
				break;
			case 4:
				if(byteOrder){
					for (j=0;j<w;j++) {
						float			r	=	src[0]*src[3]*255;
						float			g	=	src[1]*src[3]*255;
						float			b	=	src[2]*src[3]*255;
						float			a	=	src[3]*255;
						unsigned char	dr	=	(unsigned char) r;
						unsigned char	dg	=	(unsigned char) g;
						unsigned char	db	=	(unsigned char) b;
						unsigned char	da	=	(unsigned char) a;

						*dest++				=	color16(dr,dg,db,da);

						src					+=	4;
					}
				}
				else{
					for (j=0;j<w;j++) {
						float			r	=	src[0]*src[3]*255;
						float			g	=	src[1]*src[3]*255;
						float			b	=	src[2]*src[3]*255;
						float			a	=	src[3]*255;
						unsigned char	dr	=	(unsigned char) r;
						unsigned char	dg	=	(unsigned char) g;
						unsigned char	db	=	(unsigned char) b;
						unsigned char	da	=	(unsigned char) a;

						*dest++				=	color16rev(dr,dg,db,da);

						src					+=	4;
					}
				}
				break;
			default:
				if(byteOrder){
					for (j=0;j<w;j++) {
						float			r	=	src[0]*src[3]*255;
						float			g	=	src[1]*src[3]*255;
						float			b	=	src[2]*src[3]*255;
						float			a	=	src[3]*255;
						unsigned char	dr	=	(unsigned char) r;
						unsigned char	dg	=	(unsigned char) g;
						unsigned char	db	=	(unsigned char) b;
						unsigned char	da	=	(unsigned char) a;

						*dest++				=	color16(dr,dg,db,da);

						src					+=	numSamples;
					}
				}
				else{
					for (j=0;j<w;j++) {
						float			r	=	src[0]*src[3]*255;
						float			g	=	src[1]*src[3]*255;
						float			b	=	src[2]*src[3]*255;
						float			a	=	src[3]*255;
						unsigned char	dr	=	(unsigned char) r;
						unsigned char	dg	=	(unsigned char) g;
						unsigned char	db	=	(unsigned char) b;
						unsigned char	da	=	(unsigned char) a;

						*dest++				=	color16rev(dr,dg,db,da);

						src					+=	numSamples;
					}
				}
				break;
			}
		}
	}

	if (windowUp) {
		XPutImage (display, xcanvas, image_gc, xim, 0, 0, 0, 0, width, height);
		XFlush(display);
	}


	return TRUE;
}







