//   main.cpp
//
//   main.cpp is the main program code for the file demonstration system.
//   The primary function it performs is to read command lines from the
//   user input file and direct those commands to the proper member function
//   of the proper access method class for performance.
//
//   The program also is responsible for all required initialization functions
//   for graphics setup and setup of all internal file structure entities.
//   Also contained in main.cpp are the function definitions both for functions
//   used by the access method classes and for the functions used by the main
//   program itself.


#include <iostream.h>
#include <fstream.h>
#include <dos.h>
#include "filedemo.h"
#include "sequent.h"
#include "relative.h"
#include "direct.h"
// #include "isam.h"          to be added as developed

int height;                        //  height of each block location in pixels
int column1, column2, column3;     // column locations
int row1, row2, row3;              // row locations

int full_scr_x, full_scr_y;        // maximum x,y screen coordinates
int press;                         // cycle method; keypress or timed delay
int msdelay;                       // delay time for timed delay
char cmd_msg [MAXLINE];            // command currently executing
char user_msg [MAXLINE];           // messages to user during execution steps


// declaration of all internal file system structures

iobcb iobcb1 ("IO BUFFER CONTROL BLK");
iobuf iobuf1 ("IO BUFFER");
dct dct1 ("DEVICE CONTROL TBL");
iorq iorq1 ("IO REQUEST QUEUE");
uicb uicb1 ("USER INTRFC CONTROL BLK");
userbuf userbuf1 ("USER DATA BUFFER");
kdb kdb1 ("KEY DESCRIPTION BLK");
filecb filecb1 ("FILE CONTROL BLK");
iorequest iorequest1 ("IO REQUEST");


// declaration of all internal file system pointers

pointer iobuf_ptr;
pointer iorq_ptr;
pointer userbuf_ptr;
pointer kdb_ptr;
pointer filecb_ptr;
pointer dct_ptr;
pointer iobcb_ptr;
pointer iorequest_ptr;


// function prototypes

int get_line (ifstream* fp, char* bufr);
int get_cmd (char** index, char* cmd);
int get_param (char** index, char* field);
int get_lastparam (char** index, char* field);
void cmd_err (void);
void param_err (char* cmd);





