/***********************************************************************
 * 
 *  G F O ! 
 *  by Chee Chew
 *      and 
 *     Ken Duda 
 *
 *  (c) 1990
 *
 *  This software is free for distribution.  This notice, however, may 
 *  not be altered.
 *  
 *  Enjoy! (and punt... )
 *
 ***********************************************************************/

#include "gfo.h"
#include <X11/Xresource.h>
#include<sys/types.h>
#include<sys/time.h>
#include<sys/timeb.h>
  
#define ENDMESSAGE "wwwwipeout!"
#define M_DESIRED_WIDTH 750
#define M_DESIRED_HEIGHT 750
#define SCOREFONT "-adobe-times-bold-i-normal--24-240-75-75-p-128-iso8859-1"
#define ROUNDFONT "-adobe-times-bold-i-normal--24-240-75-75-p-128-iso8859-1"

int XCopyArea();

x_init(XC *xc)
{
  XClassHint chints;
  XSizeHints shints;
  XGCValues gcvals;

  xc->DontSaveUnder = 0;
  if ((xc->dsp = XOpenDisplay(NULL)) == NULL) {
    fprintf(stderr,"cannot open display\n");
    exit(1);
  }
  xc->endwin = NULL;
  xc->hswin = NULL;
  xc->planes = PlanesOfScreen(DefaultScreenOfDisplay(xc->dsp));
/*  printf("planes = %d\n", xc->planes);*/

  if (xc->dsp == NULL) {
    fprintf(stderr,"Can't open display\n");
    exit(1);
  }
  xc->scr = DefaultScreen(xc->dsp);
  xc->width = M_DESIRED_WIDTH;
  xc->height = M_DESIRED_HEIGHT;
  xc->mwin = XCreateSimpleWindow(xc->dsp, RootWindow(xc->dsp,xc->scr), 0, 0,
				 M_DESIRED_WIDTH, M_DESIRED_HEIGHT, 1,
				 WhitePixel(xc->dsp,xc->scr),
				 BlackPixel(xc->dsp,xc->scr));

  xc->wellpixmap = XCreatePixmap(xc->dsp, xc->mwin, xc->width, xc->height,xc->planes);
  xc->pmwidth = xc->width;
  xc->pmheight = xc->height;

  xc->swin = XCreateSimpleWindow(xc->dsp, xc->mwin, 10, 5,
				 M_DESIRED_WIDTH*3/16, M_DESIRED_HEIGHT*3/32, 2,
				 WhitePixel(xc->dsp,xc->scr),
				 BlackPixel(xc->dsp,xc->scr));

  /* Stuff for the window manager */ 
  shints.width = M_DESIRED_WIDTH;
  shints.height = M_DESIRED_HEIGHT;
  shints.flags = PSize;
  
  XStoreName(xc->dsp, xc->mwin, "GFO"); 
  XSetIconName(xc->dsp, xc->mwin, "GFO"); 
  chints.res_name = "gfo" ;
  chints.res_class = "GFO"; 
  XSetStandardProperties(xc->dsp, xc->mwin, "GFO", "gfo", NULL,
			 NULL, NULL, &shints);
  
  if ((xc->roundfont = XLoadQueryFont(xc->dsp, ROUNDFONT)) == NULL) {
    fprintf (stderr, "Can't open font %s\n",ROUNDFONT);
    exit (1);
  }
  if ((xc->scorefont = XLoadQueryFont(xc->dsp, SCOREFONT)) == NULL) {
    fprintf (stderr, "Can't open font %s\n",SCOREFONT);
    exit (1);
  }
  gcvals.foreground = WhitePixel(xc->dsp,xc->scr);
  gcvals.background = BlackPixel(xc->dsp,xc->scr);
  
  xc->bdraw_gc = 
    XCreateGC(xc->dsp, xc->mwin, (GCForeground| GCBackground) , 
	      &gcvals);
  xc->sdraw_gc = 
    XCreateGC(xc->dsp, xc->swin, (GCForeground | GCBackground), &gcvals);
  XSetFont (xc->dsp, xc->sdraw_gc, xc->scorefont->fid);

  xc->wellgc = 
    XCreateGC(xc->dsp, xc->wellpixmap, (GCForeground | GCBackground), &gcvals);

  gcvals.foreground = BlackPixel(xc->dsp,xc->scr);
  gcvals.background = WhitePixel(xc->dsp,xc->scr);

  xc->berase_gc =
    XCreateGC(xc->dsp, xc->mwin, (GCForeground| GCBackground), &gcvals);
  xc->serase_gc = 
    XCreateGC(xc->dsp, xc->swin, (GCForeground | GCBackground), &gcvals);
  xc->wellclear = 
    XCreateGC(xc->dsp, xc->wellpixmap, (GCForeground | GCBackground), &gcvals);

  if( xc->planes == 1 ) {
    SetupMono(xc);
  } else {
    SetupColor(xc);
  }
  SetupFace(xc);
  SetupHigh(xc);
  XSelectInput(xc->dsp, xc->mwin,
	       ButtonPressMask |
	       KeyPressMask |
	       ExposureMask |
	       StructureNotifyMask| 
	       FocusChangeMask | 
	       EnterWindowMask | 
	       LeaveWindowMask);

  XSelectInput(xc->dsp, xc->swin,
	       ExposureMask |
	       StructureNotifyMask);


  XMapWindow(xc->dsp, xc->mwin);
/*	XSynchronize(xc->dsp, 1); */
  
  WaitForMap(xc); 

}

