/*! @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;
}