int main(int argc, char* argv[])
   {

   if (argc != 2)    // check to see if filename is supplied
      {
      cout << "\nUsage: fd filename\n";
      exit (1);
      }

   ifstream infile;         // create file
   infile.open (argv [1]);  // open file
   if (!infile)             // file can not be opened
      {
      cout << "\nCan't open " << argv [1];
      exit (1);
      }



   // Print opening message
   clrscr ();
   cout << "\n\n                      WELCOME TO THE HOOD COLLEGE\n ";
   cout << "                 FILE STRUCTURE DEMONSTRATION SYSTEM\n\n ";


   // Initialize cycle method
   char choice;

   cout << "\nDo you want to cycle using:";
   cout << "\n  (k)eypress, or";
   cout << "\n  (t)ime delay";
   choice = getch ();
   if (choice == 'k')
      press = 1;
   else
      {
      press = 0;
      cout << "\n\nEnter desired time delay in milliseconds: ";
      cin >> msdelay;
      }



   // request autodetection
   int gdriver = DETECT, gmode, errorcode;

   // initialize graphics & local variables
   initgraph(&gdriver, &gmode, "");

   // Set Font, Direction, and Character size
   settextstyle (SMALL_FONT, HORIZ_DIR, 0);

   // read result of initialization
   errorcode = graphresult();
   if (errorcode != grOk)   // an error occurred
      {
      closegraph();
      printf ("Graphics error: %s\n", grapherrormsg(errorcode));
      printf ("Press any key to halt:");
      getch ();
      exit (1);        // terminate with an error code
      }


   full_scr_x = getmaxx ();
   full_scr_y = getmaxy ();


   // Set-up height to match pixel height of screen

   if (full_scr_y > 300)
      {
      height = 12;
      setusercharsize (2,3, 1,1);    // (2/3 width, full height)
      }
   else
      {
      height = 8;
      setusercharsize (2,3, 2,3);    // (2/3 width, 2/3 height)
      }



   // Set up Column and Row locations

   column1 = WIDTH;        // X coordinates
   column2 = WIDTH * 3.5;  //    for the
   column3 = WIDTH * 5;    //  columns

   row1 = height * 2;      // Y coordinates
   row2 = height * 10;     //    for the
   row3 = height * 20;     //   rows



   // set internal structure locations

   iobcb1.location (column2, row2);
   iobuf1.location (column3, row2);
   dct1.location (column1, row3);
   iorq1.location (column2, row3 - 2 * height);
   uicb1.location (column1, row1);
   userbuf1.location (column3, row1);
   kdb1.location (column2, row1 + (3 * height));
   filecb1.location (column1, row2);
   iorequest1.location (column3, row3 - 2 * height);


   // set internal pointer locations

   iobuf_ptr.location (column2 + WIDTH,row2, column3,row2);
   iorq_ptr.location (column1 + WIDTH,row3, column2,row3);
   userbuf_ptr.location (column1 + WIDTH,row1, column3,row1);
   kdb_ptr.location (column1 + WIDTH, row1 + (3 * height),
			   column2, row1 + (3 * height) );
   filecb_ptr.location (WIDTH/2,row2,  column1,row2);
   dct_ptr.location (WIDTH/2,row3,  column1,row3);
   iobcb_ptr.location (column1 + WIDTH,row2,  column2,row2);
   iorequest_ptr.location (column2 + WIDTH,row3 - 2 * height,
				   column3,row3 - 2 * height);



   //Set up initial internal structures

   // Initialize IO buffer control block
   iobcb1.put_pbn ("Physical Block: ");
   iobcb1.put_lbn ("Logical Block: ");
   iobcb1.put_modified ("Buffer Modified: NO");
   iobcb1.put_bufsize ("Buffer Size: 3 ");

   // Initialize IO buffer
   iobuf1.put_bufr (" ");

   // Initialize device control table
   dct1.put_devstat ("device status: ready");
   dct1.put_posretry ("max retries position: 3");
   dct1.put_rdretry ("max retries read: 3");
   dct1.put_wrretry ("max retries write: 3");
   dct1.put_retrycnt ("retry count: 0");

   // Initialize IO request queue
   iorq1.put_stat ("status: no request");
   iorq1.put_hdpos ("head position: cyl 0");
   iorq1.put_hddir ("head direction: out");

   // Draw initial internal structures active at startup
   draw_init ();
   wait ();

   // Initialize message buffers
   strcpy (cmd_msg, " ");
   strcpy (user_msg, " ");


   char buffer [MAXLINE];
   char* buf_ptr;
   char strng [20];
   char strng2 [20];
   char strng3 [20];
   external disk;
   char filetype [15];
   char filename [15];
   char acc_mode [10];
   acc_method* type_ptr;
   sequential seq_acc;
   relative rel_acc;
   direct dir_acc;
// isam isam_acc;         to be added as developed


   //   read first command, which must be create!

   if (!get_line (&infile, buffer))
      {
      sprintf (user_msg, "%s contains no executable statements", argv [1]);
      strcpy (cmd_msg, "Press any key to terminate");
      draw_init ();
      quit ();
      }

   buf_ptr = buffer;

   if ( (!get_cmd (&buf_ptr, strng)) || (strcmp ("create", strng) != 0) )
      {
      strcpy (user_msg, "First command must be create!");
      strcpy (cmd_msg, "Press any key to terminate");
      draw_init ();
      quit ();
      }

   sprintf (cmd_msg, "Currently Executing: %s", buffer);

   while ( *buf_ptr != '(' )     // find the (
      buf_ptr++;
   buf_ptr++;        // move to next character after (

   if (!get_param (&buf_ptr, filetype))
      param_err ("CREATE");
   if (!get_param (&buf_ptr, filename))
      param_err ("CREATE");
   if (!get_lastparam (&buf_ptr, acc_mode))
      param_err ("CREATE");


   // set up a pointer to the type of acc_method specified by the create

   if (strcmp ("sequential", filetype) == 0)
      type_ptr = &seq_acc;
   else if (strcmp ("relative", filetype) == 0)
      type_ptr = &rel_acc;
   else if (strcmp ("direct", filetype) == 0)
      type_ptr = &dir_acc;
// else if (strcmp ("isam", filetype) == 0)      to be added as developed
//    type_ptr = &isam_acc;


   else        // this filetype does not exist
      {
      sprintf (user_msg, "Unknown filetype: %s", filetype);
      strcpy (cmd_msg, "Press any key to terminate");
      draw_init ();
      quit ();
      }


   // execute the create function

   type_ptr -> create (&uicb1, &userbuf1, &filecb1, &kdb1, &iobuf1, &disk,
		       &iobcb1, &iorq1, &iorequest1, argv [1], filename,
		       acc_mode);


   // read and execute the remaining commands from the user file

   while (get_line (&infile, buffer))
      {
      buf_ptr = buffer;
      if (!get_cmd (&buf_ptr, strng))
	 cmd_err ();

      sprintf (cmd_msg, "Currently Executing: %s", buffer);

      while ( *buf_ptr != '(' )     // find the (
	 buf_ptr++;
      buf_ptr++;        // move to next character after (


      // read the parameters for the command and
      // execute the command read

      if (strcmp ("open", strng) == 0)
	 {
	 if (get_param (&buf_ptr, strng))
	    {
	    if (get_lastparam (&buf_ptr, strng2))
	       {
	       if ( strcmp (filename, strng) == 0)
		  type_ptr -> open (&uicb1, &filecb1, &iobcb1, &iobuf1,
				    &iorq1, &iorequest1, &disk, strng2);
	       else
		  param_err ("OPEN");
	       }
	    else
	       {
	       sprintf (user_msg, "File %s does not exist; can't open", strng);
	       strcpy (cmd_msg, "Press any key to terminate");
	       draw_init ();
	       quit ();
	       }
	    }
	 else
	    param_err ("OPEN");
	 }


      else if (strcmp ("close", strng) == 0)
	 {
	 if (get_lastparam (&buf_ptr, strng))
	    {
	    if ( strcmp (filename, strng) == 0)
	       type_ptr -> close (&uicb1, &iobcb1, &iobuf1, &iorq1,
				  &iorequest1, &disk);
	    else
	       {
	       sprintf (user_msg, "File %s does not exist; can't close", strng);
	       strcpy (cmd_msg, "Press any key to terminate");
	       draw_init ();
	       quit ();
	       }
	    }
	 else
	    param_err ("CLOSE");
	 }


      else if (strcmp ("position", strng) == 0)
	 {
	 //  no parameters for a sequential position
	 if (strcmp ("sequential", filetype) == 0)
	    {
	    if (get_lastparam (&buf_ptr, strng))
	       if (*strng == '\0')       // there should be no parameters
		  type_ptr -> position (&uicb1, &filecb1, &iobuf1, &iobcb1,
					 &iorq1, &iorequest1, &disk, strng);
	       else    // a parameter was passed
		  param_err ("POSITION");
	    else       // no closed parenthesis following position command
	       param_err ("POSITION");
	    }

	 else   // if not sequential there is one parameter to read
	    {
	    if (get_lastparam (&buf_ptr, strng))
	       type_ptr -> position (&uicb1, &filecb1, &iobuf1, &iobcb1,
				       &iorq1, &iorequest1, &disk, strng);
	    else
	       param_err ("POSITION");
	    }
	 }


      else if (strcmp ("read", strng) == 0)
	 {
	 if (strcmp ("sequential", filetype) == 0)
	    type_ptr -> read (&uicb1, &filecb1, &iobuf1, &iobcb1, &iorq1,
				   &iorequest1, &userbuf1, &disk, strng);
	 else
	    if (get_lastparam (&buf_ptr, strng))
	       type_ptr -> read (&uicb1, &filecb1, &iobuf1, &iobcb1, &iorq1,
				   &iorequest1, &userbuf1, &disk, strng);
	    else
	       param_err ("READ");
	 }


      else if (strcmp ("readnext", strng) == 0)
	 {
	 if (get_lastparam (&buf_ptr, strng))
	    if (*strng == '\0')
	       type_ptr -> readnext (&uicb1, &filecb1, &iobuf1, &iobcb1,
				   &iorq1, &iorequest1, &userbuf1, &disk);
	    else      // a parameter was passed
	       param_err ("READNEXT");
	 else         // no closed parenthesis following readnext command
	    param_err ("READNEXT");
	 }


      else if (strcmp ("write", strng) == 0)
	 {
	 if (strcmp ("relative", filetype) == 0)
	    {
	    if (!get_param (&buf_ptr, strng))
	       param_err ("WRITE");
	    }

	 if (get_lastparam (&buf_ptr, strng2))
	    {
	    if (strlen (strng2) <= SEC_SIZE - 2)
	       {
	       sprintf (strng3, "  %s", strng2);
	       type_ptr -> write (&uicb1, &userbuf1, &filecb1, &iobuf1,
			   &iobcb1, &iorq1, &iorequest1, &disk, strng, strng3);
	       }
	    else
	       {
	       sprintf (user_msg, "Record %s too large to write", strng);
	       strcpy (cmd_msg, "Press any key to terminate");
	       draw_init ();
	       quit ();
	       }
	    }
	 else
	    param_err ("WRITE");
	 }


      else if (strcmp ("delete", strng) == 0)
	 {
	 if (get_lastparam (&buf_ptr, strng))
	    if (*strng == '\0')
	       type_ptr -> del (&uicb1, &filecb1, &iobuf1, &iobcb1);
	    else
	       param_err ("DELETE");
	 else
	    param_err ("DELETE");
	 }


      else      // not a legitimate command
	 {
	 sprintf (user_msg, "Unknown command: %s", strng);
	 strcpy (cmd_msg, "Press any key to terminate");
	 draw_init ();
	 quit ();
	 }
      }


   // close up the graphics and quit.

   closegraph();
   return 0;

   }     // end of main program





