
/* fix xDrawString() */
/* need to fix to work with colors, GCs */
/* show image mashes orig image data -- should have separate display data. */
/* probably use lots of keyword arguments */
/* do I need `shift'?? (it was for text drawing) */


#include "x-stuff.h"
#include "uchar-remap.h"

#include <math.h>

#include <X.h>
#include <Xlib.h>
#include <Xutil.h>

/* X-related variables */

#define PM_WIDTH 800
#define PM_HEIGHT 500
#define LNWIDTH 1

#define GRAYS_COMPRESS   4    /* gray compression -- use at most 1/4 the total colors */


static  Colormap cmap;
static  Display *disp;
static  Window root;
static  Visual *visual;
static  int screen;
static  XFontStruct *font_info;
static  Pixmap shift;
static  int depth;
static  unsigned int black, white;
static  GC copyGC;

xColor   xGrayMap[256];
uchar    xGrayMapPixels[256];

xColor xWhite;                               /* colors and pixels */
xColor xBlack;
xColor xRed;
xColor xOrange;
xColor xYellow;
xColor xLime;
xColor xSlate;
xColor xBlue;
xColor xGreen;
xColor xPurple;

uchar  xWhitePixel;
uchar  xBlackPixel;
uchar  xRedPixel;
uchar  xOrangePixel;
uchar  xYellowPixel;
uchar  xLimePixel;
uchar  xSlatePixel;
uchar  xBluePixel;
uchar  xGreenPixel;
uchar  xPurplePixel;


static void make_GCs();


boolean xAllocateGreyColorMap(void)	     /* grab colors for greyscales */
{
  int i, j;
  xColor c;
  

  for (i = 0; i < 256; i += GRAYS_COMPRESS) {
    if ((c = xAllocateColor(((RGB) { i, i, i }))).ok == FALSE) {
      return FALSE;
    }

    for (j = i; j < i + GRAYS_COMPRESS; j++) {
      xGrayMap[j]       = c;
      xGrayMapPixels[j] = (uchar) c.color;
    }
  }

  return TRUE;
}

xColor xAllocateColor(RGB rgb)
{
  Colormap tempcmap;
  XColor def;

  tempcmap = cmap;
  def.red   = rgb.r << 8;
  def.green = rgb.g << 8;
  def.blue  = rgb.b << 8;
  def.flags = DoRed | DoGreen | DoBlue;

  if (XAllocColor(disp, tempcmap, &def)) {
    return ((xColor) { def.pixel, 1 });
  }
  else {
    return ((xColor) { 0, 0 });              /* error */
  }
}


xColor xAllocateNamedColor(char *name)
{
  Colormap tempcmap;
  XColor screen, exact;

  tempcmap = cmap;
  if (XAllocNamedColor(disp, tempcmap, name, &screen, &exact)) {
    return ((xColor) { screen.pixel, 1 });
  }
  else {
    return ((xColor) { 0, 0 });              /* error */
  }
}

boolean xOpenDisplay()			     /* open the display */
{
  disp   = XOpenDisplay(NULL);
  screen = DefaultScreen(disp);
  cmap   = DefaultColormap(disp, screen);
  visual = DefaultVisual(disp, screen);
  root   = DefaultRootWindow(disp);
  black  = BlackPixel(disp, screen);
  white  = WhitePixel(disp, screen);
  depth  = DefaultDepth(disp, screen);

  shift  = XCreatePixmap(disp, root, PM_WIDTH, PM_HEIGHT, depth);

  xWhite  = ((xColor) { white, 1 });
  xBlack  = ((xColor) { black, 1 });
  xRed    = xAllocateNamedColor("red");
  xOrange = xAllocateNamedColor("orange");
  xYellow = xAllocateNamedColor("yellow");
  xLime   = xAllocateNamedColor("lime");
  xSlate  = xAllocateNamedColor("slate");
  xBlue   = xAllocateNamedColor("blue");
  xGreen  = xAllocateNamedColor("green");
  xPurple = xAllocateNamedColor("purple");

  xWhitePixel  = (uchar) xWhite.color;
  xBlackPixel  = (uchar) xBlack.color;
  xRedPixel    = (uchar) xRed.color;
  xOrangePixel = (uchar) xOrange.color;
  xYellowPixel = (uchar) xYellow.color;
  xLimePixel   = (uchar) xLime.color;
  xSlatePixel  = (uchar) xSlate.color;
  xBluePixel   = (uchar) xBlue.color;
  xGreenPixel  = (uchar) xGreen.color;
  xPurplePixel = (uchar) xPurple.color;

  make_GCs();

  font_info = XLoadQueryFont(disp, "fixed");

  XFlush(disp);

  return TRUE;
}

