/*
 *    The machine specific stuff for edwin device driver
 *    Douglas 4/3/86
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/raster.h>
#include <rfont.h>
#include <icon.h>
#include <win.h>

/*
 *    Some external routine specs
 */

extern Raster *NewPhysRaster();
extern Raster *NewRaster();
extern RFont  *ReadFontFile();
extern Window *BasicRawWinCreate();
extern Window *WinCreate();

extern char *getenv();

/*
 *    Declarations for window stuff
 */

static Window *CWin;
static Raster *Page;
static int    Pan;   /* Physical panel pointer */
static RSize  CSize; /* Size of window */
RFont  *CurFont;

static WinX = 1023;
static WinY = 767;

static RBox refbox = {{0,0},{1024,768}};
static FillMode = 0;
static CCol = 1;

/*
 * These are the initialisation routine for edwin, they open physical windows,
 * set up fonts and icons for the windows, and make the windows visible
 */

static initialised = 0;

extern int IMP_ARGC;
extern char **IMP_ARGV;

#define patternfile "patterns"
#define NICONS  40
Icon Patterns[NICONS];

EDWIN_DD_INIT(MaxX,MaxY)
   int *MaxX, *MaxY;
{
   char   *Font;
   char   *icon;
   char   *title;
   static RSize   ISize = {64, 64};
   RSize  Size;
   int    i;
   int    Ch;
   RPos   Br;
   RSize  PSize;

   *MaxX = WinX; *MaxY = WinY;
   if (initialised == 2) {
      /* EDWIN_DD_TERM called, so in stowed status */
      resetscreen();
      return;
   } else if (initialised == 1) {
      /* already active - so do nothing */
      return;
   } else {
      /* Really initialising the package */
      initialised = 1;
   }

   /*
    * First set up the icon file
    */

   title = IMP_ARGV[0];
   icon = "ICON";
   icon = getenv(icon);
   CSize.w = WinX + 1;
   CSize.h = WinY + 1;
   Br.x = Br.y = 0;
   /* if (icon == NULL) {
      if (BasicInit(&Br,&CSize,0,0,0,0,0) < 0) Fail("BasicInit");
   } else {
      if (BasicInit(&Br,&CSize,0,&ISize,0,0,icon) < 0)
	 Fail("BasicInit");
   }


   if (BasicParse(&IMP_ARGC,&IMP_ARGV) < 0) Fail("BasicParse"); */

   /* Create a new physical raster */
   if ((Page = NewPhysRaster(CSize.w,CSize.h)) == NULL)
      Fail("NewPhysRaster");

   /* if ((CWin = BasicRawWinCreate(CSize,W_PHYS_TYPE)) == NULL)
      Fail("BasicRawWinCreate"); */

   CWin = WinCreate(&Br,&CSize,&CSize,NULL,NULL,0,NULL,title,NULL,W_PHYS_TYPE);
   if (CWin == NULL) Fail("WinCreate");

   Pan = WinGetPanel(CWin);
   if (Pan < PAN_ROOT) Fail("WinGetPanel");

   if ((PageDefine(Pan,0,Page,NULL,NULL)) == -1)
      Fail("PageDefine");

   PanelGetRank(Pan,&i);
   PanelPop(Pan);
   PanelGetSize(Pan,&PSize);
   *MaxX = WinX = PSize.w - 1;
   *MaxY = WinY = PSize.h - 1;
   Font = getenv("LATTICE_FONT");
   if (Font == NULL) Font = "elite";
   CurFont = ReadFontFile(Font);
   if (CurFont == NULL) Fail("Failed ReadFontFile");

   /* initialise events */
   eventinit();
   WinDisplay(CWin);
   PageSelect(Pan,0);

   /* set up icon patterns for the different fill styles */
   i = ReadIconFile(patternfile,Patterns,NICONS);
   if (i <= 0) Fail("ReadIconFile");
}

EDWIN_DD_TERM()
{
   initialised = 2;
   WinStow(CWin);
}

EDWIN_DD_UPDATE()
{
   PanelUpdate(Pan,refbox,1);
}

EDWIN_DD_CLEAR()
{
   RasterOp(F_0,0,0,0,Page,0,&CSize);
}

/* Background patterns to be used with shading */

static FillMap[] = {0,1,34,22,26,33,27,19,30,5,18};

