/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1991 by John E. Lloyd.				    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

#ifdef GL_GRAPHICS

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>

#include <gl/gl.h>
#include <device.h>

#include "../h/cdefs.h"
#include "../h/timeval.h"
#include "grf4.h"
#include "grf4Internal.h"

extern int errno;

static long winx;
static long winy;
static long winw;
static long winh;
static int panw;
static int panh;
static long drawAreaVisible;

static Colorindex fgColor;
static Colorindex bgColor;

static int (*resizeHandler)();
static int (*redrawHandler)();
static int (*motionHandler)();
static int (*buttonHandler)();
static int (*winExitHandler)();

static int screenCoordsSet = 0;

static Matrix identMatrix = { 1.0, 0.0, 0.0, 0.0,
			      0.0, 1.0, 0.0, 0.0,
			      0.0, 0.0, 1.0, 0.0,
			      0.0, 0.0, 0.0, 1.0 };


static unsigned long getcpack();
static void glwfEventHandler();
static void setScreenCoords();
static void clearScreenCoords();
static void drawDisplay();
static void glwfGetButtonExtent();
static void glwfDrawButton();
static void glwfDrawButtonFxn();
static void glwfWait();
static eachcycle();
static handle_event();

static unsigned long getcpack()
{
	short r,g,b;
	gRGBcolor(&r,&g,&b);
	return (RGBPACK(r,g,b));
}

static void glwfEventHandler(code, fxn)
int code;
int (*fxn)();
{
	switch (code)
	 { case REDRAW_EVENT:
	    { redrawHandler = fxn;
	      break;
	    }
	   case RESIZE_EVENT:
	    { resizeHandler = fxn;
	      break;
	    }
	   case MOTION_EVENT:
	    { motionHandler = fxn;
	      break;
	    }
	   case BUTTON_EVENT:
	    { buttonHandler = fxn;
	      break;
	    }
	   case WINEXIT_EVENT:
	    { winExitHandler = fxn;
	      break;
	    }
	 }
}

static void setScreenCoords()
{
	screenCoordsSet = 1;

	pushviewport();
	pushmatrix();
	viewport((Screencoord)0,(Screencoord)(winw-1),
		 (Screencoord)0,(Screencoord)(winh-1));
	ortho2 (-0.5,winw-0.5,-0.5,winh-0.5);
}

static void clearScreenCoords()
{
	ortho2 (-0.5,winw-panw-0.5,-0.5,winh-0.5);
	popmatrix();
	popviewport();

	screenCoordsSet = 0;
}

#define LIGHT_GREY	RGBPACK(160,160,160)
#define DARK_GREY	RGBPACK(100,100,100)
#define DEEP_GREY	RGBPACK(60,60,60)
#undef WHITE
#define WHITE		RGBPACK(255,255,255)
#undef BLACK
#define BLACK		RGBPACK(0,0,0)

static void drawDisplay()
{
	long savec;

	setScreenCoords();
	savec = getcpack();

	frontbuffer(TRUE);
	cpack (bgColor);
	clear ();
	cpack (WHITE);
	if (panw)
	 { move2i (winw-panw,0);
	   draw2i (winw-panw,winh-1);
	   cpack (LIGHT_GREY);
	   rectfi (winw-panw+1,0,winw-1,winh-1);
	 }
	frontbuffer(FALSE);

	cpack (savec);
	clearScreenCoords();
}

#define BUTTON_BORDER_WIDTH	2

static void glwfGetButtonExtent(b)
BUTTON_DESC *b;
{
	int height;
	int descent;
	int margin;

	descent = getdescender();
	height = getheight();	
	margin = 2 + BUTTON_BORDER_WIDTH;
	b->coffX = margin;
#ifdef _AIX
/* AIX didn't seem to get the character position right */
	b->coffY = margin + descent/2;
#else
	b->coffY = margin + descent;
#endif
	b->minw = 2*margin + strwidth(b->name);
	b->minh = 2*margin + (height-descent-1);
}

static void glwfDrawButton(b)
BUTTON_DESC *b;
{
	int x1,x2;
	int y1,y2;

	int baseColor;
	int charColor;
	int topEdgeColor;
	int bottomEdgeColor;

	int cbaseX;
	int cbaseY;

	x1 = b->x;
	y1 = b->y;
	x2 = x1 + b->w - 1;
	y2 = y1 + b->h - 1;

	cpack(WHITE);	/* white */

	cbaseX = b->x + b->coffX + (b->w-b->minw)/2;
	cbaseY = b->y + b->coffY + (b->h-b->minh)/2;

	if (b->flags & BF_NOTBOXED)
	 { cpack (LIGHT_GREY);
	   rectfi (x1,y1,x2,y2);
	   if (b->flags & BF_ILLUMINATED)
	    { cpack (WHITE);
	    }
	   else
	    { cpack (BLACK);
	    }
	   cmov2i (cbaseX, cbaseY);
	   charstr (b->name);
	 }
	else
	 { cpack(BLACK);
	   recti (x1,y1,x2,y2);		/* outer black box */
	   if (b->flags & BF_ILLUMINATED)
	    { baseColor = LIGHT_GREY;
	      charColor = WHITE;
	      topEdgeColor = WHITE;
	      bottomEdgeColor = DARK_GREY;
	    }
	   else
	    { baseColor = DARK_GREY;
	      charColor = BLACK;
	      topEdgeColor = DEEP_GREY;
	      bottomEdgeColor = LIGHT_GREY;
	    }
	   cpack (topEdgeColor);
	   recti (x1+1,y1+1,x2-1,y2-1);
	   cpack (bottomEdgeColor);
	   move2i (x1+1,y1+1);
	   draw2i (x2-1,y1+1);
	   cpack (baseColor);
	   rectfi (x1+2,y1+2,x2-2,y2-2);
	   cpack (charColor);
	   cmov2i (cbaseX, cbaseY);
	   charstr (b->name);
	 }
}

