/*
	Mein Versuch mit Bildschirmschoner.
*/
#include "screenhack.h"
#include <X11/Xutil.h>

#ifdef HAVE_XSHM_EXTENSION
#include "xshm.h"
static Bool use_shm;
static XShmSegmentInfo shm_info;
#endif

#undef M_PI
#undef M_PI_2
#include <math.h>

Window window;
Display * display;
static Visual *visual;
static XImage *orig_map, *buffer_map;
/*
static Colormap colormap;
Pixmap into_pixmap;
Screen *screen;
*/
static GC context = (GC)NULL;

/*
int shift_base = 0;
int shift_base2 = 0;
int pixel_green = 0;
int pixel_set = 0;
*/
int delay;
int w;
int h;

typedef unsigned long int ul;

int sw_rand (int range)
{
	return ((int) (random () % range));
}

inline int vergleich_rot (ul f1, ul f2)
{
	static unsigned int r1 = 0;
	static unsigned int r2 = 0;
	r1 = (f1 & buffer_map->red_mask);
	r2 = (f2 & buffer_map->red_mask);
	return r1 - r2;
}

inline int vergleich_gruen (ul f1, ul f2)
{
	static unsigned int g1 = 0;
	static unsigned int g2 = 0; 
	g1 = (f1 & buffer_map->green_mask);
	g2 = (f2 & buffer_map->green_mask);
	return g1 - g2;
}

inline int vergleich_blau (ul f1, ul f2)
{
	static unsigned int b1 = 0; 
	static unsigned int b2 = 0; 
	b1 = (f1 & buffer_map->blue_mask);
	b2 = (f2 & buffer_map->blue_mask);
	return b1 - b2;
}


void mal_pixel (int x, int y, ul col)
{
	XPutPixel (buffer_map, x, y, col);
/*
	XPutPixel (image, x, y, col);
	unsigned long color = col;
	XSetForeground (display, context, col);
	XDrawPoint (display, window, context, x, y);
*/
}

static void DisplayImage (int x, int y, int w, int h)
{
#ifdef HAVE_XSHM_EXTENSION
	if (use_shm)
		XShmPutImage (display, window, context, buffer_map, x, y, x, y,
			w, h, False);
	else
#endif /* HAVE_XSHM_EXTENSION */
		XPutImage (display, window, context, buffer_map, x, y, x, y, w, h);
}

/**
		@param anz
*/
long move_pixels (long count) 
{
	int x, y;
	long hits = 0L;
	ul c1, c2;
	int ww = w - 1;
	int hh = h - 1;
	long j;
	for (j = 0; j < count; ++j)
	{
		x = sw_rand (ww) + 1;
		y = sw_rand (hh) + 1;
		
		c1 = XGetPixel (buffer_map, x, y);
		c2 = XGetPixel (buffer_map, x-1, y);
		if (vergleich_rot (c1, c2) < 0)
		{
			++hits;
			mal_pixel (x, y, c2);
			mal_pixel (x-1, y, c1);
			c1 = c2;
		}
		c2 = XGetPixel (buffer_map, x, y-1);
		if (vergleich_blau (c1, c2) < 0)
		{
			++hits;
			mal_pixel (x, y, c2);
			mal_pixel (x, y-1, c1);
			c1 = c2;
		}
		c2 = XGetPixel (buffer_map, x-1, y-1);
		if (vergleich_gruen (c1, c2) < 0)
		{
			++hits;
			mal_pixel (x, y, c2);
			mal_pixel (x-1, y-1, c1);
		}
	}
	DisplayImage (0, 0, w, h);
	return hits;
}