static int Omode = 0;

EDWIN_DD_COL(Col)
   int Col;
{
   PanelUpdate(Pan,refbox,1);
   CCol = Col;
   EDWIN_DD_MODE(Omode);
}

static int Rop = F_S;   /* current raster operation */

static OpMap[] = {F_S, F_AND, F_1, F_XOR, F_NOTD};

EDWIN_DD_MODE(Mode)
   int Mode;
{
   if (Mode == 0) {
      if (CCol == 0)
	 Rop = F_0;
      else
	 Rop = F_1;
   } else {
      if (Mode <= 4) Rop = OpMap[Mode];
   }
   Omode = Mode;
}

EDWIN_DD_FILL(Mode)
   int Mode;
{
   if (Mode >= 80) Mode = 1;
   if (Mode == 40) Mode = 1;
   if (Mode >  40) {
      Mode = Mode - 40;
   } else {
      if (Mode <= 10) Mode = FillMap [Mode];
   }
   FillMode = Mode;
}

EDWIN_DD_DOT(x,y)
   int x,y;
{
   RPos Br;

   Br.x = x; Br.y = WinY - y;
   GPlot(Rop,&Br,Page);
}

EDWIN_DD_LINE(ox,oy,nx,ny)
   int ox,oy,nx,ny;
{
   RPos Br,To;

   Br.x = ox; Br.y = WinY - oy;
   To.x = nx; To.y = WinY - ny;
   GLine(&To,Rop,&Br,Page);
}

swap(x,y)
   int *x,*y;
{
   int z;

   if (*x > *y) {
      z = *x ; *x = *y ; *y = z;
   }
}

EDWIN_DD_RECT(lx,ly,nx,ny)
   int lx,ly,nx,ny;
{
   RPos Br,To;
   RSize Size;

   swap(&lx,&nx);
   swap(&ly,&ny);
   Br.x = lx; Br.y = WinY - ny;
   Size.w = nx - lx; Size.h = ny - ly + 1;
   if (FillMode > 1) {
      /* use patterns for fill styles */
      RasterOp(F_TILE | F_OR,&Patterns[FillMode].Image,0,0,Page,&Br,&Size);
      ny = WinY - ny; ly = WinY - ly;
      To.x = lx; To.y = ly; GLine(&To,F_1,&Br,Page);
      To.x = nx; GLine(&To,F_1,&Br,Page);
      To.y = ny; GLine(&To,F_1,&Br,Page);
      To.x = lx; GLine(&To,F_1,&Br,Page);
   } else {
      RasterOp(Rop,Page,&Br,&Size,Page,&Br,&Size);
   }
}

EDWIN_DD_HLINE(xl,xr,y)
   int xl,xr,y;
{
/*
 * this routine is used to fill in funny shaped polygons by doing a rasterop
 * on a single scan line and not drawing a box round it
 */

   RPos Br;
   RSize Size;

   Br.x = xl; Br.y = y;
   Size.w = xr - xl; Size.h = 1;
   if (FillMode > 1) {
      RasterOp(F_TILE | F_OR,&Patterns[FillMode].Image,0,0,Page,&Br,&Size);
   } else {
      RasterOp(Rop,Page,&Br,&Size,Page,&Br,&Size);
   }
}

EDWIN_DD_POLY(count,pts)
   int count;
   RPos pts[];
{
   int i;
   RPos Br;

   /* only have to draw outline - HLINE and RECT does the rest */
   Br.x = pts[0].x; Br.y = pts[0].y;
   for (i = 1; i < count - 1; i ++)
      GLine(&pts[i],F_1,&Br,Page);
}

EDWIN_DD_TEXT(sx,sy,ch)
   int sx,sy;
   char *ch;
{
   RPos Br;

   Br.x = sx; Br.y = WinY - sy;
   GPutString(Page,&Br,F_S,CurFont,ch);
}

/*
 *    Input stuff
 */

#define MAXEVENTS  100
#define EVENTMASK  (E_MOVE_CLASS | E_BUTTON_CLASS | E_KEY_CLASS)

Event events[MAXEVENTS];
static int arrayptr = 0;
static int numevents = 0;

eventinit()
{
   KeyboardAttach(Pan);
   if (WinSetMask(CWin,EVENTMASK) == -1) Fail("WinSetMask");
   PanelFlush(Pan);
}

