/* X window graphics for logo.  Nov. 1991. */

/* Note:  logo uses True and False as variables to hold references
   to the nodes containing the values true and false.  X uses them
   as macros serving the functions often served by the more standard
   TRUE and FALSE.  To avoid a conflict, don't use True and False in
   this file.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifndef X_DISPLAY_MISSING

#include <stdio.h>

#include "logo.h"
#include "xgraphics.h"
#include "globals.h"

char *LogoPlatformName="X11";

int clearing_screen = 0;

#ifdef SIGWINCH

#ifdef SIG_TAKES_ARG
#define sig_arg 0
void x_win_resize(int sig)
#else
#define sig_arg 
void x_win_resize()
#endif
{
    signal(SIGWINCH, x_win_resize);
    placate_x();
}
#endif	/* SIGWINCH */

int have_x = -1;

XWMHints xwmh = {
  (InputHint|StateHint),	/* flags */
  FALSE,			/* input */
  NormalState,                  /* initial_state */
  0,				/* icon pixmap */
  0,				/* icon window */
  0, 0,                         /* icon location */
  0,				/* icon mask */
  0,				/* Window group */
};

int screen_height = DEFAULT_HEIGHT;
int screen_width  = DEFAULT_WIDTH;
int x_mouse_x, x_mouse_y, x_buttondown=0;

/* We'll use 16 named colors for now (see xgraphics.h).
   The ordering here corresponds to the zero-based argument
   to setpencolor that gives that color -- pink is 12,
   turquoise is 10 etc.
 */

char *colorname[NUMINITCOLORS] = {
  "black", "blue"   , "green" , "cyan" ,
  "red"  , "magenta", "yellow", "white",
  "brown",
  "tan",
  "dark green",  /* Should be 'forest' */
  "aquamarine",  /* Should be 'aqua'   */
  "salmon",
  "purple",
  "orange",
  "grey"
};

XColor color[NUMCOLORS+2];
XColor dummy;

void nop() {
}

pen_info xgr_pen;
int back_ground;

Display    *dpy;		/* X server connection   */
Window      win;		/* Window ID             */
GC          draw_gc,            /* GC to draw with       */
            up_gc,              /* Do nothing gc.        */
            erase_gc,           /* Erase gc.             */
            reverse_gc;         /* GC to reverse things. */

int         screen;

int save_argc;
char ** save_argv;

void x_window_init(int argc, char **argv)
{
    save_argc = argc;
    save_argv = argv;
}