FinishXInit(XC *xc)
{
  XMapSubwindows(xc->dsp, xc->mwin);
}

Pixmap read_pixmap(XC *xc, Window window, char *fname, unsigned int *width, unsigned int *height)
{
  Pixmap temp, ret;
  int x,y;

  if (XReadBitmapFile(xc->dsp, window, fname, width, height, &temp, &x, &y) == BitmapSuccess) {
    ret = XCreatePixmap(xc->dsp, window, *width, *height, xc->planes);
    XCopyPlane(xc->dsp, temp, ret, xc->copy_gc, 0,0, *width, *height, 0,0,1);
    XFreePixmap(xc->dsp, temp);
    return ret;
  } else {
    fprintf(stderr,"Unable to read bitmap file: %s\n", fname);
    *width = 1;
    *height = 1;
    return XCreatePixmap(xc->dsp, xc->mwin, 1,1, xc->planes);
  }
}

WaitForMap(XC *xc)
{
  XEvent event; 
  XNextEvent(xc->dsp, &event);
  XPutBackEvent(xc->dsp, &event);
}

void x_event_loop( void (*button_fn)(int x, int y, int button, int pressed),
	      void (*inout_fn)(int entered, Game *g),
	      void (*key_fn)(char key, Game *g),
	      void (*config_fn)(Game *g, XC *xc, int width, int height),
	      void (*refresh_mwin)(Game *g, int x, int y, int width, int height),
	      void (*refresh_swin)(Game *g,int x, int y, int width, int height),
	     XC *xc,
	     int ousecs,
	     Game *g)
{
  XEvent e;
  fd_set fds;
  struct timeval timeout;
  struct timeb timeb;
  int mill;
  int lookupstring;
  char buffer[3];
  int usecs = ousecs;
   
  while (1) {
    XFlush(xc->dsp);
    if (usecs<0 || usecs >ousecs) return;
    FD_ZERO(&fds);
    FD_SET(ConnectionNumber(xc->dsp), &fds);
    timeout.tv_sec = usecs/1000000;
    timeout.tv_usec = usecs%1000000;
    ftime(&timeb);
    mill = timeb.millitm + 1000*(timeb.time & 255);

    if ((!XPending(xc->dsp)) && (!select(FD_SETSIZE,&fds,0,0,&timeout)))
      return;

    ftime(&timeb);
    usecs -= 1000*((timeb.millitm + 1000*(timeb.time & 255)) - mill);

    XNextEvent(xc->dsp, &e);
    
    switch(e.type) {

    case ButtonPress:
      if (button_fn)
	button_fn(e.xbutton.x, e.xbutton.y,
		  e.xbutton.button, e.type == ButtonPress);
      break;



    case LeaveNotify: 
      if(inout_fn ) inout_fn(e.type == EnterNotify, g);
      break;

    case FocusOut:
      if (inout_fn)
	inout_fn(e.type == FocusIn,g);
      break;
      
    case ConfigureNotify: 
      if(config_fn && (e.xany.window == g->xc->mwin))
	config_fn(g,xc, e.xconfigure.width, e.xconfigure.height);
      break; 

    case Expose:
      if (e.xany.window == g->xc->mwin)
	if (refresh_mwin)
	  refresh_mwin(g, e.xexpose.x, e.xexpose.y, 
		       e.xexpose.width, e.xexpose.height);
	else ;
      else if (e.xany.window == g->xc->swin)
	if (refresh_swin)
	  refresh_swin(g, e.xexpose.x, e.xexpose.y, e.xexpose.width, e.xexpose.height);
	else ;
      else if (e.xany.window == g->xc->endwin) 
	refresh_end(g->xc);
      else if (e.xany.window == g->xc->hswin)
	refresh_hs(g->xc);
      else 
	refresh_stack(e.xany.window,g);
      break;

    case KeyPress:
      if (key_fn) {
	do {
	  lookupstring = XLookupString((XKeyEvent *)&e, buffer, 2, NULL,NULL) ;
	  key_fn(buffer[0], g);
	} while( XCheckTypedWindowEvent(g->xc->dsp, g->xc->mwin, KeyPress, &e));
      }
      break;

    }
  }
}