resetscreen()
{
   if (WinStowStatus(CWin) == 1) {
      /* if the window is stowed, then open it */
      WinUnstow(CWin);
      eventinit();
   }
}

EDWIN_DD_INTCHECK()
{
   int thiseve,z;

   /*
    * This routine checks the keyboard for any ^C so that drawing can be
    * interrrupted
    */

   WinSetNoWait(CWin);
   numevents = WinRead(CWin,events,MAXEVENTS);
   if (numevents < 0) Fail("Interrupt check");
   arrayptr = 0;
   while (arrayptr < numevents) {
      if (events[arrayptr].EType == E_KEYPRESS) {
	 z = events[arrayptr].EChar;
	 if ((z == 3) || (z == 127)) {
	    /* control C or DEL key */
	    WinSetWait(CWin);
	    fprintf(stderr,"Interrupted\n");
	    return(1);
         }
      }
      arrayptr ++;
   }
   PanelFlush(Pan);
   WinSetWait(CWin);
   return(0);
}

checkevent()
{
   if (events[arrayptr].EType == E_KEYPRESS) {
      arrayptr ++;
      return(events[arrayptr - 1].EChar);
   }
   return(0);
}

EDWIN_SCREEN_TTGET()
{
   int ret;

   /* look at any old events sitting about */

   PanelUpdate(Pan,refbox,1);
   resetscreen();
   /* while (arrayptr < numevents) {
      ret = checkevent();
      if (ret > 0) return(ret);
      arrayptr ++;
   } */

   /* read some new events */
   while (1) {
      /* only read if there are not some still to be read */
      arrayptr ++;
      if (numevents <= arrayptr) {
	 /* PanelFlush(Pan); */
     	 numevents = WinRead(CWin,events,MAXEVENTS);
	 arrayptr = 0;
         if (numevents < 0) Fail("EDWIN_SCREEN_TTGET - Bad ev count");
      }
      while (arrayptr < numevents) {
         ret = checkevent();
         if (ret > 0) return(ret);
         arrayptr ++;
      }
   }
}

static int lastx = 0, lasty = 0;

static fromcursor = 0;
EDWIN___B_SAM(z,x,y)
   int *z,*x,*y;
{
   int thiseve;

   if (fromcursor == 0) {
      PanelUpdate(Pan,refbox,1);
      resetscreen();  /* open it if it is stowed */
   }
   while (1) {
      /* again only read more if there are none waiting to be processed */
      arrayptr ++;
      if (numevents <= arrayptr) {
	 arrayptr = 0;
	 /* PanelFlush(Pan); */
         numevents = WinRead(CWin,events,MAXEVENTS);
         if (numevents < 0) Fail("EDWIN___B_SAM - bad EV count");
      }
      *z = 0;
      while (arrayptr < numevents) {
         thiseve = events[arrayptr].EType;
         *x = events[arrayptr].EX;
         *y = WinY - events[arrayptr].EY;
         switch (events[arrayptr].EType) {

            case E_CURSOR_MOVES :
            case E_CURSOR_CROSS : {
	       /* dont return unless last event */
	       lastx = *x;
	       lasty = *y;
               if (arrayptr == numevents - 1) return;
	       break; 
	    }

            case E_KEYPRESS : {
               *z = events[arrayptr].EChar;
               return;
            }

            case E_BUTTON : {
               *z = events[arrayptr].EButtons;
               return;
            }

            case E_CLICK  : {
	       /* No click events enabled, because they are too slow
		* (looking for double clicks etc)
		*/
               *z = events[arrayptr].EButtons;
	       if (*z > 15) *z = *z / 16;
               return;
            }

            default :
	       break;
         }
         arrayptr ++;
      }
   }
}

EDWIN___B_REQ(z,x,y)
   int *z, *x, *y;
{
   resetscreen(); /* unstow etc if necessary */
   *z = 0;
   while (1) {
      EDWIN___B_SAM(z,x,y);
      if (((0 < *z) && (*z <= 7)) || ((' ' <= *z) && (*z <= '~'))) break;
   }
}

static Fail(FailS)
   char *FailS;
{
   fprintf(stderr,"EDWIN fails - %s\n",FailS);
   exit(1);
}