void xCloseDisplay()
{
  XCloseDisplay(disp);
}

void xFlushDisplay()
{
  XFlush(disp);
}

xWindow xNewWindow_(char *title, int w, int h, int x, int y)
{
  Window win;
  XSizeHints  sizehints;

  win = XCreateSimpleWindow(disp, root, x, y, w, h, 2, black, white);

  XStoreName(disp, win, title);
  XSetIconName(disp, win, title);

  sizehints.flags = USSize | USPosition;

  sizehints.x      = x;
  sizehints.y      = y;
  sizehints.width  = w;
  sizehints.height = h;

  XSetNormalHints(disp, win, &sizehints);

  XMapRaised(disp, win);

  return ((xWindow) {(ulong) win});
}

void xClearWindow(xWindow win)
{
  XClearWindow(disp, (Window) win.window);
  XFlush(disp);
}

void xKillWindow(xWindow win)
{
  XDestroyWindow(disp, (Window) win.window);
}

void xDrawPoint(xWindow win, xPen pen, int x, int y)
{
  XDrawPoint(disp, (Window) win.window, (GC) pen.gc, x, y);
}

void xDrawLine(xWindow win, xPen pen, int x1, int y1, int x2, int y2)
{
  XDrawLine(disp, (Window) win.window, (GC) pen.gc, x1, y1, x2, y2);
}

void xDrawVector(xWindow win, xPen pen, int x, int y, int dx, int dy)
{
  int xe, ye, xt, yt, ldx, ldy;

  xe = x + dx;
  ye = y + dy;
  XDrawLine(disp, (Window) win.window, (GC) pen.gc, x, y, xe, ye);

  xt = x + 3 * dx / 4;
  yt = y + 3 * dy / 4;
  ldx = dx / 4;
  ldy = dy / 4;
  XDrawLine(disp, (Window) win.window, (GC) pen.gc, xe, ye, xt + ldy, yt - ldx);
  XDrawLine(disp, (Window) win.window, (GC) pen.gc, xe, ye, xt - ldy, yt + ldx);
}

void xDrawRectangle(xWindow win, xPen pen, int x, int y, int w, int h)
{
  XDrawRectangle(disp, (Window) win.window, (GC) pen.gc,
		 x, y, (unsigned int) MAX(w-1,0), (unsigned int) MAX(h-1,0));
}

void xDrawString(xWindow win, xPen pen, int x, int y, char *s)
{
/*  XDrawString(disp, shift, (GC) pen.gc, x, y, s, strlen(s));  */
  fprintf(stderr, "warning: xDrawString not yet implemented\n\n");
  return ;				     /* not yet implemented */
}

xWindow xShowImage(GrayImage im, char *title, int x, int y)
{
  int w = imGetWidth(im);
  int h = imGetHeight(im);
  xWindow win;

  win = xNewWindow_(title, w, h, x, y);

  xShowImageIn(im, win);
  return win;

}

void xShowImageIn(GrayImage im, xWindow win)
{

  if (im->header->Ximage == (void *) NULL) {
    im->header->Ximage = (void *) XCreateImage(disp, visual, depth, ZPixmap, 0,
					       imGetStore(im), imGetWidth(im),
					       imGetHeight(im), 8, 0);
  }


  ucharRemap(imGetStore(im), imGetStoreLen(im), xGrayMapPixels, NULL);
  XPutImage(disp, (Window) win.window, copyGC, (XImage *) im->header->Ximage,
	    0, 0, 0, 0, imGetWidth(im), imGetHeight(im));
  XFlush(disp);
}

void xShowImageInNoRemap(GrayImage im, xWindow win)
{
  if (im->header->Ximage == (void *) NULL) {
    im->header->Ximage = (void *) XCreateImage(disp, visual, depth, ZPixmap, 0,
					       imGetStore(im), imGetWidth(im),
					       imGetHeight(im), 8, 0);
  }

  XPutImage(disp, (Window) win.window, copyGC, (XImage *) im->header->Ximage,
	    0, 0, 0, 0, imGetWidth(im), imGetHeight(im));
  XFlush(disp);
}

void xSetName(xWindow win, char *title)
{
  XStoreName(disp, (Window) win.window, title);
}

boolean xGetPointer(xWindow win, int *x, int *y)
{
  Window root, child, window;
  int rx, ry, wx, wy;
  unsigned int mask;

  window = (Window) win.window;
  if (XQueryPointer(disp, window, &root, &child, &rx, &ry, &wx, &wy, &mask)) {
    *x = wx;
    *y = wy;
    return 1;
  }

  return 0;
}

xEvent xGetEvent(xWindow win)
{
  XEvent event;
  long mask;
  Window w = (Window) win.window;
  xEvent xev = { xNoEvent, 0, 0, 0 };
  static char buff[100];
  

  mask = ButtonPressMask | ButtonReleaseMask | KeyPressMask | PointerMotionMask |
         PointerMotionHintMask | Button1MotionMask | Button2MotionMask | 
         Button3MotionMask | ButtonMotionMask | LeaveWindowMask | EnterWindowMask |
         ExposureMask | VisibilityChangeMask;
  XSelectInput(disp, w, mask);

  if (XCheckWindowEvent(disp, w, mask, &event)) {
    if (event.xany.window == w) {	     /* on my window? */
      xev.xpos = event.xkey.x;
      xev.ypos = event.xkey.y;

      switch (event.type) {
      case KeyPress:
        xev.type  = xKeyPress;
        if (XLookupString(&event.xkey, buff, 100, NULL, NULL) == 1) {
          xev.value = buff[0];
        }
        break;
        
      case ButtonPress:
        xev.type  = xButtonDown;
        xev.value = event.xbutton.button;
        break;
        
      case ButtonRelease:
        xev.type  = xButtonUp;
        xev.value = event.xbutton.button;
        break;

      case MotionNotify:
        xev.type  = xMotion;
        xev.value = 1*(event.xbutton.state == Button1Mask) +
                    2*(event.xbutton.state == Button2Mask) +
                    3*(event.xbutton.state == Button3Mask);
        break;

      case EnterNotify:
        xev.type = xEnterWindow;
        break;

      case LeaveNotify:
        xev.type = xLeaveWindow;
        break;

      case VisibilityNotify:
      case Expose:
        xev.type = xVisibilityChange;
        break;
        
      default:
        break;
      }
    }
  }

  return xev;
}

int xGetButton(xWindow win)
{
  XEvent event;
  long mask;
  int button = 0;
  Window w = (Window) win.window;

  mask = ButtonPressMask;
  XSelectInput(disp, w, mask);

  if (XCheckWindowEvent(disp, w, mask, &event)) {
    if (event.xany.window == w) {	     /* on my window? */
      button = event.xbutton.button;
    }
  }

  return button;
}