static void glwfDrawButtonFxn(b)
BUTTON_DESC *b;
{
	long savec;

	savec = getcpack();
	if (!screenCoordsSet)
	 { setScreenCoords();
	   frontbuffer(TRUE);
	   glwfDrawButton(b);
	   frontbuffer(FALSE);
	   clearScreenCoords();
	 }
	else
	 { glwfDrawButton(b);
	 }
	cpack (savec);
}

#define MIN_WINW	21
#define MIN_WINH	21

#ifdef _AIX
#include <unistd.h>
#include <fcntl.h>
int winFd;
#endif

grf4GlwfInit(name, fgcolor, bgcolor, x,y,w,h)
char *name;
int fgcolor;
int bgcolor;
int x,y,w,h;
{
#ifdef _AIX
	char isopen[FD_SETSIZE];
	int k;

	bzero (isopen, FD_SETSIZE);
	for (k=0; k<FD_SETSIZE; k++)
	 { isopen[k] = (fcntl(k,F_GETFD,0) >= 0);
	 } 
	winFd = -1;
#endif

#ifndef _AIX
	foreground();
#endif	
	if (x != -1)
	 { prefposition (x,x+w-1,y,y+h-1);
	 }
#ifdef _AIX 
/* XXX XXX XXX AIX doesn't seem to support interactive placement! */
	else
	 { prefposition (256,511,256,511);
	 }
#endif
	if (winopen (name) == -1)
	 { return (-1);
	 }
	buttonInit (&panw, &panh);
	getorigin (&winx, &winy);
	getsize (&winw, &winh);
	if (panw)
	 { /* Make sure the new window is big enough to include the
	      panel. */
	   winw += panw;
	   winh = MAX(panh,winh);
	   winposition (winx, winx+winw-1, winy, winy+winh-1);
	   minsize (panw+MIN_WINW, MAX(MIN_WINH,panh));
	   winconstraints();
	 }
	RGBmode();
	doublebuffer();
	gconfig();
	
	bgColor = bgcolor;
	fgColor = fgcolor;

	/* setup for receiving events */

	qdevice(CURSORX);
	qdevice(CURSORY);
	qdevice(LEFTMOUSE);
	qdevice(MIDDLEMOUSE);
	qdevice(REDRAW);
	qdevice(PIECECHANGE);

	loadmatrix(identMatrix);
	
	if (winw-panw-1 >= 0)
	 { viewport ((Screencoord)0, (Screencoord)(winw-panw-1), 
		     (Screencoord)0, (Screencoord)(winh-1));
	   drawAreaVisible = 1;
	 }
	else
	 { drawAreaVisible = 0;
	 }
	ortho2 (-0.5,winw-panw-0.5,-0.5,winh-0.5);

	drawDisplay();

#ifdef _AIX
	for (k=0; k<FD_SETSIZE; k++)
	 { if (!isopen[k] && (fcntl(k,F_GETFD,0) >= 0))
	    { if (winFd == -1)
	       { winFd = k;
	       }
	      else
	       { fprintf (stderr,"grf4Init() -- more than 1 winFd detected\n");
		 exit (-1);
	       }
	    }
	 } 
	if (winFd == -1)
	 { fprintf (stderr, "grf4Init() -- winFd not detected\n");
	   exit (-1);
	 }
#endif
	cpack(fgColor);

	return (1);
}

void glwfGetWindowSize(w,h)
int *w, *h;
{
	*w = winw;
	*h = winh;
}

void glwfClear(x,y,w,h)
int x,y,w,h;
{
	unsigned long savec;
	savec = getcpack();
	cpack(bgColor);
	clear();
	cpack(savec);
}

void glwfColor(color)
int color;
{
#if  0
	fgColor = color;
	cpack(color);
#endif
}

void glwfFlush(x,y,w,h)
int x,y,w,h;
{
	swapbuffers();
}
	 
void glwfDraw(x1,y1,x2,y2)
int x1, y1, x2, y2;
{
	move2 (x1, y1);
	draw2 (x2, y2);
}

