/**********************************************************************

File: binaryio.cc
Oct. 201 1999
K.Becker

Purpose: to show how binary I/O works
	Program to read a bunch of text records, 
	save them in a binary file (blocked)

	 - to keep it simple buffer (blocks) will be 256 bytes

	then read them back in and print them to show they still exist

	CAST ONLY POINTERS NOT DATA

	the file header is at the end of the file

	There is some unnecessary duplication of operations in these routines
	- done so origin of values is clear from reading code

ASSUMES: (checked on CC & g++ using 'sizeof')
	int = 4 bytes
	short int = 2 bytes
	long int = 4 bytes
	float = 4 bytes
	unsigned char = 1 byte


**********************************************************************/


#include <iostream.h>
#include <iomanip.h>
#include <stdlib.h>
#include <fstream.h>

// some handy values...

const int BLOCKSZ = 256;
const int BUFFERSZ = 257;
const int EMPTY = 0;
const int NIL = -1;
const int HEADSZ = 24;

/********************************************************************/
/****************************  structures  **************************/
/********************************************************************/
// file stuff
fstream bfile; // the binary file
fstream tfile; // the text file


// the header to the file
typedef struct head_struct {
	unsigned long int rec_cnt;
	unsigned long int rec1_ptr;
        unsigned long int index_cnt;
        unsigned long int ilist_ptr;
        unsigned long int free_cnt;
        unsigned long int flist_ptr;
     };

head_struct Header; // total size = 24 bytes

// what a record looks like in the program
typedef struct Record {
	long int      ID;
        char*         name;
        float         gpa;
        unsigned char n_of_courses;
        char**        courses;
      };

// the buffer; small so we can see what's going on
unsigned char buffer[BUFFERSZ]; 

// some other stuff we'll need...

int nextrec = 0;       // byte 'offset' within the buffer
short int nextch;      // for stepping through buffer

short int*    intp;    // generic pointer to an int - 2 bytes
float*        fltp;    // generic pointer to float - 4 bytes
long int*     lip;     // generic pointer to long int - 4 bytes
unsigned char num;     // 1 byte number

Record       rec;      // place for the unpacked record
short int    recsize;  // size of record in file
short int    namelen;  // size of name string in file

/********************************************************************/
/***********************  TABLE OF CONTENTS  ************************/
/********************************************************************/
//  startup      : to get file names and open files
//  cleanup      : closes files, etc.
//  fetch_header : to read and unpack the header
//
//
//  unpack       : to unpack a record from the buffer to the structure
//  ReadRecord   : gets the next record; refills buffer if necessary
//  pack         : packs record into buffer
//  WriteRecord  : writes the next record; flushes buffer if necessary

/********************************************************************/
/****************************  startup  *****************************/
/********************************************************************/

void startup( fstream& tfile, fstream& bfile )
{
  // EXIT if file open unsuccessful

  char bname[64];
  char tname[64];

  // intros...
  cout << "Binary File Test Program\n\n";
  cout << "Name of text file: ";
  cin >> tname;
  cout << "Name of Binary File: ";
  cin >> bname;

  tfile.open( tname, ios::in );
  if (tfile.fail())
    {
      cout << "Error opening text file. Code is: " 
	   << tfile.rdstate() << endl;
      exit(1);
    }

  bfile.open( bname, ios::binary | ios::in | ios::out );
  if (bfile.fail())
    {
      cout << "Error opening binary file. Code is: " 
	   << bfile.rdstate() << endl;
      exit(1);
    }
} // end startup

/********************************************************************/
/****************************  *Unpack  *****************************/
/********************************************************************/

void unpack( unsigned char* buffer,
	     Record& rec,
	     int nextrec )
{
  //  Unpack: to get the next record
  //     assumes record available in the buffer
  
  int i;
  
  // get pointer to the 2-byte record size
  intp = (short int*)&buffer[nextrec+0];
  recsize = *intp;
  
  // get pointer to 2-byte name char-count
  intp = (short int*)&buffer[nextrec+2];
  namelen = *intp;
  
  rec.n_of_courses = buffer[nextrec+4]; // 1-byte value: direct copy

  lip = (long int*)&buffer[nextrec+5]; // pointer to 4-byte id number
  rec.ID = *lip;

  fltp = (float*)&buffer[nextrec+9]; // gpa
  rec.gpa = *fltp;
  
  rec.name = new char[namelen+1]; // make room for the name
  strcpy( rec.name, (char*)&buffer[nextrec+13] );

  // nextch = where we are in the buffer
  nextch = nextrec+ 14 + strlen(rec.name); 
  for (i = 0; i <= rec.n_of_courses; i++)
    {
      rec.courses[i] = new char[12];
      // Q: what if we don't know max length of a course name?
      strcpy( rec.courses[i], (char*)buffer[nextch]);
      nextch += strlen(rec.courses[i]) + 1;
    }
  
  nextrec += recsize;
} // end unpack

/********************************************************************/
/***************************  ReadRecord  ***************************/
/********************************************************************/
void ReadRecord ( Record& rec )
{
  // get reclen of next record
  //   if 0 then buffer done; re-load
  //       read buffer from file
  //       nextrec = 0;
  //   Unpack a Record

  static int nextrec = 0; // to keep track of where we are in the buffer


}// end ReadRecord

/********************************************************************/
/*******************************  Pack  *****************************/
/********************************************************************/

void pack( unsigned char* buffer, 
	   Record& rec,
	   int nextrec )
{
  // Pack: to 'put' the next record
  //     assumes buffer has room fro record

  int i;
  
  recsize = 14 + strlen(rec.name);
  // recsize so far... we'll add the rest as we figure it out
  
  namelen = strlen(rec.name); // length of name
  
  // write pointer to 2-byte name char-count
  intp = (short int*)&buffer[nextrec+2]; 
  *intp = namelen;
  
  buffer[nextrec+4] = rec.n_of_courses; // 1-byte value: direct copy
  
  lip = (long int*)&buffer[nextrec+5]; // pointer to 4-byte id number
  *lip = rec.ID;
  
  fltp = (float*)&buffer[nextrec+9]; // gpa
  *fltp = rec.gpa;
  
  strcpy( (char*)&buffer[nextrec+13], rec.name ); // the name
  
  nextch = nextrec+14 + namelen; // nextch = where we are in the buffer
  for (i = 0; i <= rec.n_of_courses; i++)
    {
      strcpy( (char*)buffer[nextch], rec.courses[i]);
      nextch += strlen(rec.courses[i]) + 1;
    }
  
  intp = (short int*)&buffer[nextrec+0]; // record size
  *intp = nextch -1;
  
  nextrec += recsize;
  
} // end pack;

/********************************************************************/
/*************************** WriteRecord  ***************************/
/********************************************************************/
void WriteRecord ( Record& rec )
{
  // calculate reclen of next record
  //   if no room in buffer then buffer done; flush
  //       write buffer to file
  //       nextrec = 0;
  //   Pack a Record

  static int nextrec = 0; // to keep track of where we are in the buffer

}// end WriteRecord

/********************************************************************/
/********************** BEGIN MAIN **********************************/
int main ()
{


  cout << "DONE." << endl;
  return 0;
} // END PROGRAM ---
