/***************************************************************************
 *
 * bmapper.c  -  a simple bitmap game to test "C" and VDI
 *
 * Description:
 *               copies a metafile into a bitmap using VDI.
 *               then creates an n x m grid of windows
 *               on the bitmap.
 *               the lower right corner of the grid of windows is
 *               not created. 
 *               the remaining windows are then translated using
 *               movewindow into the null spot.
 *               random selection of nearest window to move is used.
 *
 */

#pragma extend

/*
 * The standard iC 286 V3.2 include files are first
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <udi.h>

#include <rmxc.h>
#include <rmxerr.h>

#include <rgi.h>

static char ucginame[256];
static TOKEN cgiconn, metconn;
static char cgibuff[1024], metbuff[2048];

static EXCEPTIONSTRUCT noexcept = {0,0,0};

static BitmapId bmap_t;
static WindowId wind_t[256];
static WindowId save_t[256];
static int randir;
static int olddir;
static int report_it;
static int same_pri;
static DC rel_move;
static int wspace;
static DC winsize;
static int numxwin, numywin;
static int rseed;
static int move_accepted = 0;
static int in_init = 1;
static int all_init;
static int flush_buffs;
static int num_moves;
static DC current_move = 0, bs;


static CGIDevice cgidev;

static char defcginame[] = ":VDI:";
static char defmetname[] = "ROSE4.MET";

static char *cginame;
static char *metname;

usage()
{
  fprintf(stderr,"usage: bmapper -d -f -b -v cgi_dev -m metafile\n");
  fprintf(stderr,"               -w window_size -g window_gap\n");
  fprintf(stderr,"               -i move_increment -s rand_seed\n");
  fprintf(stderr,"               -p window_priority\n");
  fprintf(stderr,"               -n num_moves_to_exit\n");
  exit(1);
}

void far bmapper_error ()
{
  WORD except;

  Word rgi_except;


  /* Clean up any bitmaps/windows */
  gqdeleteallbitmaps    (cgidev);

  cgi_terminate (cgidev, &rgi_except);

  if (rgi_except != EOK) dqexit((WORD)1);

  dqclose(cgiconn, &except);

  if (except != EOK) dqexit((WORD)1);

  dqdetach(cgiconn, &except);

  if (except != EOK) dqexit((WORD)1);

  dqexit((WORD)1);
}

seemove(
  DC rx,
  DC ry,
  DC rx2,
  DC ry2,
  int wid)
{
if (report_it == 1) 
  printf("Moving window %d from %d,%d to %d,%d\n", wid, rx, ry, rx2, ry2);
}

/* this routine is responsible for updating wind_t to reflect the
   actual window in each 'sector' and for the visual effect of moving
   the window in sector 'a' into the hole at sector 'b'...
 */
slowmove(
  int a,
  int b)
{
   WORD except;
   Word rgi_except;
   WindowId t;
   DC zx, zy, zx1, zy1;

   if (wind_t[b] != 255) {
     fprintf(stderr, "Error in slowmove %d to %d, Not a hole\n",a,b);
   }

   /* set state to indicate that a move was valid */
   move_accepted = 1;

   t = wind_t[a];
  
   /* calculate origin of sector a */
   zx = (a % 4) * bs;
   zy = (a / 4) * bs;

   /* slide a to b - assume it is legal */
   switch (randir) {
   case 0:
     seemove( zx, zy, zx, (DC)(zy+bs), a);
     if (in_init == 0)
       for (zx1 = zx, zy1 = zy; zy1 < (zy + bs); zy1 += rel_move) {
         gqmovewindow(cgidev, t, (DC)0, rel_move);
         if (flush_buffs == 0) cgi_flush(cgidev, &rgi_except);
       }
     else
       gqmovewindow(cgidev, t, (DC)0, bs);
     if (flush_buffs == 0) cgi_flush(cgidev, &rgi_except);
     break;
   case 1:
     seemove( zx, zy, (DC)(zx+bs), zy, a);
     if (in_init == 0)
       for (zx1 = zx, zy1 = zy; zx1 < (zx + bs); zx1 += rel_move) {
         gqmovewindow(cgidev,t, rel_move, (DC)0);
         if (flush_buffs == 0) cgi_flush(cgidev, &rgi_except);
       }
     else
       gqmovewindow(cgidev, t, bs, (DC)0);
     if (flush_buffs == 0) cgi_flush(cgidev, &rgi_except);
     break;
   case 2:
     seemove(zx, zy, (DC)(zx-bs), zy, a);
     if (in_init == 0)
       for (zx1 = zx, zy1 = zy; zx1 > (zx - bs); zx1 -= rel_move) {
         gqmovewindow(cgidev, t, (DC)(-rel_move), (DC)0);
         if (flush_buffs == 0) cgi_flush(cgidev, &rgi_except);
       }
     else
       gqmovewindow(cgidev, t, (DC)(-bs), (DC)0);
     if (flush_buffs == 0) cgi_flush(cgidev, &rgi_except);
     break;
   case 3:
     seemove(zx, zy, zx, (DC)(zy-bs), a);
     if (in_init == 0)
       for (zx1 = zx, zy1 = zy; zy1 > (zy - bs); zy1 -= rel_move) {
         gqmovewindow(cgidev, t, (DC)0, (DC)(-rel_move));
         if (flush_buffs == 0) cgi_flush(cgidev, &rgi_except);
       }
     else
       gqmovewindow(cgidev, t, (DC)0, (DC)(-bs));
     if (flush_buffs == 0) cgi_flush(cgidev, &rgi_except);
     break;
   default:
     fprintf(stderr, "Oops - randir is out of bounds at %d\n",randir);
     break;
   }
   if (flush_buffs == 0) cgi_flush(cgidev, &rgi_except);
   /* swap the entries for next time */
   wind_t[a] = wind_t[b];
   wind_t[b] = t;

   /* see if initialization is done */
   if (in_init != 0) in_init++;
   if (in_init >= 100) in_init = 0; /* init done */
   if (all_init == 1) in_init = 1;  /* reset it again */
}