static void glwfWait(sd)
int sd;
{
	fd_set fdset;
	int qfd, n;
#ifdef _AIX
	qfd = winFd;
#else
	qfd = qgetfd();
#endif
	do
	 { FD_ZERO (&fdset);
	   if (sd >= 0)
	    { FD_SET (sd, &fdset);
	    }
	   FD_SET (qfd, &fdset);
	   while ((n = select (FD_SETSIZE, &fdset, NULL, NULL, NULL)) < 0 &&
		   errno == EINTR)
	      ;
	   if (n < 0)
	    { perror ("grf4Wait() -- error in select");
	      exit (-1);
	    }
	   if (FD_ISSET(qfd, &fdset))
	    { eachcycle();
	    }
	 }
	while (sd >= 0 ? !FD_ISSET(sd, &fdset) : 1);
}

glwfSelect (gEvnt, rfds, wfds, efds, timeout)
int *gEvnt;
fd_set *rfds, *wfds, *efds;
struct timeval *timeout;
{
	fd_set rset;
	int qfd, n;

	if (!rfds)
	 { rfds = &rset;
	   FD_ZERO (rfds);
	 }
	if (gEvnt)
	 { *gEvnt = 0;
	 }

	/* set qfd to the file descriptor for queue events */
#ifdef _AIX
	qfd = winFd;
#else
	qfd = qgetfd();
#endif

	FD_SET (qfd, rfds);

	n = select (FD_SETSIZE, rfds, wfds, efds, timeout);
	if (n > 0)
	 { if (FD_ISSET(qfd, rfds))
	    { eachcycle();	/* queue event occurred; handle */
	      FD_CLR (qfd, rfds);
	      if (gEvnt)
	       { *gEvnt = 1;
	       }
	      n--;
	    }
	 }
	return(n);
}

void glwfCycle()
{
	eachcycle();
}

static int motionX = -1;
static int motionY = -1;

static eachcycle()
{
	long int dev;
	short int val;

	while (qtest())
	 { dev = qread(&val);
	   handle_event(dev, val);
	 }
	if (motionHandler && (motionX != -1 || motionY != -1))
	 { if (motionX == -1)
	    { motionX = getvaluator(CURSORX) - winx;
	    }
	   if (motionY == -1)
	    { motionY = getvaluator(CURSORY) - winy;
	    }
	   (*motionHandler)(motionX, motionY);
	 }
}

static handle_event(dev, val)
long int dev;
short int val;
{
 switch (dev)
  { default:
     { break;
     }
    case PIECECHANGE:
    case REDRAW:
     { long w, h;
       int resizeEvent;

       getsize(&w, &h);
       getorigin(&winx,&winy);
       if (w != winw || h != winh)
	{ /* resize event */
	  winw = w;
	  winh = h;
	  ortho2 (-0.5,winw-panw-0.5,-0.5,winh-0.5);
	  if (winw-panw-1 >= 0)
	   { viewport ((Screencoord)0, (Screencoord)(winw-panw-1), 
		       (Screencoord)0, (Screencoord)(winh-1));
	     drawAreaVisible = 1;
	   }
	  else
	   { drawAreaVisible = 0;
	   }
	  if (resizeHandler)
	   { (*resizeHandler)(w,h);
	   }
	}
       drawDisplay();
       if (redrawHandler)
        { (*redrawHandler)();
	}
       break;
     }
    case CURSORX:
     { motionX = (val-winx);
       break;
     }
    case CURSORY:
     { motionY = (val-winy);
       break;
     }
    case LEFTMOUSE:
     { if (buttonHandler)
	{ (*buttonHandler)(1, (val!=0), getvaluator(CURSORX)-winx,
					getvaluator(CURSORY)-winy);
	}
       break;
     }
    case MIDDLEMOUSE:
     { if (buttonHandler)
	{ (*buttonHandler)(2, (val!=0), getvaluator(CURSORX)-winx,
					getvaluator(CURSORY)-winy);
	}
       break;
     }
    case RIGHTMOUSE:
     { if (buttonHandler)
	{ (*buttonHandler)(3, (val!=0), getvaluator(CURSORX)-winx,
					getvaluator(CURSORY)-winy);
	}
       break;
     }
  }
}

grf4GlwfSetupFxns (ftab)
struct grf4fxnTable *ftab;
{
	ftab->getWindowSize = glwfGetWindowSize;
	ftab->cycle = glwfCycle;
	ftab->wait = glwfWait;
	ftab->select = glwfSelect;
	ftab->color = glwfColor;
	ftab->eventHandler = glwfEventHandler;

	ftab->drawButton = glwfDrawButtonFxn;
	ftab->getButtonExtent = glwfGetButtonExtent;

	ftab->pxlDraw = glwfDraw;
	ftab->pxlFlush = glwfFlush;
	ftab->pxlClear = glwfClear;

	return (1);
}

#else

#include <stdio.h>

grf4GlwfInit ()
{
	return(-1);
}

grf4GlwfSetupFxns()
{
	fprintf (stderr, "grf4Init(): GL wire-frame support not available\n");
	return (-1);
}

#endif