void real_window_init()
{
  unsigned long fth, pad;	/* Font size parameters */
  unsigned long fg, bg, bd;	/* Pixel values */
  unsigned long bw;		/* Border width */
  XFontStruct *fontstruct;	/* Font descriptor */
  XGCValues   gcv;		/* Struct for creating GC */
  XEvent      event;		/* Event received */
  XSizeHints  xsh;		/* Size hints for window manager */
  XSetWindowAttributes xswa;	/* Temporary Set Window Attribute struct */
  int n;

  /* Open X display. */
  if ((dpy = XOpenDisplay(NULL)) == NULL) {
    have_x = 0;
    return;
  } else have_x = 1;

  screen = DefaultScreen(dpy);

  /* Load default font. */
  if ((fontstruct = XLoadQueryFont(dpy, FONT)) == NULL) {
    have_x = 0;
    return;
  }

  fth = fontstruct->max_bounds.ascent + fontstruct->max_bounds.descent;

  /*
   * Select colors.
   */

  for(n = 0; n < NUMINITCOLORS; n++)
    XAllocNamedColor(dpy, DefaultColormap(dpy, screen),
		     colorname[n], &color[n+2], &dummy);

    for (n = NUMINITCOLORS; n < NUMCOLORS; n++)
	color[n+2] = color[(n+2) % NUMINITCOLORS];

    xgr_pen.color = 7;

  /* Not much alternative to the following,
     because of the use of the xor operation to erase the turtle.
     The background HAS to be the zero pixel or the scheme won't work.
   */
  bg = BlackPixel(dpy, screen);
  fg = bd = WhitePixel(dpy, screen);

  /*
   * Set the border width of the window,  and the gap between the text
   * and the edge of the window, "pad".
   */
  pad = BORDER;
  bw = 1;

  /* Set up size/position hints. */
  xsh.flags = (PPosition | PSize);
  xsh.height = DEFAULT_HEIGHT;
  xsh.width = DEFAULT_WIDTH;
  xsh.x = (DisplayWidth(dpy, DefaultScreen(dpy)) - xsh.width) / 2;
  xsh.y = (DisplayHeight(dpy, DefaultScreen(dpy)) - xsh.height) / 2;

  win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy),
			    xsh.x, xsh.y, xsh.width, xsh.height,
			    bw, bd, bg);

  /* Label window. */
  XSetStandardProperties(dpy, win, "BXLogo", "BXLogo", None,
				save_argv, save_argc, &xsh);
  XSetWMHints(dpy, win, &xwmh);

  /* Set up default color map. */
  xswa.colormap = DefaultColormap(dpy, DefaultScreen(dpy));
  xswa.backing_store = Always;
  XChangeWindowAttributes(dpy, win, CWColormap | CWBackingStore, &xswa);


 /*
  * Create GCs.
  */

  /* Make the foreground and background fields in the GC
     match the fg and bg variables that were passed to the window.
   */

  gcv.font = fontstruct->fid;
  gcv.background = BlackPixel(dpy, screen);
  gcv.foreground = WhitePixel(dpy, screen);
  gcv.plane_mask = AllPlanes;

  /* Normal drawing GC. */
  draw_gc = XCreateGC(dpy, win,
		      (GCPlaneMask | GCFont | GCForeground | GCBackground),
		      &gcv);

  /* Create GC for erasing/drawing turtle. */
  gcv.function = GXxor;
  reverse_gc = XCreateGC(dpy, win, 
			 (GCPlaneMask | GCFont | GCFunction |
			  GCForeground | GCBackground), 
			 &gcv);

  /* Erase gc just draws the bacground color. */
  gcv.foreground = bg;
  gcv.function = GXcopy;
  erase_gc = XCreateGC(dpy, win,
		       (GCPlaneMask | GCFont | GCForeground | GCBackground),
		       &gcv);

  gcv.function = GXnoop;
  up_gc = XCreateGC(dpy, win,
		    (GCPlaneMask | GCFunction | GCForeground | GCBackground),
		    &gcv);

  xgr_pen.pm = draw_gc;
  xgr_pen.vis = 0;

  XSelectInput(dpy, win, EVENT_MASK);

 /*
  * Map the window to make it visible.  See Section 3.5.
  */
  XMapWindow(dpy, win);

 /*
  * Loop,  examining each event.  Exit when we get mapped.
  */
  do {
   /*
    * Get the next event
    */
    XWindowEvent(dpy, win, StructureNotifyMask, &event);

    /*
     * Wait for the map notify event.
     */
    if (event.type == MapNotify) {
      XClearWindow(dpy, win);
      break;
    }
  } while(1);

  lclearscreen(NIL);
  move_to(screen_x_coord, screen_y_coord);
/*   if(turtle_shown)
    draw_turtle();  */
#ifdef SIGWINCH
    signal(SIGWINCH, x_win_resize);
#endif
}

void save_pen(pen_info *p) {
    memcpy(((char *)(p)),((char *)(&xgr_pen)),sizeof(pen_info));
}

void restore_pen(pen_info *p) {
    memcpy(((char *)(&xgr_pen)),((char *)(p)),sizeof(pen_info));
    set_pen_width(p->pw);
    set_pen_height(p->ph);
}

