//   filedemo.cpp
//
//   filedemo.cpp contains the definitions of the member functions
//   declared in filedemo.h


#include "filedemo.h"


extern int height;                  //  height of each block location in pixels
extern int column1, column2, column3;       // column locations
extern int row1, row2, row3;                // row locations

extern int full_scr_x, full_scr_y;  // maximum x,y screen coordinates

extern char cmd_msg [MAXLINE];      // command currently executing
extern char user_msg [MAXLINE];     // messages to user during execution steps





//   external constructor
//
//   the external constructor clears all disk blocks to all spaces

external::external (void)
   {
   for (int i = 0; i < NUM_BLKS; i++)
      strcpy (blk [i], "                    \0");
   }





//   external::write_blk
//
//   write_blk receives a pointer to a buffer. The string in that buffer
//   is moved to the specified block. If the string is not large enough
//   to fill the entire block (char array) it is filled out with spaces.
//
//   input: blk_no; block number to write to
//          buffer; a pointer to the buffer containing the string to write.

void external::write_blk (int blk_no, char* buffer)
   {
   int i = 0;

   // write each character in buffer to the disk block
   while ( *(buffer + i) != '\0' && i < BLK_SIZE)
      blk [blk_no] [i] = *(buffer + i++);

   if ( *(buffer + i) != '\0' )         // string is greater than block size
      {                                 // won't fit in block; exit
      strcpy (user_msg, "Bad string passed to external class; hit any key to end");
      draw_fileopen ();
      quit ();
      }

   // if string did not fill block; fill remaining characters with spaces
   while ( i++ < BLK_SIZE)
      blk [blk_no] [i] = ' ';

   blk [blk_no] [BLK_SIZE] = '\0';
   }





//   external::draw
//
//   draw draws the disk in a window on the left side of the screen
//   inclluding its current contents as stored in the blk array.

void external::draw (void)  // draw the "disk" onscreen
   {
   char tmp_str [15];                 // To hold cyl/track labels
   int lbl_ofst = CHAR_WID * 15;      // offset of cyl/track labels in pixels
   int blk_wid = (BLK_SIZE * CHAR_WID) + 4;       // width of block in pixels



   //  setup the disk window, clear it and frame it

   setviewport (WIDTH/4, height, WIDTH/4 + DISK_SCR_WID, 23 * height, 1);
   clearviewport ();
   rectangle (0, 0, DISK_SCR_WID, 22 * height);
				// there are 22 lines diplayed in the window


   // draw the "disk"

   for (int i = 0; i < CYLS; i++)                     // for each cylinder
      {
      sprintf (tmp_str, "Cylinder %d", i+1);          // write "Cylinder n"
      outtextxy (CHAR_WID, (i * 5 + 1) * height, tmp_str);

      for (int j = 0; j < TRKS; j++)                  // for each track
	 {
	 sprintf (tmp_str, "Track %d", j+1);          // write "Track n"
	 outtextxy (CHAR_WID * 6, ((i * 5 + 1) + (j + 1)) * height, tmp_str);

	 for (int k = 0; k < BLKS; k++)               // for each block
	    {
	    rectangle ( lbl_ofst + (blk_wid * k),     // draw the box
		   ((i * 5 + 1) + (j + 1)) * height,
		   lbl_ofst + (blk_wid * (k + 1)),
		   ( ((i * 5 + 1) + (j + 1)) * height) + height);


	    outtextxy ( lbl_ofst+(blk_wid * k) + 3,   // write record into box
		   (((i * 5 + 1) + (j + 1)) * height),
		   blk [i*12 + j*3 + k] );
	    }

	 }
      }
   // set active window back to full screen
   setviewport (0,0, full_scr_x,full_scr_y, 1);
   }





//   block constructor
//
//   the block constructor sets the number of locations (lines) in the block and
//   initializes the block title.
//
//   input: NewN; number of lines in block
//          NewTitle; title of block (a char string)

block::block (int NewN, char* NewTitle)
   {
   n = NewN;             // block constructor, establishes the number of lines
   strcpy (title, NewTitle);     // and the title of the block
   }





//   block::location
//
//   location sets the on screen location of the block
//
//   input: NewX; x coordinate of block location
//          NewY; y coordinate of block location

void block::location (int NewX, int NewY)
   {
   x = NewX;           // x,y coordinates of the upper left corner
   y = NewY;           //   of the block
   }





//   block::draw
//
//   draw draws the block as a series of rectangles one on top of another.
//   The title of the block is written above the rectangles.

void block::draw(void)
   {
   outtextxy (x, y - height, title);
   for (int i = 0; i < n; i++)
      rectangle (x, y + height * i, x + WIDTH, y + height * (i+1));
   }





//   block::write
//
//   write writes a text string into the specified line of the block.
//
//   input: line_no; the line number of the block to write to.
//          textstr; a pointer to the string to write there.

