/*
 * Vidtex version 1.0
 * by George Musser Jr.
 * CIS 76566,3714
 * 25 May 1986
 *
 * This program displays a medium or high resolution Vidtex picture
 * downloaded from CompuServe.  The program tries to scale the picture
 * to fit the current window size; the smaller the window, the more
 * information the algorithm must discard to squeeze the picture.
 *
 * CLI syntax: VIDTEX <file>
 * Workbench:  Select both picture file and VIDTEX
 *
 */

#include <exec/types.h>
#include <intuition/intuition.h>
#include <workbench/startup.h>
#include <stdio.h>

/* ===== Vidtex definitions ============================================= */

#define  MED_X        128     /* Picture elements in medium resolution */
#define  MED_Y         96
#define  HI_X         256     /* Picture elements in high resolution */
#define  HI_Y         192
#define  ZERO         ' '     /* Vidtex code for 0 pixels */
#define  SEQLEN         3     /* Length of escape sequences */
#define  MED_RES   "\x1BGM"   /* Sequence for medium resolution */
#define  HI_RES    "\x1BGH"   /* Sequence for high resolution */
#define  ESC         0x1B     /* Escape character */

/* ===== Window definitions ============================================= */

#define  MIN_WIDTH          50      /* Smallest window size */
#define  MIN_HEIGHT         25
#define  MAX_WIDTH         640      /* Largest window size */
#define  MAX_HEIGHT        200
#define  HI_WIDTH       MAX_WIDTH   /* Initial window size */
#define  MED_WIDTH         375      /* 2.5*(MED_X+BorderLeft+BorderRight) */
#define  HI_HEIGHT     MAX_HEIGHT
#define  MED_HEIGHT        109      /* MED_Y+BorderTop+BorderBottom */
#define  BACK                0      /* Background color */
#define  FORE                1      /* Foreground color */
#define  ERRCODE            20      /* Return code in case of trouble */

/* ----- The generic OpenLibrary() call ----- */

#define  OPENLIB(p,l)  (struct p *)OpenLibrary(l,LIBRARY_VERSION)

/* ===== Global variables =============================================== */

struct NewWindow NewWindow =        /* Our output window */
   {
   0,0,                             /* Upper left corner */
   HI_WIDTH,HI_HEIGHT,              /* Width and height */
   -1,-1,                           /* Use Wbench's detail and block pens */
   CLOSEWINDOW | NEWSIZE,           /* Ye olde IDCMP */
   WINDOWDEPTH | WINDOWCLOSE        /* Depth, close... */
      | WINDOWDRAG | WINDOWSIZING   /* ...drag, and sizing gadgets */
      | SMART_REFRESH,              /* Let Intuition handle refreshing */
   NULL,                            /* No gadgets */
   NULL,                            /* Or images */
   "",                              /* We'll get the title later */
   NULL,                            /* Use Workbench screen */
   NULL,                            /* No bitmap */
   MIN_WIDTH,MIN_HEIGHT,            /* Smallest size */
   MAX_WIDTH,0,                     /* Largest size */
   WBENCHSCREEN                     /* Standard screen */
   };

/* N.B. You can't scale vertically; I couldn't get that to work well */

/* ----- These guys are used in main() and Abort() ----- */

LONG olddir = NULL;                           /* Lock on former directory */
FILE *Fp = NULL;                              /* File pointer */
struct IntuitionBase *IntuitionBase = NULL;   /* Intuition pointer */
struct GfxBase *GfxBase = NULL;               /* Graphics pointer */
struct Window *Window = NULL;                 /* Pointer to our window */

/* ----- These make the program easier to read ----- */

#define  ARG     WBenchMsg->sm_ArgList

#define  RPORT   Window->RPort         /* Window's RastPort */
#define  UPORT   Window->UserPort      /* Here's the user message port */
#define  LEFT    Window->BorderLeft    /* Offsets into window area */
#define  TOP     Window->BorderTop
#define  RIGHT   (Window->Width - Window->BorderRight - 1)
#define  BOTTOM  (Window->Height - Window->BorderBottom)
#define  WIDTH   (RIGHT - LEFT)
#define  HEIGHT  (BOTTOM - TOP)

/* ===== main() ======================================================== */