void placate_x()
{
  XEvent           event;
  XConfigureEvent *xce;
  XMotionEvent    *xme;
  XButtonEvent    *xbe;
    checkX;

  while(XCheckWindowEvent(dpy, win, EVENT_MASK, (XEvent *)&event))

    switch(event.type) {

    case ConfigureNotify:

      xce = (XConfigureEvent *)&event;
      screen_height = xce->height;
      screen_width  = xce->width;

      XClearWindow(dpy, win);
      /* if (!clearing_screen) */ redraw_graphics();

      break;

    case MotionNotify:

      xme = (XMotionEvent *)&event;
      x_mouse_x = xme->x;
      x_mouse_y = xme->y;
      break;

    case ButtonPress:
	xbe = (XButtonEvent *)&event;
#undef button
	x_buttondown = xbe->button;
	mouse_click;
	break;

    case ButtonRelease:
	x_buttondown = 0;
	break;
    }
}

void check_X11_stop() {
    static int count=300;

    if (--count == 0) {
	count = 300;
	checkX;
	placate_x();
    }
}

int get_button()
{
  checkX;

  placate_x();

  return( x_buttondown );
}

int get_mouse_x()
{
  checkX;

  placate_x();

  return( x_mouse_x - (screen_width / 2));
}


int get_mouse_y()
{
  checkX;

  placate_x();

  return((screen_height / 2) - x_mouse_y);
}

 
void floodfill( XImage *img, int x, int y, unsigned long
					   oldColor, unsigned long newColor ) 
{
	int lastBorder;
	int leftLimit, rightLimit;
	int i;
	if (oldColor == newColor) {
		/* Nothing to be done */
		return;
	}
	/* Seek left */
	leftLimit = (-1);
	for (i = x; (i >= 0); i--) {
		if (XGetPixel(img, i, y) != oldColor) {
			break;
		}
		XPutPixel(img, i, y, newColor);
		leftLimit = i;
	}
	if (leftLimit == (-1)) {
		return;
	}
	/* Seek right */
	rightLimit = x;
	for (i = (x+1); (i < screen_width); i++) {	
		if (XGetPixel(img, i, y) != oldColor) {
			break;
		}
		XPutPixel(img, i, y, newColor);
		rightLimit = i;
	}
	/* Look at lines above and below and start paints */
	/* Above */
	if (y > 0) {
		lastBorder = 1;
		for (i = leftLimit; (i <= rightLimit); i++) {
			int c;
			c = XGetPixel(img, i, y-1);
			if (lastBorder) {
				if (c == oldColor) {	
					floodfill(img, i, y-1, oldColor, newColor);
					lastBorder = 0;
				}
			} else if (c != oldColor) {
				lastBorder = 1;
			}
		}
	}
	/* Below */
	if (y < screen_height - 1) {
		lastBorder = 1;
		for (i = leftLimit; (i <= rightLimit); i++) {
			int c;
			c = XGetPixel(img, i, y+1);
			if (lastBorder) {
				if (c == oldColor) {
					floodfill(img, i, y+1, oldColor, newColor);
					lastBorder = 0;
				}
			} else if (c != oldColor) {
				lastBorder = 1;
			}
		}
	}
}
 
void logofill() {
	XImage *img = XGetImage(dpy, win, 0, 0, screen_width, screen_height, -1,
							ZPixmap );
	floodfill( img, xgr_pen.xpos, xgr_pen.ypos,
			   XGetPixel( img, xgr_pen.xpos, xgr_pen.ypos ),
			   color[(xgr_pen.color)+2].pixel );
	
	XPutImage( dpy, win, draw_gc, img, 0, 0, 0, 0, screen_width,
			   screen_height );
  XDestroyImage( img );
}


void set_palette(int n, unsigned int r, unsigned int g, unsigned int b) {
    n+=2;
    color[n].red = r;
    color[n].green = g;
    color[n].blue = b;
    color[n].flags = DoRed|DoGreen|DoBlue;  /* unnecessary? */
    XAllocColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), &color[n]);
}

void get_palette(int n, unsigned int *r, unsigned int *g, unsigned int *b) {
    n+=2;
    *r = color[n].red;
    *g = color[n].green;
    *b = color[n].blue;
}

#endif


