/* coral, by "Frederick G.M. Roeber" <roeber@netscape.com>, 15-jul-97.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or 
 * implied warranty.
 */

#include "screenhack.h"
#include "colors.h"
#include "erase.h"

static GC draw_gc, erase_gc;
static unsigned int default_fg_pixel;
#define NCOLORSMAX 200
static XColor colors[NCOLORSMAX];
static int ncolors = 0;
static int colorindex = 0;
static int colorsloth;

static XPoint *walkers = 0;
static int nwalkers;
static int width, widthb;
static int height;

static unsigned int *board = 0;
#define getdot(x,y) (board[(y*widthb)+(x>>5)] &  (1<<(x & 31)))
#define setdot(x,y) (board[(y*widthb)+(x>>5)] |= (1<<(x & 31)))


static void
init_coral(Display *dpy, Window window)
{
    XGCValues gcv;
    Colormap cmap;
    XWindowAttributes xgwa;
    Bool writeable = False;
    int seeds;
    int density;
    int i;

    XClearWindow(dpy, window);
    XGetWindowAttributes(dpy, window, &xgwa);
    width = xgwa.width;
    widthb = ((xgwa.width + 31) >> 5);
    height = xgwa.height;
    if (board) free(board);
    board = (unsigned int *)calloc(widthb * xgwa.height, sizeof(unsigned int));
    if(!board) exit(1);
    cmap = xgwa.colormap;
    if( ncolors ) {
        free_colors(dpy, cmap, colors, ncolors);
        ncolors = 0;
    }
    gcv.foreground = default_fg_pixel = get_pixel_resource("foreground", "Foreground", dpy, cmap);
    draw_gc = XCreateGC(dpy, window, GCForeground, &gcv);
    gcv.foreground = get_pixel_resource ("background", "Background",dpy, cmap);
    erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
    ncolors = NCOLORSMAX;
    make_uniform_colormap(dpy, xgwa.visual, cmap, colors, &ncolors, True, &writeable, False);
    if (ncolors <= 0) {
      ncolors = 2;
      colors[0].red = colors[0].green = colors[0].blue = 0;
      colors[1].red = colors[1].green = colors[1].blue = 0xFFFF;
      XAllocColor(dpy, cmap, &colors[0]);
      XAllocColor(dpy, cmap, &colors[1]);
   }
    colorindex = random()%ncolors;
    
    density = get_integer_resource("density", "Integer");
    if( density < 1 ) density = 1;
    if( density > 100 ) density = 90; /* more like mold than coral */
    nwalkers = (width*height*density)/100;
    if (walkers) free(walkers);
    walkers = (XPoint *)calloc(nwalkers, sizeof(XPoint));
    if( (XPoint *)0 == walkers ) exit(1);

    seeds = get_integer_resource("seeds", "Integer");
    if( seeds < 1 ) seeds = 1;
    if( seeds > 1000 ) seeds = 1000;

    colorsloth = nwalkers*2/ncolors;
    XSetForeground(dpy, draw_gc, colors[colorindex].pixel);

    for( i = 0; i < seeds; i++ ) {
        int x, y;
        do {
          x = 1 + random() % (width - 2);
          y = 1 + random() % (height - 2);
        } while( getdot(x, y) );

        setdot((x-1), (y-1)); setdot(x, (y-1)); setdot((x+1), (y-1));
	setdot((x-1),  y   ); setdot(x,  y   ); setdot((x+1),  y   );
	setdot((x-1), (y+1)); setdot(x, (y+1)); setdot((x+1), (y+1));
        XDrawPoint(dpy, window, draw_gc, x, y);
    }

    for( i = 0; i < nwalkers; i++ ) {
        walkers[i].x = (random() % (width-2)) + 1;
        walkers[i].y = (random() % (height-2)) + 1;
    }
}


/* returns 2 bits of randomness (conserving calls to random()).
   This speeds things up a little, but not a lot (5-10% or so.)
 */