/*
	Aus rotzoomer.c geklaut:
*/
static void setup_X (Display * disp, Window win)
{
	XWindowAttributes xwa;
	int depth;
	XGCValues gcv;
	long gcflags;
	/*
	int hints;
	*/
	XGetWindowAttributes (disp, win, &xwa);
	window = win;
	display = disp;
	depth = xwa.depth;
	/*
	colormap = xwa.colormap;
	*/
	w = xwa.width;
	h = xwa.height;

	visual = xwa.visual;

	gcv.function = GXcopy;
	gcv.subwindow_mode = IncludeInferiors;
	gcflags = GCForeground | GCFunction;
	if (use_subwindow_mode_p (xwa.screen, window))	/* see grabscreen.c */
		gcflags |= GCSubwindowMode;
	context = XCreateGC (display, window, gcflags, &gcv);
	grab_screen_image (xwa.screen, window);

	orig_map = XGetImage (display, window, 0, 0, w, h, ~0L, ZPixmap);

	if (!context) 
	{
		fprintf (stderr, "XCreateGC failed\n");
		exit (1);
	}
	
	buffer_map = 0;

#ifdef HAVE_XSHM_EXTENSION
	if (use_shm) 
	{
		buffer_map = create_xshm_image (display, xwa.visual, depth, ZPixmap, 0, &shm_info, w, h);
		if (!buffer_map) 
		{
			use_shm = False;
			fprintf(stderr, "create_xshm_image failed\n");
		}
	}
#endif /* HAVE_XSHM_EXTENSION */

	if (!buffer_map) 
	{
		buffer_map = XCreateImage (display, xwa.visual, depth, ZPixmap, 0, 0, w, h, 8, 0);
		buffer_map->data = (char *)calloc (buffer_map->height, 	buffer_map->bytes_per_line);
	}

	/*		beschleunigen der Shifterei soweit möglich.
	// dies ist ein dirty_hack - depth = 12 wird falsch berücksichtigt,
	// depth= 32 funktioniert nur, wenn sie tatsächlich der 24er entspricht (oder umgekehrt?).
	shift_base = 8;
	{
		int d = xwa.depth;
		if ( d== 8 || d == 12)
			shift_base = 2;
		else if (d == 16 || d == 15)
			shift_base = 4;
		shift_base2 = 2 * shift_base;
	}
	fprintf (stderr, "shift_base = %i\n", shift_base);
	*/
}

static void init_hack (void)
{
	memcpy (buffer_map->data, orig_map->data, h * buffer_map->bytes_per_line);
	DisplayImage (0, 0, w, h);
	XSync (display,False);

	/** zu debugzwecken: */
	if (delay == 99)
	{
		int r, g, b;
		int i;
		/*
			Test mit einer Diagonalen, die bis hinunter zu 640x480 
		*/
		for (i = 0; i < 480; ++i)
		{
			long c = XGetPixel (buffer_map, i, i);
			r = (c & buffer_map->red_mask);
			g = (c & buffer_map->green_mask);
			b = (c & buffer_map->blue_mask);
			fprintf (stdout, "%lx\t%i%i%i\n", c, r>0, g>0, b>0);
		}
	}
}

char *progclass = "Sedimenter";

char *defaults[] = 
{
#ifdef HAVE_XSHM_EXTENSION
	"*useSHM: True",
#endif
	"*delay: 10",
	0
};


XrmOptionDescRec options[] = 
{
#ifdef HAVE_XSHM_EXTENSION
	{ "-shm",	".useSHM",	XrmoptionNoArg, "True"  },
	{ "-no-shm",	".useSHM",	XrmoptionNoArg, "False" },
#endif
	{ "-delay",	".delay",	XrmoptionSepArg, 0      },
	{ 0, 0, 0, 0 }
};

void screenhack (Display *disp, Window win)
{
	long hits = 0;
#ifdef HAVE_XSHM_EXTENSION
	use_shm = get_boolean_resource ("useSHM", "Boolean");
#endif
	delay = get_integer_resource ("delay", "Integer");

	setup_X (disp, win);

	init_hack ();

	/* Main drawing loop */
	while (42) 
	{
		hits = move_pixels (100000);
		if (delay) 
			if (delay != 99) 
				usleep (delay);
			else 
				if ((hits % 10) == 0) fprintf (stdout, "hits %li\n", hits);
		XSync (display,False);
		screenhack_handle_events (display);
	}
	/*
	fprintf (stderr, "number of put=%i\t of green=%i\n", pixel_set, pixel_green);
	*/
}