configure(XC *xc, int width, int height) 
{

  XGCValues gcvals;

  xc->width = width; 
  xc->height = height; 

  if(xc->width > xc->pmwidth || xc->height > xc->pmheight) {
    XFreePixmap(xc->dsp,  xc->wellpixmap);
    xc->wellpixmap = XCreatePixmap(xc->dsp, xc->mwin, xc->width, xc->height, xc->planes);
    xc->pmheight = xc->height;
    xc->pmwidth = xc->width;

    gcvals.foreground = WhitePixel(xc->dsp,xc->scr);
    gcvals.background = BlackPixel(xc->dsp,xc->scr);

    xc->wellgc = 
      XCreateGC(xc->dsp, xc->wellpixmap, (GCForeground | GCBackground), &gcvals);
    
    gcvals.foreground = BlackPixel(xc->dsp,xc->scr);
    gcvals.background = WhitePixel(xc->dsp,xc->scr);
    
    xc->wellclear = 
      XCreateGC(xc->dsp, xc->wellpixmap, (GCForeground | GCBackground), &gcvals);
  }
}



void draw_line(Well *w, Drawable d, GC gc,
	       int x1, int y1, int z1, 
	       int x2, int y2, int z2)
{
  int xx1, xx2, yy1, yy2;

  XDrawLine(w->xc->dsp, d, gc,
	    xx1 = project_x(w,x1,y1,z1), yy1 = project_y(w,x1,y1,z1),
	    xx2 = project_x(w,x2,y2,z2), yy2 = project_y(w,x2,y2,z2));
}


void clipgc(XC *xc,int x,int y,int w,int h )
{
    Region region;
    XRectangle rectangle;
    XEvent xevent;

    region=XCreateRegion();
    xevent.xexpose.x = x;
    xevent.xexpose.y = y;
    xevent.xexpose.width = w;
    xevent.xexpose.height = h;
    
    do {
      rectangle.x =(short) xevent.xexpose.x;
      rectangle.y =(short) xevent.xexpose.y;
      rectangle.width = (unsigned short) xevent.xexpose.width;
      rectangle.height = (unsigned short) xevent.xexpose.height;
      XUnionRectWithRegion(&rectangle, region, region);
    } while (XCheckTypedWindowEvent(xc->dsp,xc->mwin, Expose, &xevent));
    
    XSetRegion(xc->dsp, xc->bdraw_gc, region);
    XSetRegion(xc->dsp, xc->berase_gc, region);
  
}

unclipgc(XC *xc)

{
    Region region;
    XRectangle rectangle;
    XEvent xevent;

    region=XCreateRegion();

    
    rectangle.x =(short) 0;
    rectangle.y =(short)0;
    rectangle.width = (unsigned short) xc->width;
    rectangle.height = (unsigned short) xc->height;
    XUnionRectWithRegion(&rectangle, region, region);
    
    XSetRegion(xc->dsp, xc->bdraw_gc, region);
    XSetRegion(xc->dsp, xc->berase_gc, region);

}

int XCopyColorArea(Display *d, Drawable src, Drawable dest, GC gc, int x, int y,
	       unsigned int w,unsigned int h, int dest_x, int dest_y)
{
  XCopyPlane(d,src,dest,gc,x,y,w,h,dest_x,dest_y,1);
}