void block::write(int line_no, char* textstr)
   {
   // writes text into the specified line number of the block

   outtextxy (x + 2, y + height * (line_no - 1), textstr);
   }





//   iobcb::draw
//
//   iobcb::draw calls the block base class draw function to draw the
//   empty block then calls the block base class write function once
//   for each line in the block

void iobcb::draw (void)
   {
   block::draw ();
   block::write (1, pbn);
   block::write (2, lbn);
   block::write (3, modified);
   block::write (4, bufsize);
   }





//   iobuf::draw
//
//   iobuf::draw calls the block base class draw function to draw the
//   empty block then calls the block base class write function once
//   for each line in the block

void iobuf::draw (void)
   {
   block::draw ();
   block::write (1, bufr);
   }





//   dct::draw
//
//   dct::draw calls the block base class draw function to draw the
//   empty block then calls the block base class write function once
//   for each line in the block

void dct::draw (void)
   {
   block::draw ();
   block::write (1, devstat);
   block::write (2, posretry);
   block::write (3, rdretry);
   block::write (4, wrretry);
   block::write (5, retrycnt);
   }





//   iorq::draw
//
//   iorq::draw calls the block base class draw function to draw the
//   empty block then calls the block base class write function once
//   for each line in the block

void iorq::draw (void)
   {
   block::draw ();
   block::write (1, stat);
   block::write (2, hdpos);
   block::write (3, hddir);
   }





//   uicb::draw
//
//   uicb::draw calls the block base class draw function to draw the
//   empty block then calls the block base class write function once
//   for each line in the block

void uicb::draw (void)
   {
   block::draw ();
   block::write (1, id);
   block::write (2, filename);
   block::write (3, filetype);
   block::write (4, opreq);
   block::write (5, indexnum);
   block::write (6, accmode);
   }





//   userbuf::draw
//
//   userbuf::draw calls the block base class draw function to draw the
//   empty block then calls the block base class write function once
//   for each line in the block

void userbuf::draw (void)
   {
   block::draw ();
   block::write (1, data);
   }





//   kdb::draw
//
//   kdb::draw calls the block base class draw function to draw the
//   empty block then calls the block base class write function once
//   for each line in the block

void kdb::draw (void)
   {
   block::draw ();
   block::write (1, keysize);
   block::write (2, keytype);
   }





//   filecb::draw
//
//   filecb::draw calls the block base class draw function to draw the
//   empty block then calls the block base class write function once
//   for each line in the block

void filecb::draw (void)
   {
   block::draw ();
   block::write (1, filename);
   block::write (2, stat);
   block::write (3, physeof);
   block::write (4, logeof);
   block::write (5, maxrec);
   block::write (6, blkfac);
   block::write (7, lastop);
   block::write (8, crp);
   }





//   iorequest::draw
//
//   iorequest::draw calls the block base class draw function to draw the
//   empty block then calls the block base class write function once
//   for each line in the block

void iorequest::draw (void)
   {
   block::draw ();
   block::write (1, pbn);
   block::write (2, priority);
   }





//   pointer::location
//
//   location sets the starting and ending location of the pointer.
//
//   input: NewStartx; x coordinate of starting point
//          NewStarty; y coordinate of starting point
//          NewEndx; x coordinate of starting point
//          NewEndy; y coordinate of starting point

void pointer::location (int NewStartx, int NewStarty, int NewEndx, int NewEndy)
   {
   startx = NewStartx;
   starty = NewStarty;
   endx = NewEndx;
   endy = NewEndy;
   }





//   pointer::draw
//
//   draw draws the pointer as a line from start to end. Then an arrow
//   is added to the end of the line.

void pointer::draw(void)
   {
   //  Draw the line
   line (startx, starty + height/2, endx, endy + height/2);

   // Add an arrow to the end of the line
   line (endx - height/2, endy,  endx, endy + height/2);
   line (endx - height/2, endy + height,  endx, endy + height/2);
   }





//   acc_method::update_hdpos
//
//   update_hdpos writes the new head position to the IO request queue.
//   It also calculates the direction of head movement and updates the
//   head direction in the IO request queue.
//
//   input: iorq_ptr; a pointer to the IO request queue.
//          cur_hd_pos; the current head position.

void acc_method::update_hdpos (iorq* iorq_ptr, int cur_hd_pos)
   {
   char strng [WIDTH/CHAR_WID];     // temporary string

   // write new head position to iorq
   sprintf (strng, "head position: cyl %d", cur_hd_pos + 1);
   iorq_ptr -> put_hdpos (strng);

   // update head direction
   if (cur_hd_pos != hd_pos)
      if (cur_hd_pos < hd_pos)
	 iorq_ptr -> put_hddir ("head direction: in");
      else
	 iorq_ptr -> put_hddir ("head direction: out");

   // update head position
   hd_pos = cur_hd_pos;
   }





