// Beispiel für Exceptions: Telefonverzeichnis
//
// Aufruf: except
//
// Klaus Kusche, 2011

#include <iostream>
#include <exception>   // für die Ableitung von den Standard-Exceptions
#include <cstdlib>     // für exit
#include <cctype>      // für isdigit

const int maxInputLen = 256;   // max. Zeilenlänge der Eingabe
const int maxElems = 10;       // max. Anzahl der gespeicherten Einträge

using namespace std;

// unser "atoi" mit exception, wenn txt keine Zahl darstellt:
// wirft das fehlerhafte oder leere txt (also einen const char *)
int textToNumber(const char *txt);

// eine eigene Exception-Klasse
// abgeleitet von den Standard-Exceptions
// wird geworfen, wenn das Array beim Speichern eines neuen Eintrags
// schon voll ist
class dataOverflow: public exception
{
  // "what" ist in exception vordefiniert und soll überschrieben werden:
  // Beschreibung der Exception
  virtual const char* what() const throw()
  {
    return "Too much data!";
  }
};

class telList {
public:
  telList(): elems(0) { }

  // fügt einen Eintrag zur telList dazu
  // wirft eine dataOverflow-Exception, wenn das Array schon voll ist
  void add(int telNr, const char *name)
  {
    if (elems == maxElems) throw dataOverflow();  // kein Platz mehr!
    numbers[elems] = telNr;
    names[elems] = name;
    ++elems;
  }

  // sucht die Nummer telNrText in der telList und returniert den Namen dazu
  // wirft einen const char * (indirekt aus textToNumber)
  // wenn telNrText etwas anderes als eine Zahl enthält
  // und einen int, wenn es keinen Eintrag mit Nummer telNrText findet
  const char *get(const char *telNrText)
  {
    int telNr = textToNumber(telNrText);   // könnte einen const char * werfen!

    for (int i = 0; i < elems; ++i) {
      if (numbers[i] == telNr) {
        return names[i];
      }
    }
    throw telNr;  // Nicht gefunden !
  }
  
private:
  int numbers[maxElems];         // Telefonnummern
  const char *names[maxElems];   // dazugehörige Namen
  int elems;                     // Anzahl gültiger Einträge
};

void frageSchleife(telList &list);

int main()
{
  telList list;

  try {
    list.add(112, "Rettung");
    list.add(9707, "NTA");
    list.add(32168, "Rosi");
    list.add(666, "Telefonseelsorge");
    list.add(1234567, "Privat");
    
    frageSchleife(list);
  }
  catch (const exception &e) {
    // fängt auch Objekte abgeleiteter Klassen, z.B. dataOverflow
    cerr << "Exception \"" << e.what() << "\" happened." << endl;
    exit(EXIT_FAILURE);
  }
  // die anderen beiden Exceptions werden schon in frageSchleife gefangen,
  // etwas anderes sollte nie geflogen kommen
  catch (...) {
    cerr << "Something unexpected happened!" << endl;
    exit(EXIT_FAILURE);
  }

  exit(EXIT_SUCCESS);
}

void frageSchleife(telList &list)
{
  char eingabe[maxInputLen];
  for (;;) {
    try {
      cout << "Was soll ich suchen? ";
      cin.getline(eingabe, sizeof(eingabe));
      cout << list.get(eingabe) << endl;
    }
    catch (const char *txt) {
      // wenn der Eingabetext keine Zahl ist, wird der Eingabetext geworfen
      if (txt[0] == '\0') return;  // Eingabe Leerstring ==> Programmende
      cout << "Eingabe \"" << txt << "\" ist keine Zahl." << endl;
    }
    catch (int telNr) {
      // wenn die Eingabe eine Zahl ist, aber nicht gefunden wird,
      // wird die Zahl geworfen
      cout << "Die Telefonnummer " << telNr << " wurde nicht gefunden." << endl;
    }
  }
}

int textToNumber(const char *txt)
{
  int result = 0;

  if (txt[0] == '\0') throw txt;       // Leerstring
  for (int i = 0; txt[i] != '\0'; ++i) {
    if (!isdigit(txt[i])) throw txt;   // Eingabetext enthält nicht-Ziffer
    result = result * 10 + (txt[i] - '0');
  }
  
  return result;
}
