// ------------------------------- //
// -------- 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/12/1997 
// Date Last Modified: 10/15/1998
// Copyright (c) 1997 Douglas M. Gaer
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
This is test program for the Disk-based binary search tree.
This program is used to create an index file, which contains
key indexes.

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 ing80
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 <fstream.h>
#include <stdlib.h>
#include <string.h>
#include "timer.hpp"
#include "strutil.hpp"
#include "dtree.hpp"
#include "cacstats.hpp"

// Default cache size
const int cache_size = 15;

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

const int MaxLines = 10;
int Lines = 0;

void PrintTree(CachePointer Tree) 
{
  CachePointer l(Tree); // Using Tree to get cache
  while((__LWORD__)Tree) {
    l = Tree->Left;
    Tree.Release(); // Otherwise cache might fill up
    PrintTree(l);
    Lines++;
    cout << "FAU: " << (__LWORD__)Tree << ' '
	 << "Data: " << Tree->Data << endl;
    if(Lines > MaxLines) {
      Lines = 0;
      cout << endl;
      cout << "Press enter to continue >";
      cin.get();
      cout << endl;
    }

    Tree = Tree->Right;
  }
}

void ListInOrder(CachePointer Tree) 
{
  CachePointer l(Tree); // Using Tree to get cache
  while((__LWORD__)Tree) {
    l = Tree->Left;
    Tree.Release(); // Otherwise cache might fill up
    ListInOrder(l);
    cout << Tree->Data << endl;
    Lines++;
    if(Lines > MaxLines) {
      Lines = 0;
      cout << endl;
      cout << "Press enter to continue >";
      cin.get();
      cout << endl;
    }
    Tree = Tree->Right;
  }
}

void Dump(CachePointer Tree)
{
  CachePointer l(Tree); // Using Tree to get cache
  while((__LWORD__)Tree) {
    l = Tree->Left;
    Tree.Release(); // Otherwise cache might fill up
    Dump(l);
    cout << Tree->Data << endl;
    Tree = Tree->Right;
  }
}

char *InputData()
{
  char buf[255];
  cout << "Enter word: ";
  cin.getline(buf, sizeof(buf));
  unsigned len = 0;
  for(unsigned i = 0; (buf[i] != ' ') && (buf[i] != '\0'); i++) len++;
  char *s = new char[len];
  s[len] = '\0';
  memmove(s, buf, len);
  return s;
}

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;
}

int Find(DTree &t, char *s)
{
  if(!s) return 0;
  FKey e = s;
  int status = t.Search(e);
  return status;
}

void FindWord(DTree &t)
{
  char *buf = InputData();
  clock_t Begin = Start();
  int exists = Find(t, buf);
  clock_t End = Stop();
  
  if(!exists) {
    cout << "Could not find: " << buf << endl;
    return;
  }
  
  cout.precision(3);
  cout << "Found: " << buf << " in " << ElapsedTime(Begin, End)
       << " seconds" << endl;
  cout << endl;
}

void RemoveWord(DTree &t)
{
  char *buf = InputData();

  int exists = Find(t, buf);
  
  if(!exists) {
    cout << "Could not find: " << buf << endl;
    return;
  }

  FKey key(buf);
  
  cout << "Deleting: " << key << endl;
  if(t.Delete(key) != 1) {
      cout << "Problem removing " << key << endl;
      Error->SignalException(EHandler::AssertError);
    }
}

void AddWord(DTree &t)
{
  char *buf = InputData();

  int exists = Find(t, buf);

  if(exists) {
    cout << buf << " already int the dictionary!" << endl;
    return;
  }

  int ex;
  FKey key(buf);
  t.Add(key, ex);
  cout << "Added " << key << endl;
}

void Menu(void)
{
  cout << "(A)   Add a word to the dictionary" << endl;
  cout << "(F)   Find a word in the dictionary" << endl;
  cout << "(L)   List the entire contents of the dictionary" << endl; 
  cout << "(H)   Help (prints this menu)" << endl;
  cout << "(P)   Print the tree node by node" << endl;
  cout << "(R)   Remove a word from the dictionary" << endl;
  cout << "(T)   Display DTree stats" << endl;
  cout << "(Q)   Quit" << endl;
  cout << "(X)   Dump the entire dictionary" << endl;
  cout << endl;
}

void BuildDictionary(const char *fname)
{
  cout << endl;
  cout << "Building a new dictionary." << endl;

  VBDFilePtr f(new VBDFile);
  DTree t(cache_size);
  f->Create(fname, sizeof(TreeHeader));
  t.Connect(f, 1);
  
  int status, num, i;
  int count = 0;
  char words[MAXWORDS][MAXWORDLENGTH];
  const int MaxLine = 255;
  char LineBuffer[MaxLine];
  
#ifdef __DOS__
  char *FileName = "..\\..\\diction\\amerdict.txt";
#endif

#ifdef __UNIX__
  char *FileName = "../../diction/amerdict.txt";
#endif
  
  const char dchar = '/';  // Text delimiter
  
  ifstream istream(FileName, ios::in|ios::nocreate);
  if(!istream) { // Could not open the istream
    cout << "Could not open file: " << FileName << endl;
    Error->SignalException(EHandler::FileOpenError);      
  }

  cout << endl;
  cout << "WARNING: This will require 5 Meg of disk space." << endl;
  cout << "Press Enter to continue or Ctrl-C to abort..." << endl;
  cin.get();
  cout << endl;
  cout << "Adding words..." << endl;
  
  // Get CPU clock cycles before entering loop
  clock_t Begin = Start();

  while(1) {
    for(i = 0; i < MaxLine; i++) LineBuffer[i] = 0; // Clear the line buffer

    // Read in file line by line
    istream.getline(LineBuffer, MaxLine);

    if(parse(LineBuffer, words, &num, dchar) == 1) 
      Error->SignalException(EHandler::ParseError);      

    if(istream.eof()) break; // Exit loop at EOF

    int ex;
    t.Add(LineBuffer, ex);
    count++;
  }
  istream.close();

  // Get CPU clock cycles after loop is completed 
  clock_t End = Stop();

  cout.precision(3);
  cout << "Added " << count << " words in " << ElapsedTime(Begin, End)
       << " seconds" << endl;

  cout << endl;
}

int main()
{
  // Default dictionary name
  char *fname = "amerdict.dtx";

  if(!VBDFile::Exists(fname)) BuildDictionary(fname);

  VBDFilePtr f(new VBDFile);
  DTree t(cache_size);
  f->Open(fname);
  t.Connect(f, 0);
  
  // Main menu
  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); AddWord(t); break;
      case 'f' : case 'F' : SkipToEol(cin); FindWord(t); break;	
      case 'l' : case 'L' : SkipToEol(cin); ListInOrder(t.GetRoot()); break;
      case 'h' : case 'H' : Menu(); break;
      case '?' : Menu(); break;
      case 'p' : case 'P' : SkipToEol(cin); PrintTree(t.GetRoot()); break;
      case 'q' : case 'Q' : rv = Quit(); break;
      case 'r' : case 'R' : SkipToEol(cin); RemoveWord(t); break;
      case 't' : case 'T' : SkipToEol(cin); Report(t.GetCache(), 0); break;
      case 'x' : case 'X' : SkipToEol(cin); Dump(t.GetRoot()); break;
      default:
        cout << "Unrecognized command" << endl;
    }
  }
  return 0;
}
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //
//
