// ------------------------------- //
// -------- 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/15/1998
// Copyright (c) 1997 Douglas M. Gaer
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
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.

This is a test program for the VBDFile class used to read and
write user define strings to a (V)ariable (B)lock (D)atabase
file.
*/
// ----------------------------------------------------------- // 
#include <iostream.h>
#include "ustring.hpp"
#include "vbdfile.hpp"
#include "crc32.hpp"
#include "vbdstats.hpp"

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

void SkipToEol(istream &s)
// Used to clear istream
{
  char c;
  s.clear();
  while(s.get(c) && c != '\n') { ; }
}

int Quit()
{
  cout << "Exiting..." << endl;
  return 0;
}

void InputData(UString &s)
{
  cout << "Input Data: ";
  cin >> s;
}

FAU AddStringObject(const VBDFilePtr &f, const UString &str)
{
  FAU addr = f->Alloc(str.length());
  if(!addr) return 0; // Could not allocate space for string
  f->Write(str.c_str(), str.length());
  return addr;
}

void ReadStringObject(const VBDFilePtr &f, UString &str, 
		      FAU addr = CurrAddress)
{
  unsigned len = f->ObjectLength(addr);
  char *s = new char[len];
  f->Read(s, len, addr);
  s[len] = '\0';
  str = s;
  delete s;
}

FAU Find(const VBDFilePtr &f, const UString &str)
{
  UString buf;
  FAU addr = 0;
  FAU offset = sizeof(VBHeader);

  while(1)
    {
      addr = f->FindFirstObject(addr);
      if(!addr) break;
      ReadStringObject(f, buf, addr);
      if(str == buf) return addr;
    }
  return 0; // Could not find string
}

void AddString(const VBDFilePtr &f)
{
  UString buf;
  InputData(buf);

  FAU exists = Find(f, buf);

  if(exists) {
    cout << "String: " << buf << " already exists at address "
	 << exists << endl;
    return;
  }
  
  FAU addr = AddStringObject(f, buf);
  if(!addr) {
    cout << "Could not add string object to database" << endl;
    return;
  }

  cout << "Added string " << buf << " at FAU " << addr << endl;
  return;
}

void FindString(const VBDFilePtr &f)
{
  UString buf;

  InputData(buf);
  
  FAU addr = Find(f, buf);
  
  if(!addr) {
    cout << "Could not find string: " << buf << " in the database" << endl;
    return;
  }
  
  cout << "Found string: " << buf << " at address " << addr << endl;
}

void Delete(const VBDFilePtr &f)
{
  UString buf;
  InputData(buf);

  FAU addr = Find(f, buf);
  
  if(!addr) {
    cout << "Could not find string: " << buf << " in the database" << endl;
    return;
  }

  cout << "Deleting string: " << buf << " at address " << addr << endl;
  f->Delete(addr); // Delete at object's address
}

void Remove(const VBDFilePtr &f)
{
  UString buf;
  InputData(buf);

  FAU addr = Find(f, buf);
  
  if(!addr) {
    cout << "Could not find string: " << buf << " in the database" << endl;
    return;
  }

  cout << "Deleting string: " << buf << " at address " << addr << endl;
  f->Remove(addr); // Delete at object's address
}

void ListInOrder(const VBDFilePtr &f)
{
  UString str;
  FAU addr = 0;

  while(1)
    {
      addr = f->FindFirstObject(addr);
      if(!addr) break;
      ReadStringObject(f, str);
      cout << "FAU: " << addr << " \t" << str <<endl;
    }
}

void ListNextOrder(const VBDFilePtr &f)
{
  UString str;
  FAU addr = 0;

  while(1)
    {
      addr = f->FindNextObject(addr);
      if(!addr) break;
      ReadStringObject(f, str);
      cout << str << endl;
    }
}

void DumpInOrder(const VBDFilePtr &f)
{
  UString str;
  FAU addr = 0;
  FAU offset = sizeof(VBHeader);

  while(1)
    {
      addr = f->FindFirstVB(addr+offset);
      if(!addr) break;
      ReadStringObject(f, str, addr+sizeof(VBHeader));
      cout << str << endl;
    }
}

void Menu(void)
{
  cout << "(A)   Add object to the database" << endl;
  cout << "(B)   Build string database" << endl;
  cout << "(D)   Delete object from the database" << endl;
  cout << "(F)   Find specified object in the database" << endl;
  cout << "(L)   List object in order" << endl; 
  cout << "(S)   Display VBD file stats" << endl;
  cout << "(H)   Help (prints this menu)" << endl;
  cout << "(P)   Dump entire contents in order" << endl; 
  cout << "(Q)   Quit" << endl;
  cout << "(R)   Remove object object from the database" << endl;
  cout << "(Z)   List object in next order" << endl; 
}

void Build(const VBDFilePtr &f)
{
  cout << "Adding 9 three byte string objects to database..." << endl;
  
  UString aa("DOG");
  UString bb("CAT");
  UString cc("COW");
  UString dd("BAT");
  UString ee("ANT");
  UString ff("BUG");
  UString gg("HOG");
  UString hh("PIG");
  UString ii("HEN");
  
  if(!Find(f, aa)) AddStringObject(f, aa);
  if(!Find(f, bb)) AddStringObject(f, bb);
  if(!Find(f, cc)) AddStringObject(f, cc);
  if(!Find(f, dd)) AddStringObject(f, dd);
  if(!Find(f, ee)) AddStringObject(f, ee);
  if(!Find(f, ff)) AddStringObject(f, ff);
  if(!Find(f, gg)) AddStringObject(f, gg);
  if(!Find(f, hh)) AddStringObject(f, hh);
  if(!Find(f, ii)) AddStringObject(f, ii);
}

int main()
{
  VBDFilePtr f(new VBDFile);   // Should always create dynamically
  const char *FName = "strings.vbd";

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

  char key;
  Menu();

  int rv = 1;
  while(rv) {
    if (!cin) { // Input is in fail state
       SkipToEol(cin); // Go to end of line
       if (!cin) {  // Can't fix
          cout << "Input stream is broken" << endl;
          return 0;
       }
    }
    cout << '>';
    cin >> key;
    if (!cin) continue; // Fix at top of loop
    switch(key) {
      case 'a' : case 'A' : SkipToEol(cin); AddString(f); break;
      case 'b' : case 'B' : Build(f); break;
      case 'd' : case 'D' : Delete(f); break;
      case 'f' : case 'F' : FindString(f); break;	
      case 'l' : case 'L' : ListInOrder(f); break;	 
      case 's' : case 'S' : VBDStats(f); break;	
      case 'h' : case 'H' : Menu(); break;
      case '?' : Menu(); break;
      case 'q' : case 'Q' : rv = Quit(); break;
      case 'p' : case 'P' : DumpInOrder(f); break;
      case 'r' : case 'R' : Remove(f); break;
      case 'z' : case 'Z' : ListNextOrder(f); break;	 
	
      default:
        cout << "Unrecognized command" << endl;
    }
  }
  return 0;
}
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //
