// Übung zu Templates, STL vector, File-I/O, Exceptions:
// File mit zufällig permutierten Zeilen kopieren
// Fisher-Yates mit C++ Zufallsgenerator statt rand() / srand(time(NULL))
//
// Aufruf: file-perm-random infilename outfilename
//
// Klaus Kusche, 2018

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

using namespace std;

// ordne Vektor v (egal welcher Element-Typ) beliebig um
// Achtung: v muss "by reference" übergeben werden
// (wir wollen ja das v im Aufrufer umordnen, keine lokale Kopie davon!)
template <typename T>
void scramble(vector<T> &v)
{
  // Zufallszahlen-Generator-Objekt myRandGen anlegen,
  // Generator mit aktueller Zeit initialisieren (so wie srand(time(NULL))
  minstd_rand myRandGen(
      chrono::system_clock::now().time_since_epoch().count());
  // Alternativ: random_device myRandGen;

  // Algorithmus von Fisher-Yates
  // erzeugt eine zufällig Umordnung
  // liefert jede mögliche Umordnung mit gleicher Wahrscheinlichkeit
  for (int i = v.size() - 1; i > 0; --i) {
    // "Aufruf" useres myRandGen-Objektes liefert die nächste Zufallszahl
    // (einen unsigned int), gleicher Effekt wie rand()
    // Achtung: Zufallszahl pos muss zwischen 0 und i *einschließlich* liegen
    // (damit auch die "idente Vertauschung" vorkommen kann)
    // ==> Restrechnung mit (i + 1), nicht mit i
    int pos = myRandGen() % (i + 1);
    // vordefiniertes swap aus utility: Vertausche 2 Werte
    swap(v[i], v[pos]);
  }
}

// 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);
  }
  
  scramble(text);
  
  // 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);
}
