/*-=-=-=-=-=-=-=-=-=-=-=- 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 SUNVIEW_GRAPHICS

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

#include <suntool/sunview.h>
#include <suntool/canvas.h>
#include <suntool/panel.h>
#include <suntool/scrollbar.h>

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

typedef struct pixrect PIXRECT;

static Frame frame;
static Canvas canvas;
static Panel panel;
static PIXRECT *screenBuf;
static Pixwin *pixw;
static Pixfont *font;

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

static int winFd;			/* window file descriptor */
static int canFd;			/* canvas file descriptor */
extern int errno;

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

static sunviewRepaint ();
static sunviewResize ();
static eachcycle();
static sv_handle_event();

void grf4SvEventHandler(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;
	    }
	 }
}

#define BUTTON1			1
#define BUTTON_BORDER_WIDTH	2

void grf4SvGetButtonExtent(b)
BUTTON_DESC *b;
{
	struct pr_subregion bound;
	int margin;

	pf_textbound (&bound, strlen(b->name), font, b->name);
	margin = 1 + BUTTON_BORDER_WIDTH;
	b->coffX = margin - bound.pos.x;
	b->coffY = margin + ((bound.size.y-1)+bound.pos.y);
	b->minw = 2*margin + bound.size.x;
	b->minh = 2*margin + bound.size.y;
}

void grf4SvDrawButton(b)
BUTTON_DESC *b;
{
	int baseX, baseY;
	int cbaseX, cbaseY;

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

	if (!(b->flags & BF_NOTBOXED))
	 { pw_rop(pixw,baseX,baseY,b->w,b->h,
		  PIX_SET, (PIXRECT*)0, 0,0);
	 }
	else
	 { pw_rop(pixw,baseX,baseY,b->w,b->h,
		  PIX_CLR, (PIXRECT*)0, 0,0);
	 }
	if (b->flags & BF_ILLUMINATED)
	 { pw_ttext (pixw, cbaseX, cbaseY, PIX_CLR, font, b->name);
	 }
	else
	 { pw_rop(pixw,baseX+BUTTON_BORDER_WIDTH,
		       baseY+BUTTON_BORDER_WIDTH,
		       b->w-2*BUTTON_BORDER_WIDTH,
		       b->h-2*BUTTON_BORDER_WIDTH,
		  PIX_CLR, (PIXRECT*)0, 0,0);
	   pw_ttext (pixw, cbaseX, cbaseY, PIX_SET, font, b->name);
	 }
}

grf4SvInit(label, fgcolor, bgcolor, x,y,w,h)
char *label;
int fgcolor;
int bgcolor;
int x,y,w,h;
{
	int rootw, rooth;
	int canvw, canvh;

	font = pf_default();

	buttonInit (&panw, &panh);

	/* set up default window sizes */
	
	if (x == -1 && y == -1)
	 { x = y = 200;
	 }
	else if (x == -1)
	 { x = y;
	 }
	else if (y == -1)
	 { y = x;
	 }
	if (w == -1 && h == -1)
	 { w = h = 300;
	 }
	else if (w == -1)
	 { w = h;
	 }
	else if (h == -1)
	 { h = w;
	 }

	/* create root window frame, and canvas subwindow */
	
	rootw = w+panw;
	rooth = MAX(h,panh);

	frame = window_create (NULL, FRAME, 
			       FRAME_LABEL, label, 
			       WIN_X, x,
			       WIN_Y, y, 
			       WIN_WIDTH, rootw, 
			       WIN_HEIGHT, rooth,
			       0);
	if (frame == 0)
	 { return (-1);
	 }

	canvas = window_create (frame, CANVAS, 
				CANVAS_RETAINED, FALSE,
				CANVAS_FIXED_IMAGE, FALSE,
				CANVAS_REPAINT_PROC, sunviewRepaint,
				CANVAS_RESIZE_PROC, sunviewResize,
				CANVAS_FAST_MONO, TRUE,
				0);
	if (canvas == 0)
	 { fprintf(stderr,"Can't create canvas\n");
	   exit(1);
	 }

	/* usually, the canvas subwindow will be created a bit smaller than
	   the root window. But we don't want this ... we want the canvas
	   window to have the sizes defined by w, h, panw, and panh. So
	   we grow the root window by the difference, and the canvas will
	   in turn expand to the right size. */

	canvw = (int)window_get (canvas, CANVAS_WIDTH);
	canvh = (int)window_get (canvas, CANVAS_HEIGHT);

	window_set (frame, WIN_WIDTH,  rootw+(rootw-canvw),
			   WIN_HEIGHT, rooth+(rooth-canvh), 0);

	winw = rootw;
	winh = rooth;

	if (winw-panw > 0)
	 { screenBuf = mem_create(winw-panw,winh,1);
	   if (screenBuf == 0)
	    { fprintf(stderr,"Can't create draw buffer\n");
	      exit(1);
	    }
	   pr_rop (screenBuf, 0,0,winw-panw,winh, PIX_CLR,(PIXRECT *)0,0,0);
	 }
	else
	 { screenBuf = 0;
	 }
	pixw = canvas_pixwin(canvas);
	if (bgcolor)
	 { pw_blackonwhite(pixw,0,1);
	 }
	else
	 { pw_whiteonblack(pixw,0,1);
	 }
	window_set (frame, WIN_SHOW, TRUE, 0);
	pw_rop(pixw,0,0,winw,winh, PIX_CLR,(PIXRECT *)0,0,0);
	if (panw > 0)
	 { pw_vector(pixw,winw-panw,0,winw-panw,winh-1,
		     (PIX_SET/*|PIX_DONTCLIP*/),1);
	 }

	winFd = (int)window_get(frame, WIN_FD);

	/* get canvas file descriptor and make it non-blocking */
	canFd = (int)window_get(canvas, WIN_FD);
	fcntl (canFd, F_SETFL, fcntl(canFd,F_GETFL,0) | FNDELAY);

	/* ask for particular events to be delivered */
	window_set(canvas, WIN_CONSUME_PICK_EVENTS,
			   LOC_DRAG,
			   MS_LEFT, 
			   0, 0);
	return(1);
}

void grf4SvGetWindowSize(w,h,directionOfY)
int *w, *h;
int *directionOfY;
{
	*w = winw;
	*h = winh;
	*directionOfY = -1;
}

void grf4SvClear(x,y,w,h)
int x,y,w,h;
{
	if (screenBuf)
	 { pr_rop (screenBuf, x, y, w, h, PIX_CLR,(PIXRECT *)0,0,0);
	 }
}

void grf4SvColor(color)
int color;
{
}

void grf4SvFlush(x,y,w,h)
int x,y,w,h;
{
	if (screenBuf)
	 { pw_rop(pixw,x,y,w,h,PIX_SRC, screenBuf, x,y);
	 }
}
	 
void grf4SvDraw(x1,y1,x2,y2)
int x1, y1, x2, y2;
{
	if (screenBuf)
	 { pr_vector(screenBuf,x1,y1,x2,y2,(PIX_SET/*|PIX_DONTCLIP*/),1);
	 }
}

static sunviewRepaint (canvas, pw, repaint_area)
Canvas canvas;
Pixwin *pw;
Rectlist *repaint_area;
{
	if (screenBuf)
	 { pw_rop(pw,0,0,winw-panw, winh, PIX_SRC,screenBuf,0,0);
	 }
	if (panw)
	 { pw_vector(pw,winw-panw,0,winw-panw,winh-1,
		     (PIX_SET/*|PIX_DONTCLIP*/),1);
	 }
	if (redrawHandler)
	 { (*redrawHandler)();
	 }
}

static sunviewResize (canvas, width, height)
Canvas canvas;
int width;
int height;
{
	winw = width;
	winh = height;
	if (screenBuf)
	 { mem_destroy(screenBuf);
	 }
	if (winw-panw > 0)
	 { screenBuf = mem_create(winw-panw,winh,1);
	   pr_rop (screenBuf, 0,0,winw-panw,winh, PIX_CLR,(PIXRECT *)0,0,0);
	 }
	if (resizeHandler)
	 { (*resizeHandler) (winw, winh);
	 }
}