main(
int   argc,
char  *argv[])
{
  WORD except = 0, actual = 1;

  Word rgi_except;
  DC x,y;
  RGI_Index pr;
  int i,j,k;

  rqsetexceptionhandler(&noexcept, &except);

  if (except != EOK) exit(1);


  dqtrapcc( (HANDLERPTRSTRUCT *) &bmapper_error, &except);
  if (except != EOK) exit(1);



  /* initializations for command line options */
  rseed = 0;
  rel_move = 4;  /* move increment */
  wspace = 0;    /* inter-window gap */
  winsize = 160;
  report_it = 0;  /* less verbose if 0 more if 1 (-d) */
  same_pri = 255;   /* 0 = different priority, n = same priority */
  cginame = defcginame;
  metname = defmetname;
  all_init = 0; /* if 1 then all fast moves */
  flush_buffs = 0;  /* flush each move */
  num_moves = 1000;  /* exit after 1000 moves */

  while ((argc > 1) && (argv[1][0] == '-')) {
    switch (argv[1][1]) {
    case 'd':
      report_it = 1;
      break;
    case 'b':
      flush_buffs = 1;  /* don't flush each move */
      break;
    case 'f':
      all_init = 1;
      break;
    case 'p':
      argv++; argc--;
      if ((argc > 1) && (argv[1][0] != '-')) 
        same_pri = atoi( (const char *)&argv[1][0]);
      else usage();
      break;
    case 's':
      argv++; argc--;
      if ((argc > 1) && (argv[1][0] != '-')) 
        rseed = atoi( (const char *)&argv[1][0]);
      else usage();
      break;
    case 'i':
      argv++; argc--;
      if ((argc > 1) && (argv[1][0] != '-')) 
        rel_move = atoi( (const char *)&argv[1][0]);
      else usage();
      break;
    case 'n':
      argv++; argc--;
      if ((argc > 1) && (argv[1][0] != '-')) 
        num_moves = atoi( (const char *)&argv[1][0]);
      else usage();
      break;
    case 'g':
      argv++; argc--;
      if ((argc > 1) && (argv[1][0] != '-')) 
        wspace = atoi( (const char *)&argv[1][0]);
      else usage();
      break;
    case 'w':
      argv++; argc--;
      if ((argc > 1) && (argv[1][0] != '-')) 
        winsize = atoi( (const char *)&argv[1][0]);
      else usage();
      break;
    case 'm':
      argv++; argc--;
      if ((argc > 1) && (argv[1][0] != '-')) 
        metname = argv[1];
      else usage();
      break;
    case 'v':
      argv++; argc--;
      if ((argc > 1) && (argv[1][0] != '-')) 
        cginame = argv[1];
      else usage();
      break;
    default: 
      fprintf(stderr,"Unrecognized Control: %s\n",argv[1]);
      usage();
      break;
    }
    argv++; argc--;
  }

  bs = winsize + wspace;
  if ((bs * 4) > 640) {
      fprintf(stderr,"size/space to large to fit X dimension.\n");
      usage();
  }
  if ((bs * 3) > 480) {
      fprintf(stderr,"size/space to large to fit Y dimension.\n");
      usage();
  }
  if ((bs % rel_move) != 0) {
      fprintf(stderr,"move must be a divisor of size+space.\n");
      usage();
  }
  /* seed the random number generator */
  srand((unsigned int)rseed);

  /* get the cgi device connection */
  cgiconn = rqscreatefile((STRING *) udistr(ucginame, (const char *)cginame),
                          &except);


  if (except != EOK) {

    fprintf(stderr, "Error %d Creating Output File: %s\n",except,cginame);

    dqexit((WORD)1);


  }

  rqsopen(cgiconn, (BYTE)3, (BYTE)0, &except);


  if (except != EOK) {

    fprintf(stderr, "Error %d Opening Output File: %s\n", except, cginame);

    dqexit((WORD)1);


  }

  cgidev = cgi_initialize(cgiconn, cgibuff, sizeof cgibuff, &rgi_except);


  if (rgi_except != EOK) {

    fprintf(stderr, "Error %d Initializing RGI library: %s\n",
                     rgi_except, cginame);

    dqexit((WORD)1);


  }


  /* get the metafile connection */
  metconn = rqsattachfile((STRING *) udistr(ucginame,  (const char *)metname),
                          &except);


  if (except != EOK) {

    fprintf(stderr, "Error %d Attaching Input File: %s\n",except,metname);

    dqexit((WORD)1);


  }

  rqsopen(metconn, (BYTE)1, (BYTE)2, &except);


  if (except != EOK) {

    fprintf(stderr, "Error %d Opening Input File: %s\n",except,metname);

    dqexit((WORD)1);


  }


  /* Clean up any bitmaps/windows */
  gqdeleteallbitmaps(cgidev);


  /* create a bitmap */
  bmap_t = 0;
  gqcreatebitmap(cgidev, bmap_t, (DC)640, (DC)480, (RGI_Index)4);


  /* create 11 windows of size winsize at spacing winsize + wspace */
  for (y=i=k=0; ((y <= (480 - bs)) && (k < 3)); y += bs, k++) {
    for (x=j=0; ((x <= (640 - bs)) && (j < 4)); x += bs, i++, j++) {
     if (i < 11) {
      if (same_pri == 0) pr = i + 1;
      else pr = same_pri;
      save_t[i] = wind_t[i] = i;
      gqcreatewindow(cgidev, wind_t[i], bmap_t, pr, (RGI_Index)VISIBLE,
                         x, y, x, y, winsize, winsize);
     }
    }
  }

  /* Select the bitmap for drawing */
  gqselectdrawingbitmap(cgidev, bmap_t);
  cgi_flush(cgidev, &rgi_except);

  /* copy the metafile to the bitmap using the EIOS directly */
  actual = 1;
  while (actual != 0) {
    actual = rqsreadmove(metconn, (BYTE *)metbuff, (NATIVE_WORD)sizeof metbuff,
                         &except);
    if (except != EOK) {

      fprintf(stderr, "Error %d Reading Input File: %s\n",except,metname);

      dqexit((WORD)1);


    }

    if (actual != 0) {
      actual = rqswritemove(cgiconn, (BYTE *)metbuff, (NATIVE_WORD)actual,
                            &except);
      if (except != EOK) {

        fprintf(stderr, "Error %d Writing Output File: %s\n",except,cginame);

        dqexit((WORD)1);


      }

    }


  }

  /* get rid of the metafile connection */
  rqsclose(metconn, &except);


  if (except != EOK) {

    fprintf(stderr, "Error %d Closing Input File: %s\n",except,metname);

    dqexit((WORD)1);


  }

  rqsdeleteconnection(metconn, &except);


  if (except != EOK) {

    fprintf(stderr, "Error %d Detaching Input File: %s\n",except,metname);

    dqexit((WORD)1);


  }


  /* now - randomly move a neighbor of the 'hole' into the
           hole forever...

           holes are numbered:

             0  1  2  3
             4  5  6  7
             8  9 10 11

           hole starts at 11.
           diagonal moves aren't allowed.
           9/6 - added - can't move same tile twice...
                this means that if the old direction was 0 then
                the new direction can't be 3 and vice-versa - same
                holds for 1 and 2.
           directions are:

                0   
             1     2
                3   

           the following switch is the state graph...
   */

   x = 11;
   save_t[11] = wind_t[11] = 255;  /* mark the hole */
   randir = (rand() & 0x06000) >> 13;  /* pick a new direction */
   /* round randomly - should test bit 13 of previous result... */
   if ((rand() & 0x01) != 0) randir = (randir + 1) % 4;
   olddir = 255;  /* invalid */
   y = 0;  /* done flag */
   while (y == 0) {
     if (report_it == 1) printf("Hole is at: %d, Direction %d\n",x,randir);

     /* set state to indicate that the move was not accepted */
     move_accepted = 0;

     switch(x) {
     case 0:
       switch(randir) {
       case 0:
         break;
       case 1:
         break;
       case 2:
         slowmove(1,0); x = 1;
         break;
       case 3:
         slowmove(4,0); x = 4;
         break;
       }
       break;
     case 1:
       switch(randir) {
       case 0:
         break;
       case 1:
         slowmove(0,1); x = 0;
         break;
       case 2:
         slowmove(2,1); x = 2;
         break;
       case 3:
         slowmove(5,1); x = 5;
         break;
       }
       break;
     case 2:
       switch(randir) {
       case 0:
         break;
       case 1:
         slowmove(1,2); x = 1;
         break;
       case 2:
         slowmove(3,2); x = 3;
         break;
       case 3:
         slowmove(6,2); x = 6;
         break;
       }
       break;
     case 3:
       switch(randir) {
       case 0:
         break;
       case 1:
         slowmove(2,3); x = 2;
         break;
       case 2:
         break;
       case 3:
         slowmove(7,3); x = 7;
         break;
       }
       break;
     case 4:
       switch(randir) {
       case 0:
         slowmove(0,4); x = 0;
         break;
       case 1:
         break;
       case 2:
         slowmove(5,4); x = 5;
         break;
       case 3:
         slowmove(8,4); x = 8;
         break;
       }
       break;
     case 5:
       switch(randir) {
       case 0:
         slowmove(1,5); x = 1;
         break;
       case 1:
         slowmove(4,5); x = 4;
         break;
       case 2:
         slowmove(6,5); x = 6;
         break;
       case 3:
         slowmove(9,5); x = 9;
         break;
       }
       break;
     case 6:
       switch(randir) {
       case 0:
         slowmove(2,6); x = 2;
         break;
       case 1:
         slowmove(5,6); x = 5;
         break;
       case 2:
         slowmove(7,6); x = 7;
         break;
       case 3:
         slowmove(10,6); x = 10;
         break;
       }
       break;
     case 7:
       switch(randir) {
       case 0:
         slowmove(3,7); x = 3;
         break;
       case 1:
         slowmove(6,7); x = 6;
         break;
       case 2:
         break;
       case 3:
         slowmove(11,7); x = 11;
         break;
       }
       break;
     case 8:
       switch(randir) {
       case 0:
         slowmove(4,8); x = 4;
         break;
       case 1:
         break;
       case 2:
         slowmove(9,8); x = 9;
         break;
       case 3:
         break;
       }
       break;
     case 9:
       switch(randir) {
       case 0:
         slowmove(5,9); x = 5;
         break;
       case 1:
         slowmove(8,9); x = 8;
         break;
       case 2:
         slowmove(10,9); x = 10;
         break;
       case 3:
         break;
       }
       break;
     case 10:
       switch(randir) {
       case 0:
         slowmove(6,10); x = 6;
         break;
       case 1:
         slowmove(9,10); x = 9;
         break;
       case 2:
         slowmove(11,10); x = 11;
         break;
       case 3:
         break;
       }
       break;
     case 11:
       switch(randir) {
       case 0:
         slowmove(7,11); x = 7;
         break;
       case 1:
         slowmove(10,11); x = 10;
         break;
       case 2:
         break;
       case 3:
         break;
       }
       break;
     default:
       fprintf(stderr, "Oops - Hole switch is out of bounds at %d\n",x);
       break;
    }

    /* pick next direction - impose rule stating no reversal of moves */
    /* note that if an invalid direction is picked we won't know it
       here since the state machine detects/ignores it.
       to fix this we need a state variable saying that the machine
       accepted a particular move as valid.
       we can then test this variable before resetting olddir
    */
    if (move_accepted == 1) {
      olddir = randir;
      if (num_moves != 0) {  /* test for "run forever condition" */
        current_move++;
        if (current_move > num_moves) exit(0);
      }
    }
    y = 1;
    while (y == 1) {
      randir = (rand() & 0x06000) >> 13;  /* pick a new direction */
      /* round randomly - should test bit 13 of previous result... */
      if ((rand() & 0x01) != 0) randir = (randir + 1) % 4;
      switch (olddir) {
      case 0:
        if (randir != 3) y = 0;
        break;
      case 1:
        if (randir != 2) y = 0;
        break;
      case 2:
        if (randir != 1) y = 0;
        break;
      case 3:
        if (randir != 0) y = 0;
        break;
      default: /* olddir was not yet set - proceed */
        y = 0;
        break;
      }
    }

    /* test for game end */
    /* if the window id's in wind_t match those in save_t then we are
       back to the original arrangement...
     */
    y = 1;  /* assume we are done */
    for (i = 0; (i < 12) && (y == 1); i++) {
      if (wind_t[i] != save_t[i]) y = 0;  /* not done */
    }
  }

  /* get rid of the cgi connection */  
  cgi_terminate(cgidev, &rgi_except);


  if (rgi_except != EOK) dqexit((WORD)1);


  rqsclose(cgiconn, &except);


  if (except != EOK) dqexit((WORD)1);


  rqsdeleteconnection(cgiconn, &except);


  if (except != EOK) dqexit((WORD)1);



  /* return */
}
