// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: testprog.cpp 
// Compiler Used: MSVC40, DJGPP 2.7.2.1, GCC 2.7.2.1, HP CPP 10.24
// Produced By: Doug Gaer 
// File Creation Date: 02/04/1997  
// Date Last Modified: 10/16/1998
// Copyright (c) 1997 Douglas M. Gaer
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
This is a test program for the VBDFile class.

The VBD C++ classes are copyright (c) 1997, by Douglas M. Gaer.
All those who put this code or its derivatives in a commercial
product MUST mention this copyright in their documentation for
users of the products in which this code or its derivative
classes are used. Otherwise, you have the freedom to redistribute
verbatim copies of this source code, adapt it to your specific
needs, or improve the code and release your improvements to the
public provided that the modified files carry prominent notices
stating that you changed the files and the date of any change.

THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
CORRECTION.
*/
// ----------------------------------------------------------- // 
#include <iostream.h>
#include <iomanip.h>
#include <string.h>
#include "vbdfile.hpp"
#include "float64.hpp"
#include "timer.hpp"
#include "llist.hpp"
#include "vbdstats.hpp"

class Part
{ 
public:
  Part(INT32 i = 0, FLOAT64 p = 0) { id = i; price = p; }

public:
  friend void *operator new(size_t n, int id, double price,
			    const VBDFilePtr &f) {
    void *ptr = new Part(id, price);
    f->Write(ptr, n, f->Alloc(n));
    return ptr;
  }
  
public:
  INT32 id; 
  FLOAT64 price;
};

void pause()
{
  cout << endl;
  cout << "Press enter to continue..." << endl;
  cin.get();
}

void SimpleTest()
// Simple demo of how to use the file manager class.
{
  VBDFilePtr f(new VBDFile); // Must create dynamically
  const char *FName = "simple.vbd";
  int exists;

  cout << endl;
  cout << "Simple demo of the file manager class." << endl;
  
  if(!VBDFile::Exists(FName)) {
    cout << "Creating new file..." << endl;
    f->Create(FName);
    exists = 0;
    cout << "Reference count = " << f->Count() << endl;
  }
  else {
    cout << "Opening existing file..." << endl;
    f->Open(FName);
    exists = 1;
    cout << "Reference count = " << f->Count() << endl;
  }
  
  pause();
  VBDStats(f);
  pause();
  
  FAU addr;
  int rv;
  __ULWORD__ object_crc, calc_crc;
  
  if(!exists) {
    cout << "Adding object to VBD file..." << endl;
    long id = 17; double price = 42.97;
    Part part(id, price);  // In-memory copy of part to be stored
    cout << "Size of object = " << sizeof(Part) << endl;
    addr = f->Alloc(sizeof(Part)); // Allocate room for part #1
    f->Write(&part, sizeof(Part));
    
    // 9/25/1998: CRC checksum routines
    f->WriteObjectChecksum(addr);
    f->ReadObjectChecksum(addr, &object_crc, &calc_crc);
    cout.setf(ios::uppercase);
    cout << "Object CRC =     0x" << setfill('0') << setw(8) << hex
	 << object_crc << endl;
    cout << "Calculated CRC = 0x" << setfill('0') << setw(8) << hex
	 << calc_crc << dec << endl;
    cout << "Reference count = " << f->Count() << endl;
    VBStats(f, addr);
    pause();
    cout << "Deleting the object from the VBD file..." << endl;
    f->Delete(addr);
    cout << "Reference count = " << f->Count() << endl;

    cout << "UnDeleting the object from the VBD file..." << endl;
    f->UnDelete(addr);
  }
  else {
    f->Open(FName); // Open() closes first
    cout << "Reference count = " << f->Count() << endl;
    Part buf;
    cout << "Reading the first object from the VBD file..." << endl;
    addr = f->FindFirstVB();

    // 9/25/1998: CRC checksum routines
    rv = f->ReadObjectChecksum(addr+sizeof(VBHeader), &object_crc, &calc_crc);
    if(!rv) {
      cout << "Checksum error!" << endl;
      cout.setf(ios::uppercase);
      cout << "Object CRC =     0x" << setfill('0') << setw(8) << hex
	   << object_crc << endl;
      cout << "Calculated CRC = 0x" << setfill('0') << setw(8) << hex
	   << calc_crc << dec << endl;
      cout.unsetf(ios::uppercase);
    }
    
    f->Read(&buf, sizeof(Part), addr+sizeof(VBHeader));
    cout << "Reference count = " << f->Count() << endl;
    cout << "Part: <" << buf.id << ", " << buf.price << ">" << endl;
  }
  
  pause();
  
  VBDStats(f);
  pause();

  cout << "Testing vb find functions..." << endl;

  FAU vb = f->FindFirstVB();
  FAU obj = f->FindFirstObject(); // First object that has not been deleted
  if(vb) cout << "First vb address =       " << vb << endl;
  if(obj) cout << "First Object's address = " << obj << endl;
  
  vb = f->FindNextVB(vb); 
  obj = f->FindNextObject(vb); // Next object that has not been deleted
  if(vb) cout << "Next vb address =        " << vb << endl;
  if(obj) cout << "Next Object's address =  " << obj << endl;

  cout << endl;
  cout << "Closing file automatically using VBDFile destructor..." << endl;
  return; 
}

