/***************************************************************************
 *
 * first_app.c
 *
 *  This program is an prototype application for iRMX II/III written for
 *  use with iC n86 using the iSBX 279A interface library.  It exhibits
 *  proper device initialization and termination, and provides a
 *  first program from which to build a graphics application.
 *  
 *  A single output stream to a metafile device is utilized.  This is the
 *  simplest graphics application one can write.  Input handling and use
 *  of multiple devices or I/O streams is not shown.  Deletion of default
 *  (or pre-existing) bitmaps is shown, and creation of a new bitmap and
 *  window is shown.
 */

#pragma extend

/*
 * The standard iC include files are first
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
/*
 * The UDI library includes
 */
#include <udi.h>
/*
 * The iRMX library includes
 */
#include <rmxc.h>
#include <rmxerr.h>
/*
 * The iRGI library includes
 */
#include <rgi.h>

/*
 * The default name of the graphics device/metafile.
 */
static char *defcginame = ":VDI:";
/*
 * The 'real' name in case the user specifies a non-default name.
 */
static char *cginame;
/*
 * Static data area for pathname conversion from null-terminated string
 * to counted string for use by iRMX II.
 */
static char ucginame[256];
/*
 * A token for the device/file connection from iRMX II.  This will be
 * passed to the buffered O/S layer of RGI for use in subsequent I/O
 * operations.
 */
static TOKEN cgiconn;
/*
 * The 'I/O stream identifier' returned by cgi_initialize and required in
 * all subsequent RGI calls to identify the I/O stream in use.  This
 * may be thought of as a 'file descriptor' created by cgi_initialize.
 * Up to 10 concurrent I/O streams may be in use in a single application
 * (or instance of the RGI library data segment for those who delve
 * deeply into PL/M models and like unusual invocations of BND286).
 * The cgi_initialize call is currently not ROM-able, due to use of a
 * single 'INITIAL' statement to detect first entry and clear the data
 * area.
 */
static CGIDevice cgidev;
/*
 * A buffer for use by the buffered I/O layer of iRGI.  Any buffer size may
 * be used, however 64K segment limits restrict the maximum size and 0 is
 * of course the minimum.  When a buffer size of zero is used, every call
 * to the RGI interface results in a 'write' call.  This is usually
 * undesirable.  An average packet is about 4 bytes for 'primitive attributes'
 * so a buffer of just 64 bytes will greatly improve through-put.  If large
 * polygons or other lists are routinely drawn, then the buffer size depends
 * upon the average size of these lists.  In general, when memory usage is
 * not critical, buffers up to 32K should be used.  Of course, when you
 * examine the SUBMIT file that builds this program you will see that the
 * same argument is made for stack size.
 *
 * The application should never access this buffer.  It is 'given away'
 * to RGI to allow the application program to control the resource usage
 * of the interface library.
 */
static char cgibuff[8192];
/*
 * Nearly all applications that run on top of the HI should use in-line
 * exception handling.  Some interface libraries assume that exceptions
 * will be returned in-line for proper operation.  RGI assumes
 * internally assume in-line exception handling in this release.
 * Our example code will check exceptions so the reader becomes aware
 * of the most likely error conditions and simple mechanisms for handling
 * these errors.
 */
static EXCEPTIONSTRUCT noexcept = {0,0,0};

/*
 * The usual 'usage' message 
 */
void usage( void )
{
  fprintf(stderr,
     "usage: first_app -c cgi_dev -x x_size -y y_size -n num_color\n");
  fprintf(stderr,
     "   if -c is not specified then :VDI: is assumed.\n");
  fprintf(stderr,
     "   if -x is not specified then 320 is used.\n");
  fprintf(stderr,
     "   if -y is not specified then 240 is used.\n");
  fprintf(stderr,
     "   if -n is not specified then 16 is used.\n");
  exit(1);
}

/*
 * Some text strings we will display in a window.
 */
static char *title = "Available Line Styles";