mutilate_end_window(XC *xc)
{
  XDestroyWindow(xc->dsp,xc->endwin);
  xc->endwin = NULL;
}

make_end_window(XC *xc)
{

  int width; 

  xc->message = ENDMESSAGE;
  width = XTextWidth(xc->scorefont, xc->message, strlen(xc->message)) + 20;
  xc->endwin = XCreateSimpleWindow(xc->dsp, xc->mwin, 
			       xc->width/2 - width/2,
			       xc->height/2 - 30,
			       width,60,5,
			       WhitePixel(xc->dsp,xc->scr),
			       BlackPixel(xc->dsp,xc->scr));

  XSelectInput(xc->dsp, xc->endwin, ExposureMask);
  xc->endgc = 
    XCreateGC(xc->dsp, xc->swin, 0,0);
  XSetFont (xc->dsp, xc->endgc, xc->scorefont->fid);
  XSetForeground(xc->dsp, xc->endgc, WhitePixel(xc->dsp,xc->scr));
  XSetBackground(xc->dsp, xc->endgc, BlackPixel(xc->dsp,xc->scr));
  XMapWindow(xc->dsp, xc->endwin);
  XFlush(xc->dsp);
}

refresh_end(XC *xc)
{
  int width; 

  width = XTextWidth(xc->scorefont, xc->message, strlen(xc->message)) + 20;
  XDrawString(xc->dsp,xc->endwin, xc->endgc, 10, 35, xc->message, strlen(xc->message));
}

SetupMono(XC *xc)
{
  int n;
  XGCValues gcvals;
  gcvals.foreground = WhitePixel(xc->dsp,xc->scr);
  gcvals.background = BlackPixel(xc->dsp,xc->scr);

  xc->CopyFunction = XCopyArea;
  for (n = 0; n < NUM_TILE_PIXMAPS; n++) {
    xc->tilegc[n] = 
      XCreateGC(xc->dsp, xc->wellpixmap, (GCForeground | GCBackground), &gcvals);

    XSetFillStyle(xc->dsp, xc->tilegc[n], FillOpaqueStippled);
  }
  init_tile_pixmaps(xc);

}



SetupColor(XC *xc)
{
  int n;
  Colormap colormap ;

  colormap = DefaultColormap( xc->dsp, xc->scr);

  xc->CopyFunction = XCopyArea;

  bzero(xc->bgcolor, sizeof(xc->bgcolor));

  xc->bgcolor[0].red = 65535; 
  xc->bgcolor[1].green = 65535; 
  xc->bgcolor[2].blue = 65535; 
  xc->bgcolor[3].red = 65535; 
  xc->bgcolor[3].green = 65535; 
  xc->bgcolor[4].red = 65535; 
  xc->bgcolor[4].blue = 65535; 
  xc->bgcolor[5].green = 65535; 
  xc->bgcolor[5].blue = 65535; 

  if (!XAllocColor(xc->dsp, colormap , &(xc->bgcolor[0]))) puts("failed on 1");
  if (!XAllocColor(xc->dsp, colormap , &(xc->bgcolor[1]))) puts("failed on 1");
  if (!XAllocColor(xc->dsp, colormap , &(xc->bgcolor[2]))) puts("failed on 1");
  if (!XAllocColor(xc->dsp, colormap , &(xc->bgcolor[3]))) puts("failed on 1");
  if (!XAllocColor(xc->dsp, colormap , &(xc->bgcolor[4]))) puts("failed on 1");
  if (!XAllocColor(xc->dsp, colormap , &(xc->bgcolor[5]))) puts("failed on 1");

  for (n = 6; n < NUM_TILE_PIXMAPS; n++) {
    xc->bgcolor[n] = xc->bgcolor[n-6];
  }

  for (n = 0; n < NUM_TILE_PIXMAPS; n++) {
    xc->tilegc[n] = XCreateGC(xc->dsp, xc->wellpixmap, 0,0);
    XSetBackground(xc->dsp, xc->tilegc[n], xc->bgcolor[n].pixel);
    XSetForeground(xc->dsp, xc->tilegc[n], xc->bgcolor[n].pixel);
  }
}

#include "bitmaps/level0"
#include "bitmaps/level1"
#include "bitmaps/level2"
#include "bitmaps/level3"
#include "bitmaps/level4"