static int 
rand_2(void)
{
  static int i = 0;
  static int r = 0;
  if (i != 0) {
    i--;
  } else {
    i = 15;
    r = random();
  }

  {
    register int j = (r & 3);
    r = r >> 2;
    return j;
  }
}


static void
coral(Display *dpy, Window window)
{
    int delay2 = get_integer_resource ("delay2", "Integer");

    int max_points = 200;
    int npoints = 0;
    XPoint *pointbuf = (XPoint *) calloc(sizeof(XPoint), max_points+2);
    if (!pointbuf) exit(-1);

    while( 1 ) {
        int i;

        for( i = 0; i < nwalkers; i++ ) {
            int x = walkers[i].x;
            int y = walkers[i].y;

            if( getdot(x, y) ) {

	        Bool flush = False;
	        Bool color = False;

	        /* XDrawPoint(dpy, window, draw_gc, x, y); */
	        pointbuf[npoints].x = x;
	        pointbuf[npoints].y = y;
		npoints++;

                /* Mark the surrounding area as "sticky" */
                setdot((x-1), (y-1)); setdot(x, (y-1)); setdot((x+1), (y-1));
		setdot((x-1),  y   );                   setdot((x+1),  y   );
                setdot((x-1), (y+1)); setdot(x, (y+1)); setdot((x+1), (y+1));
                nwalkers--;
                walkers[i].x = walkers[nwalkers].x;
                walkers[i].y = walkers[nwalkers].y;
                if( 0 == (nwalkers%colorsloth) ) {
		  color = True;
                }
		  
		if (flush || color || 0 == nwalkers || npoints >= max_points) {
		  XDrawPoints(dpy, window, draw_gc, pointbuf, npoints,
			      CoordModeOrigin);
		  npoints = 0;
		  XSync(dpy, False);
		}

		if (color) {
                    colorindex++;
                    if( colorindex == ncolors )
                        colorindex = 0;
                    XSetForeground(dpy, draw_gc, colors[colorindex].pixel);
                }

                if( 0 == nwalkers ) {
                    XSync(dpy, False);
		    free(pointbuf);
                    return;
                }
            } else {
                /* move it a notch */
                do {
                    switch(rand_2()) {
                    case 0:
                        if( 1 == x ) continue;
                        walkers[i].x--;
                        break;
                    case 1:
                        if( width-2 == x ) continue;
                        walkers[i].x++;
                        break;
                    case 2:
                        if( 1 == y ) continue;
                        walkers[i].y--;
                        break;
                    default: /* case 3: */
                        if( height-2 == y ) continue;
                        walkers[i].y++;
                        break;
		    /* default:
		      abort(); */
                    }
                } while(0);
            }
        }

	if (delay2 > 0) {
	  if (npoints > 0) {
	    XDrawPoints(dpy, window, draw_gc, pointbuf, npoints,
			CoordModeOrigin);
	    npoints = 0;
	    XSync(dpy, False);
	  }
          screenhack_handle_events (dpy);
	  usleep(delay2);
	}
    }
}

char *progclass = "Coral";

char *defaults[] = {
  ".background:	black",
  ".foreground:	white",
  "*density:	25",
  "*seeds:	20", /* too many for 640x480, too few for 1280x1024 */
  "*delay:	5",
  "*delay2:	1000",
  0
};

XrmOptionDescRec options[] = {
    { "-density", ".density", XrmoptionSepArg, 0 },
    { "-seeds", ".seeds", XrmoptionSepArg, 0 },
    { "-delay", ".delay", XrmoptionSepArg, 0 },
    { "-delay2", ".delay2", XrmoptionSepArg, 0 },
    { 0, 0, 0, 0 }
};

void
screenhack(dpy, window)
Display *dpy;
Window window;
{
    int delay = get_integer_resource ("delay", "Integer");
    while( 1 ) {
        init_coral(dpy, window);
        coral(dpy, window);
        screenhack_handle_events (dpy);
        if( delay ) sleep(delay);
	erase_full_window(dpy, window);
    }
}