static char *line_styles[] = {
  "SOLID_LINE (0)",
  "DASHED_LINE (1)",
  "DOTTED_LINE (2)",
  "DOTTED_DASHED_LINE (3)"
};

/*
 * At Last!  main()
 */
main(
int   argc,
char *argv[])
{
  WORD except = EOK; 
  Word rgi_except = EOK; 
  int i,k;
  int XR = 320, YR = 240, N = 16;
  RGI_Index bits;
  char *p;

  /*
   * Step 1.  Set up the exception mode so errors are returned inline.
   */ 
  rqsetexceptionhandler(&noexcept,&except);

  /* 
   * Step 2.  (Optional) Parse the command line in typical 'C' fashion.
   * Some applications may have no command-line options.
   */
  cginame = defcginame;
  while ((argc > 1) && (argv[1][0] == '-')) {
    switch (argv[1][1]) {
    case 'c':
      argv++; argc--;
      if ((argc > 1) && (argv[1][0] != '-')) 
        cginame = argv[1];
      else usage();
      break;
    case 'x':
      argv++; argc--;
      if ((argc > 1) && (argv[1][0] != '-')) 
        XR = atoi( (const char *)&argv[1][0]);
      else usage();
      if ((XR > 640) || (XR < 0)) {
        fprintf(stderr,"X-size must be positive and cannot exceed 640.\n");
        exit(1);
      }
      break;
    case 'y':
      argv++; argc--;
      if ((argc > 1) && (argv[1][0] != '-')) 
        YR = atoi( (const char *)&argv[1][0]);
      else usage();
      if ((YR > 480) || (YR < 0)) {
        fprintf(stderr,"Y-size must be positive and cannot exceed 480.\n");
        exit(1);
      }
      break;
    case 'n':
      argv++; argc--;
      if ((argc > 1) && (argv[1][0] != '-')) 
        N = atoi( (const char *)&argv[1][0]);
      else usage();
      if ((N != 2) && (N != 4) && (N != 16) && (N != 256)) {
        fprintf(stderr,"Number of Colors must be 2, 4, 16, or 256.\n");
        exit(1);
      }
      break;
    default: 
      fprintf(stderr,"Unrecognized Control: %s\n",argv[1]);
      usage();
      break;
    }
    argv++; argc--;
  }
  if ((argc > 1) && (argv[1][0] != '-')) usage();

  /* 
   * Step 3.  Obtain access to the graphics device or metafile.
   * We use 'attach' here to allow 'appending' to a disk resident
   * file.  Multiple applications can then 'build' a picture.
   * Note that a collection of trivial programs can use this strategy
   * to implement tools that together can be very powerful.  The first
   * one you will want to write is one that deletes all existing resources
   * and restores the graphics interpreter to a known state (single bitmap,
   * single window, known bit depth, known color table, etc).
   * Otherwise, just detach the device and re-attach it to restore the
   * configuration parameters.
   */
  cgiconn = rqsattachfile((STRING *)udistr(ucginame,(const char *)cginame),
                          &except);
  if (except != EOK) {
    fprintf(stderr, "Error %d Attaching Output File: %s\n",except,cginame);
    exit(1);
  }
  /*
   * Step 4.  Open for read and write with no buffers.  If this were
   * a disk file then EIOS buffers could be used, but use of buffering
   * in the iRGI interface provides the majority of any performance boost
   * while no EIOS buffering is needed when output is sent to the screen (the
   * physical file driver and TSC are used in that case).
   */
  rqsopen(cgiconn, (BYTE)3, (BYTE)0, &except);
  if (except != EOK) {
    fprintf(stderr, "Error %d Opening Output File: %s\n",except,cginame);
    exit(1);
  }
  /*
   * Step 5.  (Optional) This step allows appending to the metafile.
   * Programs that do not expect to 'cooperatively' build pictures with
   * other programs may simply create the output file in step 2 and
   * not 'seek' in step 5.
   *
   * Since the TSC does not allow a 'terminal' to 'seek', we ignore the
   * E$IDDR error expected here.
   */
  rqsseek(cgiconn, (BYTE)4, (DWORD) 0, &except);
  except = EOK;  /* ignore any errors... */
  /*
   * Step 6.  Initialize the 'Buffered O/S Interface' layer of RGI.
   * This step is required for all graphics applications.  The identifier
   * returned is use in all subsequent calls.  The identifier is similar
   * to a file descriptor and should never be modified by the program.
   */
  cgidev = cgi_initialize(cgiconn,cgibuff,sizeof cgibuff,&rgi_except);
  if (rgi_except != EOK) {
    fprintf(stderr, "Error %d Initializing CGI library: %s\n",except,cginame);
    exit(1);
  }

  /*
   * Figure out the requested bit depth from the requested number of
   * colors.
   */
  bits = (int)(log((double)N)/log((double)2.0));

  /*
   * Step 7.  (Optional)  Get rid of any other resources left by other
   * applications.
   *
   * Note that applications intending to 'append' to a picture would not
   * perform this step.  This application will remove all prior drawings
   * and start over each time it is invoked.
   *
   * Both styles of usage are shown in this program to provide examples and
   * ideas to the application programmer.
   */
  gqdeleteallbitmaps(cgidev);
  gqdeleteallwindows(cgidev);
  /*
   * Step 8.  (Optional)  Allocate new resources.  Once the pre-existing
   * bitmaps and windows are destroyed, the application must create new
   * resources (or just exit if the program simply gets rid of stuff on
   * the device).
   *
   * Note that applications that 'append' to a picture can also skip step
   * 7 and still perform step 8 - the intent may be to incrementally build
   * a picture composed of multiple bitmaps and/or several windows.
   */
  gqcreatebitmap(cgidev, (BitmapId)0, (DC)XR, (DC)YR, bits);

  /*
   * The remainder of this program is application specific, so we can
   * regard it all as Step 9.
   *
   * When drawing in a bitmap, it must first be selected.
   */ 
  gqselectdrawingbitmap(cgidev, (BitmapId)0);
  /*
   * Prior to using 'coordinates', the Virtual Device Coordinate Space
   * must be defined by specifying the lower left and upper right corners.
   * Also, the device viewport must be set to determine the mapping to
   * device coordinate space by specifying the lower left and upper right
   * corners.
   */
  gqsetvdcextent(cgidev, (VDC)0, (VDC)0, (VDC)(XR-1), (VDC)(YR-1));
  gqsetdeviceviewport(cgidev, (DC)0, (DC)0, (DC)(XR-1), (DC)(YR-1));
  /*
   * BEGINPICTURE is not required, but is good form.  Also, the background
   * color can be set here.
   */
  gqbeginpicture(cgidev, (void *)"", (ColorIndex)BLACK);
  /*
   * Bitmaps are initialized to zero when they are created so both BEGIN
   * PICTURE and CLEARVIEWSURFACE are optional in this context, however
   * programs that use pre-existing resources should initialize the
   * graphics device as needed.
   * Using both BEGIN PICTURE and CLEAR VIEW SURFACE is redundant and will
   * cause the bitmap to be cleared twice.  This will be visible if the
   * window is visible.
   */
  gqclearviewsurface(cgidev);
  /*
   * Primitive text attributes are initialized here.  We use the Stroke
   * font at index 0 and set the character height to allow 26 lines on
   * the device.  Alignment is LEFT and TOP, while path is RIGHT.
   */
  gqsettextprecision   (cgidev, (RGI_Index)STROKE_PRECISION);
  gqsettextalignment   (cgidev, (RGI_Index)ALIGN_LEFT, (RGI_Index)ALIGN_TOP,
                                (Real)0.0, (Real)0.0);
  gqsetcharacterpath   (cgidev, (RGI_Index)CHARACTER_RIGHT);
  gqsettextfontindex   (cgidev, (RGI_Index)0);
  gqsetcharacterspacing (cgidev, (Real)0.25);
  gqsetcharacterheight (cgidev, (VDC)(YR/26));
  /*
   * Sub-Titles for each of four lines (one in each style) are now drawn.
   */
  gqsettextcolor       (cgidev, (ColorIndex)GREEN);
  for (k = 0; k < 4; k++) {  /* line style */
    p = line_styles[k];
    gqtext (cgidev, (VDC)(XR/4), (VDC)((k+1)*YR/6), (RGI_Index)0,
                    (Integer)strlen((const char *)p), (void *)p);
  }
  /*
   * The Title is added to the picture.
   */
  gqsettextcolor  (cgidev, (ColorIndex)CYAN);
  p = title;
  gqtext (cgidev, (VDC)(XR/4), (VDC)(5*YR/6), (RGI_Index)0,
                  (Integer)strlen((const char *)p), (void *)p);
 
  /*
   * A window is created to allow the user to view the bitmap.
   *
   * Note that the window may be created at any time after the bitmap
   * is created, however drawing and then creating a window is the
   * preferred method since users need not know how a picture is constructed
   * and perceived drawing time is reduced if the user never sees individual
   * drawing primitives - just completed pictures.
   *
   * In addition, this application demonstrates the concept of 'caching'
   * often used pictures and creating windows to view them as necessary.
   * Static 'help' screens, 'menus' and other selection screens are all
   * good candidates.  Pictures that are primarily static (graphs and
   * other data presentation displays) can cache a portion of the bitmap
   * (the 'background'), and use a raster-op to copy the static portion
   * into a new bitmap, add the dynamic information, and then create
   * a window to view the combined picture.  Finally, a miriad of visual
   * effects may be achieved by dynamically manipulating multiple windows
   * on the same or different bitmaps and by modifying the color table.
   * These examples are beyond the scope of this simple application.
   *
   * In this application, the user will see four lines drawn in different
   * styles and text will describe the line style.  Each line is redrawn
   * in each of the available colors (depends on bit depth).  This provides
   * a simple visual effect to draw attention to the lines.  The window is
   * created prior to the use of this effect in order to allow the effect
   * to be seen by the user.
   *
   * The created window is 'on top' at priority 255, is visible, has
   * borders, and displays the entire bitmap in the upper right corner
   * of the screen.  Note the conversion from bitmap to screen coordinates
   * by inverting the Y coordinate. 
   */ 
  gqcreatewindow(cgidev, (WindowId)0, (BitmapId)0, (RGI_Index)255,
                         (RGI_Index)(VISIBLE | BORDER_VISI),
                         (DC)0, (DC)0, (DC)(640-XR), (DC)0, (DC)XR, (DC)YR);

  /*
   * now we will draw some lines
   */
  for (k = 0; k < 4; k++) {  /* line style */
    gqsetlinetype(cgidev, (RGI_Index)k);
    for (i = 0; i < N; i++) {  /* line color */
      gqsetlinecolor(cgidev, (ColorIndex)i);
      gqline(cgidev, (VDC)(XR/4),   (VDC)((k+1)*YR/6),
                     (VDC)(3*XR/4), (VDC)((k+1)*YR/6));
    }
  }

  /*
   * Step 10.  Terminate Properly.  The RGI interface library
   * is called to free internal resources allocated by cgi_initialize.
   */
  cgi_terminate(cgidev,&rgi_except);
  if (rgi_except != EOK) exit(1);
  /*
   * Step 11.  Close the connection and delete it.
   */
  rqsclose(cgiconn,&except);
  if (except != EOK) exit(1);
  rqsdeleteconnection(cgiconn, &except);
  if (except != EOK) exit(1);

  /*
   * As a final note, the application may use UDI calls to obtain connections
   * for use by RGI.  iRMX II EIOS calls are shown only as an example.
   *
   * That's the end of the program.
   */
  /*  return or exit(0) */
}
