// Übung zu Templates, STL vector, File-I/O, Exceptions:
// File mit zufällig permutierten Zeilen kopieren
// Mit STL shuffle statt eigenem Fisher-Yates
//
// Aufruf: file-perm-shuffle infilename outfilename
//
// Klaus Kusche, 2018

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
// für shuffle
#include <algorithm>
// für die Zufallszahlen-Generatoren
#include <random>
// für die Zeit-Funktionen
#include <chrono>


using namespace std;

// kopiere die Datei in_name in die Datei out_name
// mische dabei die Zeilen zufällig durcheinander
void copy_scrambled(string in_name, string out_name)
{
  ifstream in_file(in_name);
  if (!in_file) {
    // C++ string-Objekte kann man mit + aneinanderhängen,
    // funktioniert auch mit C String-Konstanten
    throw "Opening " + in_name + " for reading failed";
  }
  ofstream out_file(out_name);
  if (!out_file) {
    throw "Opening " + out_name + " for writing failed";
  }
  string line;
  // Der STL-Container vector ist wie ein "selbstwachsendes" Array
  // wir wissen die Anzahl der Elemente ja vorher nicht
  // Jedes Element ist eine Zeile unseres Inputs, also Elementtyp string
  vector<string> text;
  
  // getline liest genau eine Zeile
  // (>> kann ja nur wortweise lesen, nicht zeilenweise)
  while (getline(in_file, line)) {
    // push_back: Hänge einen Wert hinten an den Vektor an
    text.push_back(line);
  }

  // shuffle will Iteratoren auf den Bereich, den es umordnen soll
  // wir wollen den gesamten Container umordnen,
  // also übergeben wir dessen begin- und end-Iteratoren
  //
  // Weiters will es ein Objekt, das einen Zufallszahlen-Generator repräsentiert
  // (keine einzelne Zufallszahl!)
  // Wir übergeben ihm ein temporätes Object der Klasse random_device
  // (repräsentiert den Hardware-Zufallszahlen-Generator) oder minstd_rand
  // (repräsentiert einen ganz einfachen Zufallszahlen-Generator wie bei rand())
  //
  // shuffle ruft dann jedesmal intern diesen Generator auf,
  // wenn es eine Zufallszahl braucht
  //
  // random_device liefert von sich aus jedesmal andere Zufallszahlen,
  // bei allen anderen Zufalls-Generatoren muss man im Konstruktor
  // einen Startwert angeben (wie bei srand(time(NULL)) )
  // wir verwenden die aktuelle Systemzeit:
  // chrono::system_clock::now().time_since_epoch().count()
  //  
  //shuffle(text.begin(), text.end(), random_device());
  shuffle(text.begin(), text.end(),
          minstd_rand(chrono::system_clock::now().time_since_epoch().count()));
  
  // C++-11 Container-Schleife:
  // elem wird der Reihe nach auf die einzelnen Elemente von text gesetzt
  for (const string &elem : text) {
    out_file << elem << endl;
  }
  // Konventionelle Alternative:
  //for (int i = 0; i < text.size(); ++i) {
  //  out_file << text[i] << endl;
  //}
}

int main(int argc, const char *argv[])
{
  if (argc != 3) {
    cerr << "Usage: " << argv[0] << " infilename outfilename" << endl;
    exit(EXIT_FAILURE);
  }

  try {
    copy_scrambled(argv[1], argv[2]);
  }
  catch (const string &err) {
    cerr << err << endl;
    exit(EXIT_FAILURE);
  }
  
  exit(EXIT_SUCCESS);
}
