// ------------------------------- //
// -------- 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: 09/18/1997  
// Date Last Modified: 10/08/1998 
// Copyright (c) 1997 Douglas M. Gaer
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
This is a test program use to test the (P)ersistent base 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 "part.hpp"
#include "btreeprt.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 << "Enter the name of the part:    ";
  cin >> s;
}

void DisplayPart(Part &part, int full = 1)
{
  cout << endl;
  cout << "Item's Name  = " << part.GetName() << endl;
  if(full) {
    cout << "Stock Number = " << part.GetID() << endl;
    cout.setf(ios::showpoint | ios::fixed);
    cout.precision(2);
    cout << "Price        = $" << part.GetPrice() << endl;
    cout << endl;
  }
}

void AddPart(const POD *DB)
{
  UString buf;
  InputData(buf);

  Part part(DB);
  part.SetName(buf);
  FAU exists = part.FindObject();

  if(exists) {
    cout << "Part: " << buf << " already exists at address "
	 << exists << endl;
    return;
  }

  int id;
  double price;

  cout << "Enter the part's stock number: ";
  cin >> id;
  if(cin) {
    part.SetID(id);
    cout << "Enter the part's price:       $";
    cin >> price;
  } 
  else {
    cout << "Invalid entry. Object not added!" << endl;
    return;
  }
  if(cin) {
    part.SetPrice(price);
  }
  else {
    cout << "Invalid entry. Object not added!" << endl;
    return;
  }
  
  SkipToEol(cin);
  
  FAU addr = part.AddObject();
  if(!addr) {
    cout << "Could not add object to database" << endl;
    return;
  }
  return;
}

void Delete(const POD *DB)
{
  UString buf;
  InputData(buf);

  Part part(DB);
  part.SetName(buf);
  FAU addr = part.DeleteObject();
  
  if(!addr) {
    cout << "Could not find part: " << buf << " in the database" << endl;
    return;
  }

  cout << "Deleted part: " << buf << " at address " << addr << endl;
}

void Remove(const POD *DB)
{
  UString buf;
  InputData(buf);

  Part part(DB);
  part.SetName(buf);
  FAU addr = part.RemoveObject();
  
  if(!addr) {
    cout << "Could not find part: " << buf << " in the database" << endl;
    return;
  }

  cout << "Deleted part: " << buf << " at address " << addr << endl;
}

void ListInOrder(CachePointer t, const POD *DB)
// List using the index file.
{
  int i = 0;
  Part part(DB);

  // Ensure that the in memory buffers and the file data
  // stay in sync during multiple file access.
  DB->Index()->TestTree();
  
  if ((__LWORD__)t) {
    while (i < t->cnt) {
      part.ReadObject(t->entry[i].object_address);
      DisplayPart(part);
      i++;
    }

    int n = t->cnt;
    CachePointer p(t);
    for(i = -1; i < n; i++) { 
      if((__LWORD__)p) 
	p = t->Branch(i);
      t.Release(); // Prevent cache from filling up during recursion
      ListInOrder(p, DB);  
    }
  }
}

void ListInOrder(const POD *DB)
// List without using the index file
{
  FAU addr = 0;
  Part part(DB);
  unsigned count = 0;
  SkipToEol(cin); // Clear input stream
  while(1)
    {
      addr = DB->OpenDatabase()->FindFirstObject(addr);
      if(!addr) break;
      part.ReadObject(addr);
      DisplayPart(part);
      count++;
      if(count==2) {
	pause();
	count = 0;
      }
    }
}

void FindPart(const POD *DB)
{
  UString buf;
  InputData(buf);

  Part part(DB);
  part.SetName(buf);
  FAU addr = part.FindObject();
  
  if(!addr) {
    cout << "Could not find part: " << buf << " in the database" << endl;
    return;
  }

  cout << "Found part " << buf << " at address " << addr << endl;
  part.ReadObject(addr);
  DisplayPart(part);
}

void Menu(void)
{
  cout << "(A)   Add object to the database" << endl;
  cout << "(D)   Delete object from the database" << endl;
  cout << "(F)   Find part by name" << endl;          
  cout << "(L)   List without using index file" << endl; 
  cout << "(H)   Help (prints this menu)" << endl;
  cout << "(I)   List using the index file" << endl;
  cout << "(P)   Print the index file contents" << endl;
  cout << "(Q)   Quit" << endl;
  cout << "(R)   Remove object object from the database" << endl;
  cout << "(X)   Compare the index file to the data file" << endl;
  cout << "(Y)   Rebuild the index file" << endl;
}

void Compare(const POD *DB)
{
  if(!DB->UsingIndex()) return;
  
  Part part(DB);
  cout << endl;
  cout << "Comparing the index file to the data file..." << endl;
  int rv = part.CompareIndex();
  if(!rv) {
    cout << "The index file does not match the data file!" << endl;
    cout << "The index file needs to be rebuilt." << endl;
    cout << endl;
    return;
  }

  cout << "The index file checks good" << endl;
  cout << endl;
}

void Rebuild(const POD *DB, const char *fname)
{
  if(!DB->UsingIndex()) return;
  
  Part part(DB);
  UString NewIndexFile(fname);
  cout << endl;
  cout << "Rebuilding the index file..." << endl;
  int rv = part.RebuildIndexFile(NewIndexFile.c_str());
  if(!rv) {
    cout << "The index file was not rebuilt!" << endl;
    cout << endl;
    return;
  }

  cout << "The index file was rebuilt." << endl;
  cout << "A new index file named " << fname << " was created.";
  cout << endl;
}

int main()
{
  int CacheSize = 15;   // Memory cache size for the index file
  int UseIndexFile = 1; // This application will use an index file

  // Define file access modes used in the application
  VBDFile::AccessMode RWMode = VBDFile::READWRITE;
  VBDFile::AccessMode ROMode = VBDFile::READONLY;

  // Test POD constructors that use the same file name
  UString FileName("part");      // Testing UString constructor 
  // POD pod(FileName, RWMode); // Not using index file
  // POD pod(FileName.c_str(), RWMode); // Not using index file
  // POD pod(FileName, RWMode, UseIndexFile, CacheSize);
  POD pod(FileName.c_str(), RWMode, UseIndexFile, CacheSize);
  
  // Test POD constructors that use the different file names
  UString dfname("part.pod"); UString ifname("part.btx");
  // POD pod(dfname, ifname, RWMode); // Not using index file
  // POD pod(dfname.c_str(), ifname.c_str(), RWMode); // Not using index file
  // POD pod(dfname, ifname, RWMode, UseIndexFile, CacheSize);
  // POD pod(dfname.c_str(), ifname.c_str(), RWMode, UseIndexFile, CacheSize);

  POD *DB = &pod;

  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); AddPart(DB); break;
      case 'd' : case 'D' : Delete(DB); break;
      case 'f' : case 'F' : FindPart(DB); break;	
      case 'l' : case 'L' : ListInOrder(DB); break;	 
      case 'i' : case 'I' : ListInOrder(DB->Index()->GetRoot(), DB); break;
      case 'h' : case 'H' : Menu(); break;
      case '?' : Menu(); break;
      case 'p' : case 'P' :
	PrintTree(DB->Index()->GetRoot(), DB->Index()); break;
      case 'q' : case 'Q' : rv = Quit(); break;
      case 'r' : case 'R' : Remove(DB); break;
      case 'x' : case 'X' : Compare(DB); break;
      case 'y' : case 'Y' : Rebuild(DB, "newindex.btx"); break;

      default:
        cout << "Unrecognized command" << endl;
    }
  }

  return 0;
}
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //
