/*
 * irisio.c - Nina Amenta, Aug. 1989
 * kaliprint.c - Nina Amenta, Aug. 1989 *
 *
 * modified: merged kaliprint and irisio into io,
 * so can print directly from kali as well as kaliprint.
 * 7/94 Tamara Munzner 
 *
 * entirely rewritten by Ed H. Chi
 * 
 * $Id: xio.c,v 1.3 1996/10/07 15:56:06 slevy Exp $
 * $Log: xio.c,v $
 * Revision 1.3  1996/10/07  15:56:06  slevy
 * XTextProperties want unsigned chars.
 *
 * Revision 1.2  1996/10/05  02:13:04  slevy
 * Get int vs. unsigned int's correct for XGetGeometry() params.
 *
 * Revision 1.1  1996/10/05  01:32:41  slevy
 * Initial revision
 *
 * Revision 1.1  1996/06/11  04:57:54  slevy
 * Initial revision
 *
 * Revision 1.4  1994/09/12  23:14:41  chi
 * version 3.0 changes
 * header file recreated
 *
 * Revision 1.3  1994/08/26  16:29:02  chi
 * rewritten by Ed H. Chi (chi@geom)
 * start of a port to X11
 *
 * Revision 1.2  1994/08/15  20:05:38  chi
 * significant code cleanup.
 * header files created.
 * main.h created
 * changed #defines in main.h from DRAW to KALIDRAW, CUT to .... etc
 */

#include <math.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

#define __EMX__ 1	/* Hack, to avoid conflict btwn gcc and Xlib wchar_t */

#include "main.h"
#include "symmetry.h"
#include "io.h"
#include "kali.h"

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

#define MAXDEPTH 25
static POINT tstack[MAXDEPTH];

static float GXx0, GXy0;
static int pushed = 0;

struct GXdata {
  Display *dpy;
  Window w, wvis;
  GC	gc;
  Colormap cmap;
  unsigned int	depth;
  int		xbase, ybase;
  unsigned int	xsize, ysize;
  unsigned int	screeny;
  float	x0, xscale, y0, yscale;
  int	dynvis;			/* Dynamic Visual -- must we XFreeColor()? */
  int   abgr;			/* Current color */
  int	hascpixel;
  unsigned long cpixel;
  Pixmap backpix;
};

static struct GXdata *gxd;

void GXinit(DRAWER *d, Display *dpy, Window w)
{
  Screen *s = DefaultScreenOfDisplay(dpy);
  if(d->data)
    free(d->data);
   
  d->data = malloc(sizeof(struct GXdata));
  gxd = (struct GXdata *)(d->data);		/* Save in static area */
  gxd->dpy = dpy;
  gxd->w = gxd->wvis = w;
  gxd->x0 = gxd->y0 = 0;
  gxd->xscale = gxd->yscale = 1;
  gxd->screeny = 0;				/* Not yet known */
  gxd->hascpixel = 0;
  gxd->cmap = DefaultColormapOfScreen(s);
  gxd->dynvis = (DefaultVisualOfScreen(s)->class & 1) != 0;
  gxd->backpix = None;
  if(w == None) {
    gxd->xbase = gxd->ybase = 100;
    gxd->xsize = 640;  gxd->ysize = 480;
  } else {
    gxd->gc = XCreateGC(dpy, w, 0, NULL);
    reshapeviewport();
  }
}

swapbuffers()
{
   if(gxd->backpix != None)
	XCopyArea(gxd->dpy, gxd->backpix, gxd->wvis, gxd->gc,
		0, 0, gxd->xsize, gxd->ysize, 0, 0);
}

winset(int w)
{ /* Could do something useful, but for now just assume we have only 1 window */
}

winconstraints()
{}

foreground()
{}

/*
 * Must be called after GXinit()
 */
int
winopen(char *title)
{
  Screen *s = DefaultScreenOfDisplay(gxd->dpy);
  XSetWindowAttributes swa;
  XTextProperty tp;
  XSizeHints szh;

  swa.backing_pixel = BlackPixelOfScreen(s);
  swa.event_mask = ButtonPressMask|ButtonReleaseMask|ButtonMotionMask
		  |KeyPressMask|KeyReleaseMask|ExposureMask;
  gxd->w = gxd->wvis =
	XCreateWindow(gxd->dpy, RootWindowOfScreen(s),
		gxd->xbase, gxd->ybase, gxd->xsize, gxd->ysize,
		0, DefaultDepthOfScreen(s), InputOutput,
		DefaultVisualOfScreen(s),
		CWBackPixel|CWEventMask, &swa);
  gxd->gc = XCreateGC(gxd->dpy, gxd->w, 0, NULL);

  tp.value = (unsigned char *)title;
  tp.encoding = XA_STRING;
  tp.format = 8;
  tp.nitems = strlen(title);
  XSetWMName(gxd->dpy, gxd->wvis, &tp);

  szh.flags = USPosition;
  szh.x = gxd->xbase;
  szh.y = gxd->ybase;
  szh.width = gxd->xsize;
  szh.height = gxd->ysize;
  XSetWMSizeHints(gxd->dpy, gxd->wvis, &szh, XA_WM_NORMAL_HINTS);

  XMapWindow(gxd->dpy, gxd->wvis);
  reshapeviewport();
  return gxd->wvis;
}

getsize(long *x, long *y)
{
  *x = gxd->xsize;
  *y = gxd->ysize;
}

getorigin(long *x, long *y)
{
  *x = gxd->xbase;
  *y = gxd->ybase;
}

doublebuffer()
{
  if(gxd->backpix != None)
    XFreePixmap(gxd->dpy, gxd->backpix);
  gxd->backpix = gxd->w = XCreatePixmap(gxd->dpy, gxd->wvis,
		gxd->xsize, gxd->ysize, gxd->depth);
}

gconfig()
{ }

RGBmode()
{ }

void
prefposition(int x0, int x1, int y0, int y1)
{
  gxd->xbase = x0;
  gxd->ybase = y0;
  gxd->xsize = x1-x0+1;
  gxd->ysize = y1-y0+1;
}

reshapeviewport()
{
  Window rootw;
  int x0, y0;
  unsigned int screenx, border, depth;
  int oldx = gxd->xsize, oldy = gxd->ysize;

  XGetGeometry(gxd->dpy, gxd->wvis, &rootw, &gxd->xbase, &gxd->ybase,
		&gxd->xsize, &gxd->ysize,
		&border, &gxd->depth);
  if(gxd->backpix != None && (oldx != gxd->xsize || oldy != gxd->ysize))
	doublebuffer();
  if(gxd->screeny == 0) {
	XGetGeometry(gxd->dpy, rootw, &rootw, &x0,&y0, &screenx,
		&gxd->screeny, &border, &depth);
  }
}

void
ortho2(float left, float right, float bottom, float top)
{
  gxd->x0 = left;
  gxd->y0 = bottom;
  gxd->xscale = gxd->xsize / (right - left);
  gxd->yscale = gxd->ysize / (top - bottom);
}

#define MAXC 20
static int nc = 0;
static struct knowncolor {
  unsigned long pixel;
  int abgr;
} knowncolors[MAXC];

cpack(int abgr)
{
  XColor c;
  int i;

  if(gxd->abgr == abgr)
    return;

  for(i = nc; --i >= 0 && knowncolors[i].abgr != abgr; )
    ;
  if(i < 0) {
    if(nc >= MAXC) {
	if(gxd->dynvis)
	    XFreeColors(gxd->dpy, gxd->cmap, &knowncolors[MAXC-1].pixel, 1, 0);
	nc--;
    }
    i = nc++;
    c.red = (abgr & 0xFF) * 257;
    c.green = ((abgr>>8)&0xFF) * 257;
    c.blue = ((abgr>>16)&0xFF) * 257;
    knowncolors[i].abgr = abgr;
    if(XAllocColor(gxd->dpy, gxd->cmap, &c)) {
	knowncolors[i].pixel = c.pixel;
    } else {
	knowncolors[i].pixel = (abgr == 0)
			? BlackPixelOfScreen(DefaultScreenOfDisplay(gxd->dpy))
			: WhitePixelOfScreen(DefaultScreenOfDisplay(gxd->dpy));
    }
  }
  gxd->abgr = abgr;
  XSetForeground(gxd->dpy, gxd->gc, knowncolors[i].pixel);
}

clear()
{
  XSetBackground(gxd->dpy, gxd->gc, gxd->cpixel);
  XFillRectangle(gxd->dpy, gxd->w, gxd->gc, 0, 0, gxd->xsize, gxd->ysize);
}

#define MAXP 16
static XPoint xp[MAXP];
static int xpc = 0;
static int in_line = 0;

v2f(float p[2])
{
  if(xpc >= MAXP) {
     if(in_line) {
	endline();
	bgnline();
     } else {
	endpoint();
	bgnpoint();
     }
  }
  xp[xpc].x = (p[0] - gxd->x0)*gxd->xscale;
  xp[xpc].y = (p[1] - gxd->y0)*gxd->yscale;
  xpc++;
}

bgnline()
{
  xpc = 0;
  in_line = 1;
}

endline()
{
  XDrawLines(gxd->dpy, gxd->w, gxd->gc, xp, xpc, CoordModeOrigin);
  xpc = 0;
}

bgnpoint()
{
  xpc = 0;
  in_line = 0;
}

endpoint()
{
  XDrawPoints(gxd->dpy, gxd->w, gxd->gc, xp, xpc, CoordModeOrigin);
  xpc = 0;
}

   
void GXPush(DRAWER *d) {
   tstack[pushed].x = GXx0;
   tstack[pushed].y = GXy0;
   if(++pushed>=MAXDEPTH) {
	pushed = MAXDEPTH-1;
	fprintf(stderr, "GXPush(%d)\n", pushed);
   }
}

void GXTranslate(DRAWER *d, double x, double y) {
  GXx0 += x;
  GXy0 += y;
}

void GXPop(DRAWER *d) {
   pushed--;
   if(pushed < 0) {
	fprintf(stderr, "GXPop(%d)\n", pushed);
	pushed = 0;
   }
   GXx0 = tstack[pushed].x;
   GXy0 = tstack[pushed].y;
}


void GXDrawLine(DRAWER *drawer, LINE *l) {
  float v1[2], v2[2];
  v1[0] = l->m[SX] + GXx0;  v1[1] = l->m[SY] + GXy0;
  v2[0] = l->m[EX] + GXx0;  v2[1] = l->m[EY] + GXy0;
  bgnline();
  v2f(v1);
  v2f(v2);
  endline();
}  


void GXDrawLines(DRAWER *drawer, LINE *obj) {
  LINE *l;
  for(l=obj; l!=NULL; l=l->next)
      GXDrawLine(drawer, l);
}

void GXDrawPoints(DRAWER *drawer, POINT *obj, int count) {
  int i;
  bgnpoint();
  for(i = 0; i < count; i++)
      v2f(&obj[i].x);
  endpoint();
}

DRAWER GXDraw = {
   "GX",
   GXDrawLines,
   GXPush,		/* ignore push */
   GXTranslate,
   GXPop,		/* ignore pop */
   GXDrawLine,
   GXDrawPoints,
   NULL
};
