/*! @file

    $Id$
*/
//       1         2         3         4         5         6         7         8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890

// Kompilation mit:
//   g++ fussnotensortierer.cxx -o fussnotensortierer -lmrw -lboost_regex
// ohne libmrw-c++ (siehe unten):
//   g++ fussnotensortierer.cxx -o fussnotensortierer -lboost_regex

#include <boost/regex.hpp>
#include <iostream>
#include <exception>

//==============================================================================
/* #include <mrw/file.hpp> // <- so, oder wie folgt: */
/* Kommentar entfernen, wenn mrw-c++ Library fehlt (Option -lmrw entfällt dann) */
#include <fstream>
namespace mrw {
  class File {
    public:
      //------------------------------------------------------------------------
      static std::string read(const std::string& filename)
          throw(std::exception) {
        std::string contents; // declare on top to allow compiler optimization
        std::ifstream is(filename.c_str());
        std::string::size_type sz(size(is, filename));
        contents.resize(sz); // make buffer long enough to store all
        is.read(contents.begin().operator->(), sz); // hack to get the buffer
        if (!is.good() && is.eof())
          throw std::invalid_argument("Cannot read file: '"+filename+"'");
        return contents;
      }
      //------------------------------------------------------------------------
      static std::string::size_type size(std::ifstream& file,
                                         const std::string& filename = "")
          throw(std::exception) {
        if (!file)
          throw std::invalid_argument("Cannot get size of file: '"
                                      +filename+"'");
        file.seekg(0, std::ios::end);
        std::string::size_type sz(file.tellg());
        file.seekg(0, std::ios::beg);
        if (!file)
          throw std::invalid_argument("Cannot get size of file: '"
                                      +filename+"'");
        return sz;
      }
  };
}
/*------------------------------------------------------------------------------
*/

//==============================================================================
//! Datei einlesen, Fussnoten sortieren und nach std::cout schreiben.
void sort(char const*const file) {
  //............................................................................
  // Datei vollständig in String einlesen
  std::string contents(mrw::File::read(file));
  //............................................................................
  // Erste Hälfte: Bis zur Limite Fussnoten numerieren
  const std::string DELIM("\n@footnotes:\n"); // Delimiter-Markierung
  std::map<std::string, unsigned long> mapper; // number remapping
  unsigned long num(1); // actual number
  std::string::iterator von(contents.begin());
  boost::match_results<std::string::iterator> match;
  for (; boost::regex_search(von,
                             contents.end(),
                             match, // suche alle "[x]"
                             boost::regex("\\[[0-9]+\\]|"+DELIM));
       von=match[0].second)
    if (match[0].str()==DELIM) { // Delimiter-Markierung erreicht
      // Ausgabe von übrigem unbearbeitetem Text
      std::copy(von, match[0].second, std::ostream_iterator<char>(std::cout));
      break; // Abbruch und unten in zweiter Hälfte weiter
    } else { // Nummer gefunden, bei Bedarf neuen Eintrag erzeugen
      if (mapper.insert(std::make_pair(match.str(), num)).second) ++num;
      // Ausgabe von unbearbeitetem Text vorher und umgeschriebener Nummer
      std::copy(von, match[0].first, std::ostream_iterator<char>(std::cout));
      std::cout<<"["<<mapper[match.str()]<<"]";
    }
  //............................................................................
  // Zweite Hälfte: Intern Abbildung von Nummern auf Text (Fussnoten) aufbauen
  std::map<unsigned long, std::string> fntexts; // number to text mapper
  for (von=match[0].second;
       boost::regex_search(von,
                           contents.end(),
                           match, // suche "[x] ... \n"
                           boost::regex("(\\[[0-9]+\\])([^\n]*)(\n|$)"));
       von=match[0].second) {
    std::map<std::string, unsigned long>::iterator it(mapper.find(match[1]));
    if (it==mapper.end()) // Nummer gefunden, die noch nicht abgebildet wird
      it = mapper.insert(std::make_pair(match[1].str(), num++)).first;
    fntexts[it->second] = match[2].str(); // Text zu Nummer zuweisen
    std::copy(von, match[0].first, std::ostream_iterator<char>(std::cout));
  }
  //............................................................................
  // Zweite Hälfte: Fussnoten und falls vorhanden Überbleibsel ausgeben.
  std::copy(von, contents.end(), std::ostream_iterator<char>(std::cout));
  for (std::map<unsigned long, std::string>::iterator it(fntexts.begin());
       it!=fntexts.end(); ++it)
    std::cout<<"["<<it->first<<"]"<<it->second<<std::endl;
}

//==============================================================================
//! Ruft nur für alle Dateien in der Argumentliste sort() auf
int main(const int argv, char const*const*const argc) {
  for (int i(1); i<argv; ++i) sort(argc[i]);
  return 0;
}