init_tile_pixmaps(XC *xc)
{
  float c, step;
  Pixmap p;
  GC gc;
  int n, xy;
  XGCValues gcv;
  static char *pix[5] = {
    level0_bits,
    level1_bits,
    level2_bits,
    level3_bits,
    level4_bits
  };

  for (n = 0; n < NUM_TILE_PIXMAPS; n++) {
    xc->tile_pixmaps[n] = XCreateBitmapFromData(xc->dsp, xc->wellpixmap,pix[n%5],
						level0_width, level0_height);
    XSetStipple(xc->dsp, xc->tilegc[n],xc->tile_pixmaps[n % 5]);
  }

}

ClearMainWin(XC *xc)
{
  XClearWindow(xc->dsp, xc->mwin);
}

pause_game(Game *g)
{
  XEvent e;
  int waiting = 1;
  char buffer[2];

  ClearMainWin(g->xc);
  do {
    XNextEvent (g->xc->dsp, &e);
    if (e.type == KeyPress) {
      (void) XLookupString(&e.xkey, buffer, 2, NULL,NULL) ;
      waiting = buffer[0] != 9;
    }
  } while (waiting);

  redraw_well(g->well, 0,0,g->well->xc->width, g->well->xc->height);

  if (g->block)
    draw_block(g->well, g->block);
}

wait_for_focus(Game *g)
{
  XEvent e;
  int waiting = 1;
  char buffer[2];

  ClearMainWin(g->xc);
  do {
    XNextEvent (g->xc->dsp, &e);
  } while ((e.type != FocusIn) && (e.type != EnterNotify));

  redraw_well(g->well, 0,0,g->well->xc->width, g->well->xc->height);

  if (g->block)
    draw_block(g->well, g->block);
}


show_gfo(Game *g, char *message)
{
  static int message_counter=0;

  ClearMainWin(g->xc);
  XDrawString(g->xc->dsp, g->xc->mwin, g->xc->sdraw_gc, g->xc->width/2-100,
	      g->xc->height/2 - 30, 
	      message, strlen(message));
  XSync(g->xc->dsp, 0);
  sleep(2);
  redraw_well(g->well, 0,0,g->well->xc->width, g->well->xc->height);
}


GetResourceKeydefs(Game *g)
{
  XrmDatabase db;
  char buffer[10];
  char *str_type;
  XrmValue value;
  
  db = XrmGetStringDatabase(XResourceManagerString(g->xc->dsp));
  if(XrmGetResource(db, "gfo.up", "Gfo.Up", &str_type, &value)) {
    g->kd->up = *((char *)value.addr);
  }
  if(XrmGetResource(db, "gfo.down", "Gfo.Down", &str_type, &value)) {
    g->kd->down = *((char *)value.addr);
  }
  if(XrmGetResource(db, "gfo.right", "Gfo.Right", &str_type, &value)) {
    g->kd->right = *((char *)value.addr);
  }
  if(XrmGetResource(db, "gfo.left", "Gfo.Left", &str_type, &value)) {
    g->kd->left = *((char *)value.addr);
  }
  if(XrmGetResource(db, "gfo.rotate1", "Gfo.Rotate1", &str_type, &value)) {
    g->kd->rotate1 = *((char *)value.addr);
  }
  if(XrmGetResource(db, "gfo.rotate2", "Gfo.Rotate2", &str_type, &value)) {
    g->kd->rotate2 = *((char *)value.addr);
  }
  if(XrmGetResource(db, "gfo.rotate3", "Gfo.Rotate3", &str_type, &value)) {
    g->kd->rotate3 = *((char *)value.addr);
  }
  if(XrmGetResource(db, "gfo.upright", "Gfo.Upright", &str_type, &value)) {
    g->kd->upright = *((char *)value.addr);
  }
  if(XrmGetResource(db, "gfo.upleft", "Gfo.Upleft", &str_type, &value)) {
    g->kd->upleft = *((char *)value.addr);
  }
  if(XrmGetResource(db, "gfo.downright", "Gfo.Downright", &str_type, &value)) {
    g->kd->downright = *((char *)value.addr);
  }
  if(XrmGetResource(db, "gfo.downleft", "Gfo.Downleft", &str_type, &value)) {
    g->kd->downleft = *((char *)value.addr);
  }
}