main(argc,argv)
int argc;
char *argv[];
{
   void Abort();                       /* Error handler */
   struct IntuiMessage *GetMsg();      /* Message retrieval */

   /* ===== Local variables ============================================= */

   extern struct WBStartup *WBenchMsg; /* Startup message from Workbench */
   char *name;                         /* Name of file to open */
   register char Buffer[SEQLEN];       /* Input buffer when scanning */
   register int Max_x = HI_X;          /* Width (for wrap-around) */
   register int Max_y = HI_Y;          /* Height (for bounds checking) */
   long Pos;                           /* File position of picture */
   register int color;                 /* Current color */
   float xFactor,yFactor;              /* Scaling factors for picture */
   float x,y;                          /* Position in picture */
   register char ch;                   /* Single character from file */
   register int New_y,Old_y;           /* Coordinates in window */

   register struct IntuiMessage *message;   /* IDCMP message */
   ULONG class;                             /* Message class */
                                                         
   /* ===== Open up file ================================================ */

   if (argc == 0)             /* Looks like we were called from Workbench */
      if (WBenchMsg->sm_NumArgs > 1)        /* Did user select a project? */
         if ((++ARG)->wa_Lock) {                  /* Yup, but is lock OK? */
            olddir = CurrentDir(ARG->wa_Lock);    /* Lock current dir */
            name = (ARG--)->wa_Name;              /* Get filename */
            }                       /* (++ and -- select second argument) */
         else
            Abort ("Can't lock directory");
      else
         Abort ("Please select a project");
   else if (argc == 1)                      /* Oops, user forgot filename */
      Abort ("Please specify a filename");
   else                                                 /* Got a filename */
      name = argv[1];

   if ((Fp = fopen(name,"r")) == NULL)                /* Try to open file */
      Abort ("Can't open file");

   /* ===== Search for picture ========================================== */

   strncpy (Buffer,"   ",SEQLEN);
   while (strncmp(Buffer,MED_RES,SEQLEN) && strncmp(Buffer,HI_RES,SEQLEN)
          && NOT feof(Fp)) {
      Buffer[0] = Buffer[1];        /* Buffer holds last three characters */
      Buffer[1] = Buffer[2];                      /* Shift buffer down... */
      Buffer[2] = getc(Fp);        /* ...so we can grab another character */
      }

   if (feof(Fp))                               /* File is worthless */
      Abort ("Can't find start of picture");

   Pos = ftell(Fp);                            /* Save position */

   /* ===== Open up libraries =========================================== */

   if ((IntuitionBase = OPENLIB(IntuitionBase,"intuition.library")) == NULL)
      Abort ("Can't open Intuition Library");

   if ((GfxBase = OPENLIB(GfxBase,"graphics.library")) == NULL)
      Abort ("Can't open Graphics Library");

   /* ===== Open up window ============================================== */

   if (NOT strncmp(Buffer,MED_RES,SEQLEN)) {        /* Medium resolution? */
      NewWindow.Width = MED_WIDTH;           /* If so, adjust window size */
      NewWindow.Height = MED_HEIGHT;
      Max_x = MED_X;                               /* ...and picture size */
      Max_y = MED_Y;
      }

   NewWindow.Title = argv[1];             /* Set window title to filename */

   /* Now it's time to try opening the window */
   if ((Window = (struct Window *)OpenWindow(&NewWindow)) == NULL)
      Abort ("Can't open window");

   /* ===== Get ready to show picture =================================== */

   do {                          /* We may have to redraw this */

      if (fseek(Fp,Pos,0))       /* Go to start of picture */
         Abort ("Fseek woes");

      xFactor = (float)WIDTH / Max_x;      /* Scale to fit  */
      yFactor = (float)HEIGHT / Max_y;     /* window snugly */

      color = FORE;        /* This flips to BACK initially */
      x = (float)LEFT;
      y = (float)TOP;
      New_y = TOP;

      SetAPen (RPORT,BACK);                       /* Clear window */
      RectFill (RPORT,LEFT,TOP,RIGHT,BOTTOM);

      Move (RPORT,LEFT,TOP);                /* Start in upper left corner */

      /* ===== Go for it ================================================ */

      while (NOT feof(Fp) && (message = GetMsg(UPORT)) == NULL) {

         SetAPen (RPORT,(color = (color == FORE) ? BACK : FORE));

         /* This do-while loop gathers all the consecutive segments
          * for the current color.
          */

         do {

            /* Specs say we've got to watch out for cursor movement */

            while ((ch = getc(Fp)) == ESC)   /* Cursor movement? */
               switch (getc(Fp)) {           /* Yup... */
                  case 'D' :                 /* Left */
                     x -= xFactor;
                     if (x >= LEFT) break;
                     x += WIDTH;
                  case 'A' :                 /* Up */
                     y -= yFactor;
                     if (y < TOP) y += HEIGHT;
                     break;
                  case 'C' :                 /* Right */
                     x += xFactor;
                     if (x <= RIGHT) break;
                     x -= WIDTH;
                  case 'B' :                 /* Down */
                     y += yFactor;
                     if (y > BOTTOM) y -= HEIGHT;
                     break;
                  case 'H' :                 /* Home */
                     x = (float)LEFT;
                     y = (float)TOP;
                     break;
                  case 'Y' :                 /* <lin><col> positioning */

                     /* Control characters are invalid */
                     if ((ch = getc(Fp)) < ZERO) ch = ZERO;

                     /* Assume line 1 corresponds to y=0 */
                     y = (float)(TOP + ch - ZERO);

                     if ((ch = getc(Fp)) < ZERO) ch = ZERO;
                     x = (float)(LEFT + getc(Fp) - ZERO);
                     break;
                  }

            if (ch < ZERO) {
               ch = NULL;     /* We don't recognize this character... */
               break;         /* ...so give it up */
               }

            /* Not an escape sequence, so assume it's a run-length code */

            else if ((x += (ch - ZERO)*xFactor) > RIGHT) {
               y += yFactor;
               x -= WIDTH;
               }

            } while ((ch = getc(Fp)) == ZERO);

         if (ch == NULL)         /* Don't understand this one... */
            break;               /* ...so we're done */
         else {
            ungetc (ch,Fp);      /* Last character belongs to next color */
            Old_y = New_y;       /* Previous position in window */
            New_y = Round(y);    /* New position */
            }

         /* Don't bother drawing if we're going to be overwritten */

         if (New_y == Round(y + yFactor))  
            Move (RPORT,Round(x),New_y);   /* Move pen along */

         else {

            if (New_y > Old_y) {           /* More than one line to draw? */

               Draw (RPORT,RIGHT,Old_y);  /* Yep, so finish out this line */
       
               if (New_y > BOTTOM) {   /* ...finish screen if appropriate */
                  RectFill (RPORT,LEFT,Old_y + 1,RIGHT,BOTTOM);
                  break;
                  }

               if (New_y - Old_y > 1)        /* ...fill intermediate area */
                  RectFill (RPORT,LEFT,Old_y + 1,RIGHT,New_y - 1);

               Move (RPORT,LEFT,New_y);    /* ...and move to the new line */

               }

            Draw (RPORT,Round(x),New_y);     /* Draw line to new position */

            }

         if (Chk_Abort()) Abort ("");         /* Abort if user so chooses */

         }   /* while */

      /* When you size the window and then leave the mouse perfectly still,
       * Wait() and GetMsg() return with message == NULL.  So I put the
       * two statements into a while loop.
       */

      while (message == NULL) {          /* Do we have a message? */
         Wait (1 << UPORT->mp_SigBit);   /* No, so sleep 'til we get one */
         message = GetMsg(UPORT);
         }

      class = message->Class;    /* What sort of message? */
      ReplyMsg (message);        /* Reply */

      } while (class != CLOSEWINDOW);    /* Continue until done */
   
   Abort ("");                           /* We're outta here */

}


/* ===== Round() ========================================================
 *
 * Round() rounds a positive float to the nearest whole number.  Could be
 * a macro, but that would involve four calculations of the expression.
 *
 */

int Round(x)
float x;
{
   return ( ((x) - (int)(x) < 0.5)  ?  (int)(x)  :  (int)(x) + 1 );
}


/* ===== Abort() ========================================================
 *
 * Abort() closes resources and returns the appropriate error code.
 *
 */

void Abort (Msg)
char *Msg;         /* Error message */
{
   if (olddir) CurrentDir (olddir);
   if (Fp) fclose (Fp);
   if (IntuitionBase) CloseLibrary (IntuitionBase);
   if (GfxBase) CloseLibrary (GfxBase);
   if (Window) CloseWindow (Window);
   if (stclen(Msg)) {
      fprintf (stderr,"%s.\n",Msg);
      exit (ERRCODE);
      }
   else exit (0);
}