void grf4SvWait(sd)
int sd;
{
	fd_set fdset;
	int n;

	notify_dispatch();
	do
	 { FD_ZERO (&fdset);
	   if (sd >= 0)
	    { FD_SET (sd, &fdset);
	    }
	   FD_SET (canFd, &fdset);
	   FD_SET (winFd, &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(winFd, &fdset))
	    { if (notify_dispatch() != NOTIFY_OK)
	       { notify_perror("dispatch:");
	       }
	    }
	   if (FD_ISSET(canFd, &fdset))
	    { eachcycle();
	    }
	 }
	while (sd >= 0 ? !FD_ISSET(sd, &fdset) : 1);
}

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

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

	notify_dispatch();
	FD_SET (canFd, rfds);
	FD_SET (winFd, rfds);

	n = select (FD_SETSIZE, rfds, wfds, efds, timeout);
	if (n > 0)
	 { if (FD_ISSET(canFd, rfds))
	    { eachcycle();	/* queue event occurred; handle */
	      FD_CLR (canFd, rfds);
	      if (gEvnt)
	       { *gEvnt = 1;
	       }
	      n--;
	    }
	   if (FD_ISSET(winFd, rfds))
	    { if (notify_dispatch() != NOTIFY_OK)
	       { notify_perror("dispatch:");
	       }
	      FD_CLR (winFd, rfds);
	      if (gEvnt)
	       { *gEvnt = 1;
	       }
	      n--;
	    }
	 }
	return(n);
}

void grf4SvCycle()
{
	notify_dispatch();
	eachcycle();
}

static eachcycle()
{
	Event e;
	int status;

	while ((status = window_read_event (canvas, &e)) != -1)
	 { canvas_event(canvas, &e);
	   sv_handle_event(&e);
	 }
	if (status == -1 && errno != EWOULDBLOCK)
	 { perror ("Argh!");
	 }
}

static sv_handle_event(e)
Event *e;
{
 int x, y;

 switch (event_action(e))
  { default:
       break;
    case LOC_DRAG:
     { if (motionHandler)
	{ (*motionHandler)(event_x(e), (winh-1)-event_y(e));
	}
#if 0
       /* XXX need to fix lost events */
	{ /* XXX sunView can lose mouse up events that conflict with
	     a drag event, and so we need to double check here! */
	  if ((short)window_get(canvas, WIN_EVENT_STATE, MS_LEFT) == 0)
	   { dragging = 0;
	   }
#endif
       break;
     }
    case MS_LEFT:
     { if (buttonHandler)
	{ (*buttonHandler)(1,event_is_down(e),event_x(e),(winh-1)-event_y(e));
	}
       break;
     }
    case MS_MIDDLE:
     { if (buttonHandler)
	{ (*buttonHandler)(2,event_is_down(e),event_x(e),(winh-1)-event_y(e));
	}
       break;
     }
    case MS_RIGHT:
     { if (buttonHandler)
	{ (*buttonHandler)(3,event_is_down(e),event_x(e),(winh-1)-event_y(e));
	}
       break;
     }
    case LOC_WINEXIT:
     { if (winExitHandler)
	{ (*winExitHandler)();
	}
       break;
     }
  }
}

grf4SvSetupFxns (ftab)
struct grf4fxnTable *ftab;
{
	ftab->getWindowSize = grf4SvGetWindowSize;
	ftab->cycle = grf4SvCycle;
	ftab->wait = grf4SvWait;
	ftab->select = grf4SvSelect;
	ftab->color = grf4SvColor;
	ftab->eventHandler = grf4SvEventHandler;

	ftab->drawButton = grf4SvDrawButton;
	ftab->getButtonExtent = grf4SvGetButtonExtent;

	ftab->pxlDraw = grf4SvDraw;
	ftab->pxlFlush = grf4SvFlush;
	ftab->pxlClear = grf4SvClear;

	return (1);
}

#else

#include <stdio.h>

grf4SvInit ()
{
	return(-1);
}

grf4SvSetupFxns()
{
	fprintf (stderr, "grf4Init(): Sunview support not available\n");
	return (-1);
}

#endif
