
//  Problem:  words_to_line
//  Author:   Chuck Stweart
//  Purpose:  Given a text file, give an alphabetical listing of the
//     words in the file and the file line numbers on which each word
//     appears.  If a word appears on a line more than once, the line
//     numbers are not repeated.  A map of strings and vectors is used
//     to store the information.

#include <algorithm>
#include <cctype>
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <vector>

//  Forward reference to a function that will be discussed in Lecture 13.
std::vector<std::string> breakup_v2( const std::string& line );

int 
main( int argc, char* argv[] )
{
  if ( argc != 2 )
    {
      std::cerr << "Usage: " << argv[1] << " text-file\n";
      return 0;
    }
  std::ifstream in_str( argv[1] );
  if ( !in_str )
    {
      std::cerr << "Failed to open " << argv[1] << '\n';
      return 0;
    }

  std::map< std::string, std::vector<int> > words_to_lines;
  std::string line;
  int line_number = 0;

  while ( std::getline( in_str, line ) )
    {
      ++ line_number;

      //  Break the string up into words
      std::vector<std::string> words = breakup_v2( line ); 

      //  Enter each word in the map
      for ( std::vector<std::string>::iterator p = words.begin(); p!= words.end(); ++p )
        {
          //  Find if the word is already in the map.  If it is not,
          //  create a new entry with an empty std::vector (by
          //  default) and add to the end of the std::vector

          std::map< std::string, std::vector<int> >::iterator map_itr = words_to_lines.find( *p );
          if ( map_itr == words_to_lines.end() )
            {
              words_to_lines[ *p ].push_back( line_number );  // could use insert here
            }
          
          //  If the word is in map, check the last entry to see if
          //  the line number is already there.  If it isn't, add it
          //  to the back of the std::vector.

          else if ( map_itr -> second . back() != line_number )
            {
              map_itr -> second . push_back( line_number );
            }
        }
    }

  //  Output each word on a single line, followed by the line numbers.

  for ( std::map< std::string, std::vector<int> >::iterator map_itr = words_to_lines.begin(); 
	map_itr != words_to_lines.end(); ++ map_itr )
    {
      std::cout << map_itr -> first << ":\t";
      for ( unsigned int i = 0; i < map_itr -> second . size(); ++i )
        std::cout << ( map_itr -> second )[ i ] << " ";
      std::cout << "\n";
    }

  return 0;
}


std::vector<std::string> breakup_v2( const std::string& line )
{
  std::vector<std::string> strings;
  std::string::const_iterator p = line.begin();
  std::string one_word;

  while ( p != line.end() )
    {
      //  Find the beginning of the next alphabetic string
      while ( p != line.end() && !isalpha( *p ) ) p ++;

      //  If haven't reached the end of the line
      if ( p != line.end() )
        {
          //  Restart the word with *p
          one_word.clear();
          one_word += tolower(*p);
          
          //  Add remaining letters
          for ( ++p; p != line.end() && isalpha(*p); ++p )
            one_word += tolower(*p);

          //  Add word to the vector of strings
          strings.push_back( one_word );
        }
    }

  return strings;
}