//   wait
//
//   wait is used to cycle the execution of the commands. If the global
//   variable press = 1, then the function waits for a keypress and
//   then returns. If press = 0, indicating timed cycling, then wait
//   waits for a delay in milliseconds equivalent to the value stored
//   in the global variable msdelay and then returns.

void wait (void)
   {
   if (press)
      getch ();
   else
      delay (msdelay);
   }





//   quit
//
//   quit is an exit function. It waits for a keypress, closes the graphics
//   and then exits, terminating the program and returning to the operating
//   system.

void quit (void)
   {
   getch ();
   closegraph ();
   exit (1);
   }





//   draw_init
//
//   draw_init draws the internal structures which exist at startup.
//   Those structures are: iobcb, iobuf, dct, and iorq along with their
//   assocaited pointers. draw_init also prints the contents of the messages
//   cmd_msg and user_msg which are preloaded before calling draw_init.

void draw_init (void)
   {
   clearviewport ();
   iobcb1.draw ();
   iobuf_ptr.draw ();
   iobuf1.draw ();
   dct1.draw ();
   iorq_ptr.draw ();
   iorq1.draw ();

   outtextxy (column2, full_scr_y - 20, cmd_msg);
   outtextxy (column2, full_scr_y - 10, user_msg);
   }





//   draw_fileopen
//
//   draw_fileopen draws all internal structures which exist whenever
//   a file is open. draw_init is called to draw the initial structures,
//   then draw_fileopen draws the structures uicb, userbuf, kdb, filecb,
//   and associated pointers.

void draw_fileopen (void)
   {
   draw_init ();
   uicb1.draw ();
   userbuf_ptr.draw ();
   userbuf1.draw ();
   kdb_ptr.draw ();
   kdb1.draw ();
   line (WIDTH/2,row1 + height/2,  column1,row1 + height/2);
   line (WIDTH/2,row1 + height/2,  WIDTH/2,row2 + height/2);
   filecb_ptr.draw ();
   filecb1.draw ();
   line (WIDTH/2,row2 + (5.5 * height),  column1,row2 + (5.5 * height) );
   line (WIDTH/2,row2 + (5.5 * height), WIDTH/2,row3 + height/2);
   dct_ptr.draw ();
   iobcb_ptr.draw ();
   }





//   draw_all
//
//   draw_all draws all internal file structures by calling draw_init,
//   draw_fileopen, and then drawing the iorequest block and its
//   pointer.

void draw_all (void)
   {
   draw_fileopen ();
   iorequest_ptr.draw ();
   iorequest1.draw ();
   }





//   get_line
//
//   get_line reads a command line from the users input file and places
//   it into a buffer. All comment lines, those beginning with a *, and
//   blank lines are skipped.
//
//   input: fp; a pointer to the ifstream class which holds the file pointer.
//          bufr; a pointer to the buffer where the line should be placed.
//
//   output: The line read is returned in the indicated buffer.
//           The function returns a 1 if a line is placed in the buffer,
//              and a 0 if EOF or no command lines left in the file.

int get_line (ifstream* fp, char* bufr)
   {
   if (*fp)     // if not eof
      {
      do
	 fp -> getline (bufr, MAXLINE);
      while (*fp && ( (bufr [0] == '*') || (bufr [0] == '\0') ));
			 // skip comment lines, and blank lines, in file
      }
   else return (0);

   if ( (bufr [0] == '*') || (bufr [0] == '\0') )
      return (0);
   else
      return (1);
   }





//   get_cmd
//
//   get_cmd reads the command from the buffer pointed to by index, and
//   places that command into the buffer pointed to by cmd. The index
//   is updated by the function so that parsing by the calling program
//   can continue.
//
//   input: index; a pointer to the buffer pointer
//          cmd; a pointer the string to hold the command
//
//   output: The command read is loaded into cmd.
//           If the index points to an end of string character the function
//           returns a 0. The function returns a 1 if a command is actually
//           loaded into cmd.

int get_cmd (char** index, char* cmd)
   {
   int i = 0;

   while ( (**index != '\0') && (**index != ' ') && (**index != '(') )
      {
      *(cmd + i) = **index;
      *index += 1;
      i++;
      }

   if (**index == '\0')
      return (0);
   else
      {
      *(cmd + i) = '\0';
      return (1);
      }
   }





//   get_param
//
//   get_param reads a parameter from the buffer pointed to by index, and
//   places that parameter into the buffer pointed to by field. The index
//   is updated by the function so that parsing by the calling program
//   can continue. Termination of the parameter occurrs upon reaching either
//   an end of string or a comma.
//
//   input: index; a pointer to the buffer pointer
//          field; a pointer the string to hold the parameter
//
//   output: The parameter read is loaded into field.
//           If the index points to an end of string character the function
//           returns a 0. The function returns a 1 if a parameter is actually
//           loaded into field.

int get_param (char** index, char* field)
   {
   int i = 0;

   while ( (**index == ',') || (**index == ' ') )
      *index += 1;

   while ( (**index != '\0') && (**index != ',') )
      {
      *(field + i) = **index;
      *index += 1;
      i++;
      }

   if (**index == '\0')
      return (0);
   else
      {
      *(field + i) = '\0';
      return (1);
      }
   }





//   get_lastparam
//
//   get_lastparam reads a parameter from the buffer pointed to by index, and
//   places that parameter into the buffer pointed to by field. The index
//   is updated by the function so that parsing by the calling program
//   can continue. Termination of the parameter occurrs upon reaching either
//   an end of string or a comma, or a right parenthesis.
//
//   input: index; a pointer to the buffer pointer
//          field; a pointer the string to hold the parameter
//
//   output: The parameter read is loaded into field.
//           If the index points to an end of string character the function
//           returns a 0. The function returns a 1 if a parameter is actually
//           loaded into field.

int get_lastparam (char** index, char* field)
   {
   int i = 0;

   while ( (**index == ',') || (**index == ' ') )
      *index += 1;

   while ( (**index != '\0') && (**index != ' ') && (**index != ')') )
      {
      *(field + i) = **index;
      *index += 1;
      i++;
      }

   if (**index == '\0')
      return (0);
   else
      {
      *(field + i) = '\0';
      return (1);
      }
   }





//   cmd_err
//
//   cmd_err is an exit function which is called whenever an unrecognized
//   command is encountered. An error message is sent and then the quit
//   function is called to exit.

void cmd_err (void)
   {
   strcpy (user_msg, "Illegal command");
   strcpy (cmd_msg, "Press any key to terminate");
   draw_fileopen ();
   quit ();
   }





//   param_err
//
//   param_err is an exit function which is called whenever improper parameters
//   or the wrong number of parameters are encountered. An error message is
//   sent and then the quit function is called to exit.


void param_err (char* cmd)
   {
   char *strng;

   sprintf (strng, "Improper parameter usage: %s", cmd);
   strcpy (user_msg, strng);
   strcpy (cmd_msg, "Press any key to terminate");
   draw_init ();
   quit ();
   }
