// Dictionary Class

// C++
#include <iostream>
#include <list>

// Custom/Local
#include "isr.h"

namespace ISR
{
 // Class Functions

 // ctor
 cDictionary::cDictionary( void )
 {
 }

 // dctor
 cDictionary::~cDictionary( void )
 {
 }

 // Throw all entries into a list, sort list by references
 void cDictionary::PrintHighReferences(bool occurances)
 {
   std::list<cWord> WordTable;
   std::list<cWord>::const_iterator x;
   WordMap::iterator i;
   cWord * ptr = NULL;
   
   // load map into list
   for ( i = mTable.begin(); i != mTable.end(); i++ )
     WordTable.push_back(*(new cWord(i->first, i->second)));

   // sort word table
   WordTable.sort();
   WordTable.reverse(); // gets sorted in ascending, we want descending
   
   // print
   std::cout << "\n\n";   // preliminiary new lines
   
   for ( x = WordTable.begin(); x != WordTable.end(); x++ )
   {
    if ( occurances )
     printf( "%9d - %s\n", i->second, i->first.c_str() );
    else
     printf( "%s\n", i->first.c_str() );       
   }
  }

  // Print all words and numbers in alphabetical order
  void cDictionary::PrintAlphabetical( bool occurances )
  {
   WordMap::iterator i;
  
   // preliminiary new lines
   std::cout << "\n\n";
  
   // loop through and print
   for ( i = mTable.begin(); i != mTable.end(); i++ )
   {
    if ( occurances )
     printf( "%9d - %s\n", i->second, i->first.c_str() );
    else
     printf( "%s\n", i->first.c_str() );
   }
  }


  // Populate dictionary with words from cin
  void cDictionary::Populate( bool display )
  {
   std::string str;
   int i = 0;

   // if we are displaying, print out header
   if ( display )
    std::cout << "Words:           ";

   // Loop until we hit the end of the file
   while ( !std::cin.eof() )
   {
      ScanAlnum( str ); // grab our word
     
      // if we are displaying then update progress
      if ( display )
        printf( "\b\b\b\b\b\b\b\b\b\b%10d", ++i );

      AddRef( str ); // add it
   }
  }
  
  void cDictionary::Populate( cDictionary &Stop, bool display )
  {
   std::string str;
   int i = 0;

   // if we are displaying, print out header
   if ( display )
    std::cout << "Words:           ";

   // Loop until we hit the end of the file
   while ( !std::cin.eof() )
   {
      ScanAlnum( str ); // grab our word

      // if we are displaying then update progress
      if ( display )
        printf( "\b\b\b\b\b\b\b\b\b\b%10d", ++i );

      // if it was found in the stop file then don't add it
      if ( Stop.GetRef( str ) > 0 )
       continue;
      else
       AddRef( str ); // add it
   }
  }

  // Populate dictionary, check for double newlines to indicate a new file
  // Return true if double new lines found
  bool cDictionary::PopulateCheckFile(bool display)
  {
   std::string str;
   char c = '\0';
   int i = 0;

   // if we are displaying, print out header
   if ( display )
    std::cout << "Words:           ";

   // Loop until we hit the end of the file
   while ( !std::cin.eof() )
   {
      ScanAlnum( str ); // grab our word
              
      // if we are displaying then update progress
      if ( display )
        printf( "\b\b\b\b\b\b\b\b\b\b%10d", ++i );

      AddRef( str ); // add it
      
      // check for double newlines to indicate new file  
      c = std::cin.get();      
      
      if ( c == '\n' || c == '\r' )
      {
        if ( std::cin.peek() == '\n' || std::cin.peek() == '\r' )
          return true;
     }
      else
       std::cin.unget();        
   }
   
   return false;
  }

  // Populate dictionary, check for double newlines to indicate a new file
  // Return true if double new lines found. Compare each entry against a stop file
  bool cDictionary::PopulateCheckFile(cDictionary &Stop, bool display)
  {
   std::string str;
   char c = '\0';
   int i = 0;

   // if we are displaying, print out header
   if ( display )
    std::cout << "Words:           ";

   // Loop until we hit the end of the file
   while ( !std::cin.eof() )
   {
      ScanAlnum( str ); // grab our word
             
      // if we are displaying then update progress
      if ( display )
        printf( "\b\b\b\b\b\b\b\b\b\b%10d", ++i );
 
      // if it was found in the stop file then don't add it
      if ( Stop.GetRef( str ) > 0 )
       continue;
      else
       AddRef( str ); // add it
      
      // check for double newlines to indicate new file  
      c = std::cin.get();      
      
      if ( c == '\n' || c == '\r' )
      {
        if ( std::cin.peek() == '\n' || std::cin.peek() == '\r' )
          return true;
     }
      else
       std::cin.unget();        
   }
   
   return false;
  }

  // Add to the dictionary. If it exits, reference++. Return # of references
  int cDictionary::AddRef( const std::string & str)
  {
    WordMap::iterator i;

    i = mTable.find(str);

    // if it wasnt found in the map, add it, otherwise increment its occurance
    if ( i == mTable.end() )
     mTable[str] = 1;
    else
     i->second++;
     
    return i->second;
  }

  // If it exits, add x. Return # of references
  int cDictionary::ChangeRef( const std::string & str, int x)
  {
    WordMap::iterator i;

    i = mTable.find(str);

    // if it wasnt found in the map, add it, otherwise increment its occurance
    if ( i == mTable.end() )
     mTable[str] = x;
    else
     i->second += x;
     
    return i->second;
  }

  // Subtract reference, if references = 0 then remove word, return # of references
  int cDictionary::SubRef( const std::string & str)
  {
    WordMap::iterator i;

    i = mTable.find(str);

    // if it wasnt found in the map return 0, otherwise decrement its occurance
    if ( i == mTable.end() )
     return 0;
    else
    {
     i->second--;
     
     if ( i->second <= 0 ) // remove from table?
     {
       mTable.erase(i);
       return 0;
     }
    }
    
    return i->second;
  }

  // Return number of references to a certain string
  int cDictionary::GetRef( const std::string & str)
  {
    WordMap::iterator i;

    i = mTable.find(str);

    // if it wasnt found in the map, add it, otherwise increment its occurance
    if ( i == mTable.end() )
     return 0;

    return i->second;
  }
};