void DefragStats(const VBDFilePtr &f)
{
  unsigned t, d, r;
  t = f->VBTotal();
  f->VBDeleted(&d, &r);
  
  cout << endl;
  cout << "Number of blocks alloacted: " << t << endl;
  cout << "Total blocks in use: " << (t - (d + r)) << endl;
  cout << "Total deleted/removed:  " << d << "/" << r << " ("
       << d + r << ")" << endl;
}

void DefragTest()
// This function is used to test the VBDFile::Reclaim() function
// using the frist-fit or best-fit method of reclaiming deleted
// and removed blocks. Define the __RECLAIM_BEST_FIT__ macro to
// use the best-fit routine or the __RECLAIM_FIRST_FIT__ macro
// to use the first-fit routine.
{
  const int NumObjects = 100; // Number of objects to allocate

  VBDFilePtr f(new VBDFile); // Must create dynamically
  const char *FName = "defrag.vbd";

  cout << endl;
  cout << "Testing the best-fit/frist-fit allocation methods." << endl;
  
#ifdef __RECLAIM_BEST_FIT__
  cout << endl;
  cout << "Using the best-fit method to reclaim deleted/removed blocks."
       << endl;
#else
  cout << endl;
  cout << "Using the first-fit method to reclaim deleted/removed blocks."
       << endl;
#endif

  if(!VBDFile::Exists(FName)) {
    cout << "Creating new file..." << endl;
    f->Create(FName);
  }
  else {
    cout << "Opening existing file..." << endl;
    f->Open(FName);
  }

  pause();
  VBDStats(f);
  pause();

  FAU addr;

  LList list;
  LListB *ptr;
  char data = 'X';
  int i,j;

  cout << "Adding " << NumObjects << " objects to the file." << endl;
  cout << "Objects range from " << sizeof(data) << " to " << NumObjects
       << " bytes in length"
       << endl;
  cout << "Writing..." << endl;
  
  clock_t Begin = Start();
  for(i = 0; i < NumObjects; i++) {
    addr = f->Alloc(sizeof(data)+i);
    list.Store((__LWORD__)addr); // Store the address of each node alloacted
    for(j = 0; j <= i; j++) {
      f->Write(&data, sizeof(data), addr+j);
    }
  }
  clock_t End = Stop();

  cout.precision(3);
  cout << "Comleted in " << ElapsedTime(Begin, End) << " seconds" << endl;
  DefragStats(f);
  pause();
  
  cout << "Deleting all the blocks alloacted that were just allocated."
       << endl;
  cout << "Working..." << endl;
  
  ptr = list.GetStart();
  Begin = Start();
  do {
    f->Delete(ptr->info);
    ptr = ptr->GetNext();
  }while(ptr);
  End = Stop();
  
  cout.precision(3);
  cout << "Comleted in " << ElapsedTime(Begin, End) << " seconds" << endl;
  DefragStats(f);
  pause();
  
  cout << "Fragmenting the file." << endl;
  cout << "Undeleting every other block that was just deleted."
       << endl;
  cout << "Working..." << endl;
  
  ptr = list.GetStart();
  Begin = Start();
  do {
    ptr = ptr->GetNext();
    f->UnDelete(ptr->info);
    ptr = ptr->GetNext();
  } while(ptr);
  End = Stop();
  
  cout.precision(3);
  cout << "Comleted in " << ElapsedTime(Begin, End) << " seconds" << endl;
  DefragStats(f);
  pause();
  
  cout << "Adding " << NumObjects << " objects to the file." << endl;
  cout << "All objects are " << sizeof(data) << " bytes in length."
       << endl;
  cout << "Writing..." << endl;

  Begin = Start();
  for(i = 0; i < NumObjects; i++) {
    f->Alloc(sizeof(data));
    f->Write(&data, sizeof(data));
  }
  End = Stop();
  
  cout.precision(3);
  cout << "Comleted in " << ElapsedTime(Begin, End) << " seconds" << endl;
  DefragStats(f);
  pause();
  
  int offset = 1;
  cout << "Adding " << NumObjects << " objects to the file." << endl;
  cout << "Objects range from " << (sizeof(data) + offset) << " to "
       << (NumObjects + offset) << " bytes in length"
       << endl;
  cout << "Writing..." << endl;

  Begin = Start();
  for(i = 0; i < NumObjects; i++) {
    addr = f->Alloc((sizeof(data)+i)+offset);
    for(j = 0; j <= i; j++)
      f->Write(&data, sizeof(data), (addr+j)+offset);
  }
  End = Stop();  

  cout.precision(3);
  cout << "Comleted in " << ElapsedTime(Begin, End) << " seconds" << endl;
  DefragStats(f);

  cout << endl;
  list.Clear();
  return;
}

