// Übung zu Exceptions: Zeilenweise Mittelwerte
//
// Aufruf: mittelw
//
// Klaus Kusche, 2011

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

using namespace std;

double getNum(bool mustBeText);

class SyntaxError
{
  // Zum Ausgeben eines SyntaxError-Objektes (gibt die Member aus)
  // Muss die Member lesen können, daher friend
  friend ostream &operator<<(ostream &outF, const SyntaxError &err);
  
  public:
    SyntaxError(const string &errMsg, const string &errInput, int errLine) :
      msg(errMsg), input(errInput), line(errLine) {}
      
  private:
    const string msg, input;  // Fehlermeldungstext & falsche Input-Zeile
    int line;                 // Zeilennummer
};

ostream &operator<<(ostream &outFile, const SyntaxError &err)
{
  outFile << "Syntax error at line " << err.line << ": " << err.msg << endl;
  outFile << err.input << endl;

  return outFile;
}

// Eine Zeile einlesen
// Wenn mustBeText true ist: Muss eine #-Zeile sein
double getNum(bool mustBeText)
{
  static int lineNr = 0;   // über Aufrufe hinweg zählen & merken!
  string input;
  const char *beg;         // Der zu input gehörende C-String
  char *end;               // Pointer auf das erste Zeichen nach der Zahl
  double val;

  ++lineNr;
  getline(cin, input);
  beg = input.c_str();

  if ((*beg == '\0') || (*beg == '#')) {  //  Leerzeile oder #-Zeile
    throw input;
  }

  if (mustBeText) throw SyntaxError("Text line expected", input, lineNr);

  // strtod setzt Ausgabe-Parameter end auf erstes unerkanntes Zeichen
  val = strtod(beg, &end);

  // end zeigt auf String-Anfang: strtod hat nichts gefunden!
  if (beg == end) throw SyntaxError("No number found", input, lineNr);

  // Kommen hinter der Zahl (ab end) nur Zwischenräume?
  for ( ; *end != '\0'; ++end) {
    if (!isspace(*end)) throw SyntaxError("Text following after value", input, lineNr);
  }

  return val;
}

int main()
{
  string name = "";  // Aktuelle Überschrift
  double sum = 0;    // Summe & Anzahl der Messwerte
  int cnt = 0;

  for (;;) {
    try {
      sum += getNum(name == "");
      ++cnt;   // Wegen Exceptions: *Nach* dem getNum-Aufruf
               // (nur wenn getNum erfolgreich), nicht davor!
    }
    catch (const SyntaxError &error) {
      cerr << error;
    }
    catch (const string &input) {
      // File-Ende oder neue Überschrift
      if (name != "") {
        // Wenn es eine alte Überschrift gibt:
        // Alten Mittelwert ausgeben
        cout << name.substr(1) << ": ";
        if (cnt == 0) {
          cout << "Keine Messwerte" << endl;
        } else {
          cout << "Mittelwert " << sum / cnt << endl;
        }
      }
      if (input == "") {
        // Leerzeile = Ende
        exit(EXIT_SUCCESS);
      } else {
        // #-Zeile: Text merken, neue Berechnung anfangen
        name = input;
        sum = 0;
        cnt = 0;
      }
    }
  }
}
