// Templates: Zeichen- und Wortstatistik eines Textes
// Variante mit zwei Arrays, unsortiert
//
// Aufruf: textcnt filename
//
// Klaus Kusche, 2011

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cctype>

using namespace std;

// unser Zähler-Array kann max. size verschiedene Werte vom Typ T zählen
template <typename T, int size>
class ValCntArray
{
  public:
    ValCntArray() : used(0) {}

    void count(const T &val);
    void print() const;

  private:
    int used;        // Anzahl der schon benutzten Werte in den Arrays
    T vals[size];    // die Werte ...
    int cnts[size];  // ... und wie oft jeder bisher vorgekommen sind
};

template <typename T, int size>
void ValCntArray<T, size>::count(const T &val)
{
  // zuerst schauen, ob Wert "val" schon im Array steht
  for (int i = 0; i < used; ++i) {
    if (vals[i] == val) {
      ++cnts[i];  // Wenn ja: Zähler erhöhen und fertig
      return;
    }
  }

  // val nicht gefunden ==> anhängen!
  if (used == size) {
    // kein Platz mehr
    cerr << "More than " << size << " values to count" << endl;
    exit(EXIT_FAILURE);
  }
  vals[used] = val;
  cnts[used] = 1;
  ++used;  // wir haben jetzt ein Element mehr...
}

template <typename T, int size>
void ValCntArray<T, size>::print() const
{
  for (int i = 0; i < used; ++i) {
    cout << vals[i] << ": " << cnts[i] << endl;
  }
}

const int LINE_WIDTH = 4095;   // max. Zeilenlänge des Eingabe-Files

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

  ifstream inf(argv[1]);  // Eingabe-File
  if (!inf) {
    cerr << argv[0] << ": Cannot open " << argv[1] << " for reading" << endl;
    exit(EXIT_FAILURE);
  }

  ValCntArray<char, 30> charCnt;     // zählt die einzelnen Buchstaben
  ValCntArray<string, 1000> wordCnt; // zählt die ganzen Wörter
  ValCntArray<int, 50> lenCnt;       // zählt die Wortlängen

  char line[LINE_WIDTH + 1]; // +1 für \0
  
  while (inf.getline(line, sizeof(line))) {
    int len = strlen(line);
    // an welchem Index in line beginnt das aktuelle Wort?
    // wenn wortStart -1 ist, sind wir nicht innerhalb eines Wortes
    int wordStart = -1;
    // die Schleife geht absichtlich bis <= len statt < len:
    // wir machen noch einen extra Umlauf für das '\0' selbst,
    // damit auch das letzte Wort der Zeile sicher gezählt wird.
    for (int i = 0; i <= len; ++i) {
      if (isalpha(line[i])) {
        // ein Buchstabe...
        line[i] = tolower(line[i]);  // in Kleinbuchstaben verwandeln
        charCnt.count(line[i]);      // und zählen
        if (wordStart == -1) {       // wenn wir nicht in einem Wort waren:
          wordStart = i;             // hier beginnt ein neues Wort!
        }
      } else {
        // kein Buchstabe
        if (wordStart != -1) {
          // wir waren in einem Wort: Teilstring ausschneiden und zählen
          string s(&(line[wordStart]), i - wordStart);
          wordCnt.count(s);
          lenCnt.count(s.length());
          wordStart = -1;  // wir sind nicht mehr in einem Wort
        }
      }
    }
  }

  charCnt.print();
  cout << "===" << endl;
  wordCnt.print();
  cout << "===" << endl;
  lenCnt.print();

  // wenn fail() true ist aber eof() nicht, ist ein Fehler aufgetreten
  if (!inf.eof()) {
    cerr << argv[0] << ": Cannot read " << argv[1] <<
            " (line too long?)" << endl;
    exit(EXIT_FAILURE);
  }
  
  exit(EXIT_SUCCESS);
}