void ReopenTest()
{
  VBDFilePtr f(new VBDFile); 
  const char *fname1 = "cat.vbd";
  const char *fname2 = "dog.vbd";
  const char *fname3 = "mouse.vbd";
  
  cout << endl;

  cout << "Testing the VBDFile::ReOpen() function." << endl;
  if(!VBDFile::Exists(fname1)) f->Create(fname1);
  if(!VBDFile::Exists(fname2)) f->Create(fname2);
  if(!VBDFile::Exists(fname3)) f->Create(fname3);
  
  // Variables for Part class objects
  int id = 17; double price = 42.97;

  cout << endl;
  cout << "Writing one object to: " << fname1 << endl;
  f->Open(fname1);
  new(id, price, f) Part; 
  pause();
  VBDStats(f);
  pause();

  cout << "Writing two objects to: " << fname2 << endl;
  f->ReOpen(fname2);
  new(id, price, f) Part;
  new(id, price, f) Part; 
  pause();
  VBDStats(f);
  pause();

  cout << "Writing three objects to: " << fname3 << endl;
  f->ReOpen(fname3);
  new(id, price, f) Part;
  new(id, price, f) Part; 
  new(id, price, f) Part; 
  pause();
  VBDStats(f);
  pause();
}

void BigBlockTest()
{
  VBDFilePtr f(new VBDFile); 
  const char *FName = "bigblk.vbd";
  f->Create(FName);

  __UWORD__ xl_block = 10000000; // 10Meg bytes
  __UWORD__ el_block = 1000000;  // 1Meg bytes
  __UWORD__ vl_block = 100000;   // 100K bytes
  __UWORD__ l_block  = 10000;    // 10K bytes  
  
  cout << "Testing the allocation/removal of large blocks." << endl;
  cout << "WARNING: This test will require 12 Meg of disk space." << endl;
  cout << "Press Enter to continue or Ctrl-C to abort..." << endl;
  cin.get();
  
  cout << endl;
  cout << "Allocating " << xl_block << " bytes..." << endl;
  FAU addr = f->Alloc(xl_block);
  f->CalcChecksum(xl_block, addr);
  cout << "Removing the block..." << endl;
  f->Remove(addr);
  pause();

  VBDStats(f);
  pause();
  cout << "Allocating " << el_block << " bytes..." << endl;
  addr = f->Alloc(el_block);
  f->CalcChecksum(el_block, addr);
  cout << "Removing the block..." << endl;
  f->Remove(addr);
  pause();

  VBDStats(f);
  pause();
  cout << "Allocating " << vl_block << " bytes..." << endl;
  addr = f->Alloc(vl_block);
  f->CalcChecksum(vl_block, addr);
  cout << "Removing the block..." << endl;
  f->Remove(addr);
  pause();

  VBDStats(f);
  pause();
  cout << "Allocating " << l_block << " bytes..." << endl;
  addr = f->Alloc(l_block);
  f->CalcChecksum(l_block, addr);
  cout << "Removing the block..." << endl;
  f->Remove(addr);
  pause();

  VBDStats(f);
  pause();
  return;
}

void HelpMessage() {
  cout << endl;
  cout << "VBD file manager test program." << endl;
  cout << "Usage: " << endl;
  cout << '\t' << "testprog all" << "\t"
       << "- Execute all of the test functions."
       << endl;
  cout << '\t' << "testprog simple" << "\t"
       << "- Simple demo of the file manager class."
       << endl;
  cout << '\t' << "testprog defrag" << "\t"
       << "- Test the best-fit/frist-fit allocation methods."
       << endl;
  cout << '\t' << "testprog reopen" << '\t'
       << "- Test the VBDFile::ReOpen() function."
       << endl;
  cout << '\t' << "testprog big" << '\t'
       << "- Test the allocation/removal of large blocks."
       << endl;
}

void ProcessArguments(int argc, char *argv[]) 
{
  if(strcmp(argv[1], "all") == 0) {
    SimpleTest();
    pause();
    DefragTest();
    pause();
    ReopenTest();
    pause();
    BigBlockTest();
    return;
  }

  if(strcmp(argv[1], "simple") == 0) {
    SimpleTest();
    return;
  }

  if(strcmp(argv[1], "defrag") == 0) {
    DefragTest();
    return;
  }

  if(strcmp(argv[1], "reopen") == 0) {
    ReopenTest();
    return;
  }

  if(strcmp(argv[1], "big") == 0) {
    BigBlockTest();
    return;
  }

  HelpMessage();
}

int main(int argc, char *argv[]) 
{
  if (argc <= 1) 
    HelpMessage();
  else
    ProcessArguments(argc, argv);
  
  return 0;
}
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //
