// Hash table implementation
// By: Nick Cash
// Coded for Computer Science III

// Main.cpp - The starting point of the program.

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <iomanip>
#include "hash.h"

using namespace std;

// Jenkins One-at-a-time hash
uint JenkinsHash(const string& key)
{
     uint hash = 0;
     uint i;

     for (i = 0; i < key.length(); i++)
     {
         hash += key[i];
         hash += (hash << 10);
         hash ^= (hash >> 6);
     }

     hash += (hash << 3);
     hash ^= (hash >> 11);
     hash += (hash << 15);

     return hash;
}

uint CaseHash(const string& key)
{
  return (toupper(key[0])-64);
}

uint LenHash(const string& key)
{
 return key.length();
}


// Read 27k names, compare lookup times
int main( void )
{
    HashTable WordTable(JenkinsHash);
    HashTable CaseTable(CaseHash, 26);
    HashTable LenTable(LenHash, 8);

    ElemList  WordList;
    string    Word;
    ifstream  file;
    int       count, table_time;
    
    time_t    start,end;
    ProfTimer t;
    
    // open the file
    file.open("word_list.txt");
    
    // read in
    time(&start);
    t.Start();
    for ( count = 0; !file.eof() && count < 15100000; count++ )
    {
      file >> Word;
      
      WordList.push_back(Word);
      WordTable.Add(Word);
      CaseTable.Add(Word);
      LenTable.Add(Word);
    }
    t.Stop();
    time(&end);

    cout << fixed << setprecision(15);
    cout << "Read in " << count << " words in " << (int)difftime(end,start) << " / " << t.GetDurationInSecs() << " seconds. (Last word = " << Word << ")\n\n";

    // allow users to search
    while ( true )
    {
       cout << "\nWhat word would you like to search for?\n> ";
       cin >> Word;
       
       if ( Word == "quit" )
        break;
       
       // do normal hash table lookup first
       t.Start();
       table_time = WordTable.Lookup(Word);
       t.Stop();
       
       if ( table_time == -1 )
        cout << "HashTable: Word not found ( " << t.GetDurationInSecs() << ")\n";
       else
        cout << "HashTable: " << table_time << " / " << t.GetDurationInSecs() << " seconds\n";

       // do case table lookup first
       t.Start();
       table_time = CaseTable.Lookup(Word);
       t.Stop();

       if ( table_time == -1 )
        cout << "CaseTable: Word not found ( " << t.GetDurationInSecs() << ")\n";
       else
        cout << "CaseTable: " << table_time << " / " << t.GetDurationInSecs() << " seconds\n";

       // do table lookup first
       t.Start();
       table_time = LenTable.Lookup(Word);
       t.Stop();

       if ( table_time == -1 )
        cout << "LenTable:  Word not found ( " << t.GetDurationInSecs() << ")\n";
       else
        cout << "LenTable:  " << table_time << " / " << t.GetDurationInSecs() << " seconds\n";
        
       // do list lookup
       bool found = false;
       
       time(&start);
       t.Start();
       for ( ElemList::iterator iter = WordList.begin(); iter != WordList.end(); iter++ )
       {
         if ( *iter == Word )
         {
           found = true;
           break;
         }
       }
       t.Stop();
       time(&end);
       
       if ( !found )
        cout << "WordList : Word not found (" << (int)difftime(end,start) << " / " << t.GetDurationInSecs() << " seconds)\n";
       else
        cout << "WordList : " << (int)difftime(end,start) << " / " << setprecision(15) << t.GetDurationInSecs() << " seconds\n";
    }
    
    file.close();
    return EXIT_SUCCESS;
}