//   acc_method::put_blk
//
//   put_blk moves the block in the IO buffer to the disk. It cycles through
//   the required access steps in doing so, opening an IO request, moving
//   the block, then closing the IO request.
//
//   input: pointers to the following blocks are passed:
//               iobuf, iobcb, iorq, iorequest, disk

void acc_method::put_blk (iobuf* iobuf_ptr, iobcb* iobcb_ptr, iorq* iorq_ptr,
			 iorequest* iorequest_ptr, external* disk_ptr)
   {
   char strng [WIDTH/CHAR_WID];    // temporary string
   int blk_num;                    // the block number to move

   // get the block number of block currently in buffer
   iobcb_ptr -> get_pbn (strng);
   blk_num = atoi (strng + 16);

   // open an IO request
   iorq_ptr -> put_stat ("status: in progress");
   sprintf (strng, "Physical Block: %d", blk_num);
   iorequest_ptr -> put_pbn (strng);
   strcpy (user_msg, "Move IO buffer to disk");
   draw_all ();
   disk_ptr -> draw ();
   wait ();

   // move the block to the disk
   iobuf_ptr -> get_bufr (strng);
   disk_ptr -> write_blk (blk_num, strng);
   strcpy (user_msg, "Close the IO request");
   draw_all ();
   disk_ptr -> draw ();
   wait ();

   // close the IO request
   iobcb_ptr -> put_modified ("Buffer Modified: NO");
   iorq_ptr -> put_stat ("status: no request");

   // update head position and direction
   update_hdpos (iorq_ptr, blk_num / (BLKS * TRKS));
   strcpy (user_msg, " ");
   }





//   acc_method::get_blk
//
//   get_blk moves a block from the disk to the IO buffer. It cycles through
//   the required access steps in doing so, opening an IO request, moving
//   the block, then closing the IO request.
//
//   input: pointers to the following blocks are passed:
//               iobuf, iobcb, iorq, iorequest, disk
//          blk_num; the block number of the block to be moved.

void acc_method::get_blk (iobuf* iobuf_ptr, iobcb* iobcb_ptr, iorq* iorq_ptr,
			 iorequest* iorequest_ptr, external* disk_ptr,
			 int blk_num)
   {
   char strng [WIDTH/CHAR_WID];     // temporary string

   // open an IO request
   iorq_ptr -> put_stat ("status: in progress");
   sprintf (strng, "Physical Block: %d", blk_num);
   iobcb_ptr -> put_pbn (strng);
   iorequest_ptr -> put_pbn (strng);
   sprintf (strng, "Logical Block: %d", blk_num);
   iobcb_ptr -> put_lbn (strng);
   strcpy (user_msg, "Move block from disk to IO buffer");
   draw_all ();
   disk_ptr -> draw ();
   wait ();

   // move the block to the buffer
   disk_ptr -> read_blk (blk_num, strng);
   iobuf_ptr -> put_bufr (strng);
   strcpy (user_msg, "Close the IO request");
   draw_all ();
   disk_ptr -> draw ();
   wait ();

   // close the IO request
   iobcb_ptr -> put_modified ("Buffer Modified: NO");
   iorq_ptr -> put_stat ("status: no request");

   // update head position and direction
   update_hdpos (iorq_ptr, blk_num / (BLKS * TRKS));
   strcpy (user_msg, " ");
   }





//   get_cur_rec
//
//   get_cur_rec extracts the current record number from the field
//   in the file control block which holds the CRP as a block/record
//   number composite.
//
//   input: filecb_ptr; a pointer to the file control block
//
//   output: returns the record number

int acc_method::get_cur_rec (filecb* filecb_ptr)
   {
   char strng [WIDTH/CHAR_WID];     // temporary string
   int i;                           // used as index

   filecb_ptr -> get_crp (strng);   // read current record pointer
   i = 13;
   while (*(strng + i) != '/')
      i++;                          // skip past block CRP to record CRP
   i++;                             // point to start of current record number
   return (atoi (strng + i));
   }





//   acc_method::close
//
//   close closes up the internal structures associated with the open
//   file. If the buffer has been modified it is written back to the
//   disk before closing.
//
//   input: pointers to the following classes are passed:
//             uicb, iobcb, iobuf, iorq, iorequest, disk

void acc_method::close (uicb* uicb_ptr, iobcb* iobcb_ptr, iobuf* iobuf_ptr,
			iorq* iorq_ptr,	iorequest* iorequest_ptr,
			external* disk_ptr)
   {
   char strng [WIDTH/CHAR_WID];   // temporary string

   uicb_ptr -> put_opreq ("op request: CLOSE");
   strcpy (user_msg, "Check for modified buffer, then close file");
   draw_fileopen ();
   wait ();


   iobcb_ptr -> get_modified (strng);
   if (strcmp ("Buffer Modified: YES", strng) == 0)
      put_blk (iobuf_ptr, iobcb_ptr, iorq_ptr, iorequest_ptr, disk_ptr);

   strcpy (user_msg, "File closed");
   draw_init ();
   wait ();
   }