xPen xMakePen_(kwArgDecl(xPenKW))
{
  kwDefault(fg,    ((xColor) { black, 1 }));
  kwDefault(bg,    ((xColor) { white, 1 }));
  kwDefault(func,  xFuncCopy);
  kwDefault(width, 0);
  kwDefault(style, xStyleSolid);
  kwDefault(fill,  xFillSolid);
  xPen pen;
  GC gc;
  XGCValues gcv;
  unsigned long mask;
  Font font;

  font = XLoadFont(disp, "fixed");

  mask = GCForeground | GCBackground | GCFunction | GCFont | 
         GCLineWidth  | GCLineStyle  | GCFillStyle;
    
  gcv.foreground         = fg.color;
  gcv.background         = bg.color;
  gcv.function           = func;
  gcv.font               = font;
  gcv.line_width         = width;
  gcv.line_style         = style;
  gcv.fill_style         = fill;
  gc                     = XCreateGC(disp, root, mask, &gcv);

  pen.gc = (void *) gc;

  return pen;
}

static void make_GCs()			     /* Make a couple of simple GCs. */
{
  XGCValues gcv;
  unsigned long mask;
  Font font;

  
  font = XLoadFont(disp, "fixed");
  mask = GCForeground | GCBackground | GCFunction | GCFont;
    
  gcv.foreground         = black;
  gcv.background         = white;
  gcv.function           = GXcopy;
  gcv.font               = font;
  copyGC                 = XCreateGC(disp, root, mask, &gcv);
}





/* sajit is responsible for this rainbow color stuff... */
static  uchar RainbowMapUChar[256]; /* map from grayvalues to colormap indices */

boolean xAllocateRainbowMap(void)    /* grab colors for greyscales */
{
  register int i;
  Colormap tempcmap;
  XColor def;
  xColor xcol;
  double x, step;
  uchar a, b, c;
#if 0
  double PI=3.142;
#endif
  tempcmap = cmap;
  
  step = PI/300.0;
  
  for (i = 1; i < 255; i++) 
  {
    x = ((double) i) * step;

    a = (uchar) (31.5 * (1.0 - cos(x)));
    b = (uchar) (90.0 * (1.0 - sin(x)));
    c = (uchar) (31.5 * (1.0 + cos(x)));

    def.red = (63 - a) << 10;
    def.green = (90 - b) << 9;
    def.blue = (63 - c) << 10;
    def.flags = DoRed | DoGreen | DoBlue;

    if (!XAllocColor(disp, tempcmap, &def)) 
    {
      fprintf(stderr, "\nAllocRainbow: %d can't alloc [r g b]=[%d %d %d]",
              i, def.red, def.green, def.blue);
      RainbowMapUChar[i] = xAllocateNamedColor("black").color;
    }
    else
    {
        
      xcol = ((xColor) {def.pixel});
      RainbowMapUChar[i] = ((uchar) xcol.color);

      /******** debug ******
        fprintf(stderr, "%d res = %d ", i, RainbowMapUChar[i]);
        **********************/

    }
  }

  /* Let 255 be the NULL value and let it map to white */
  /* WARNING - MAKE SURE THAT THIS IS NOT SELECTED FROM ONE
     OF THE COLORS ALLOCATED ABOVE */
  RainbowMapUChar[255] = xAllocateNamedColor("white").color;

  return TRUE;
}

uchar *xGetRainbowMapUchar()
{
  return RainbowMapUChar;
}

void xShowColorImageIn(GrayImage im, xWindow win)
{
  if (im->header->Ximage == (void *) NULL) {
    im->header->Ximage = (void *) XCreateImage(disp, visual, depth, ZPixmap, 0,
                                               imGetStore(im), imGetWidth(im),
                                               imGetHeight(im), 8, 0);
  }

  ucharRemap(imGetStore(im), imGetStoreLen(im), RainbowMapUChar, NULL);
  XPutImage(disp, (Window) win.window, copyGC, (XImage *) im->header->Ximage,
            0, 0, 0, 0, imGetWidth(im), imGetHeight(im));
  XFlush(disp);
}


xWindow xShowColorImage(GrayImage im, char *title, int x, int y)
{
  xWindow win = xNewWindow_(title, imGetWidth(im), imGetHeight(im), x, y);

  xShowColorImageIn(im, win);
  return win;
}

