// Morsezeichen-Dekodierung
//
// Aufruf: morse string
//
// Klaus Kusche, 2011

#include <stdio.h>
#include <stdlib.h>

// Typ eines Baumknotens
typedef struct _knoten {
  char zeichen;           // Das Zeichen, das dem Pfad zu diesem Knoten entspricht
                          // '\0', wenn dieser Knoten keinem Zeichen entspricht
  struct _knoten *punkt;  // Subbaum, wenn als nächstes ein Punkt folgt
  struct _knoten *strich; // Subbaum, wenn als nächstes ein Strich folgt
} baumKnoten;

typedef struct {
  const char zeichen;
  const char *code;
} codeDef;

// unvollständig
codeDef codeTab[] = {
  { 'a', ".-" },
  { 'b', "-..." },
  { 'c', "-.-." },
  { 'd', "-.." },
  { 'e', "." },
  { 'f', "..-." },
  { 'g', "--." },
  { 'h', "...." },
  { 'i', ".." },
  { 'j', ".---" },
  { 'k', "-.-" },
  { 'l', ".-.." },
  { 'm', "--" },
  { 'n', "-." },
  { 'o', "---" },
  { 'p', ".--." },
  { 'q', "--.-" },
  { 'r', ".-." },
  { 's', "..." },
  { 't', "-" },
  { 'u', "..-" },
  { 'v', "...-" },
  { 'w', ".--" },
  { 'x', "-..-" },
  { 'y', "-.--" },
  { 'z', "--.." },
  { 'ä', ".-.-" },
  { 'ö', "---." },
  { 'ü', "..--" },
  { 'C', "----" },
  { '1', ".----" },
  { '2', "..---" },
  { '3', "...--" },
  { '4', "....-" },
  { '5', "....." },
  { '6', "-...." },
  { '7', "--..." },
  { '8', "---.." },
  { '9', "----." },
  { '0', "-----" },
  { '.', ".-.-.-" },
  { ',', "--..--" },
  { ':', "---..." },
  { '?', "..--.." },
  { '(', "-.--.-" },
  { ';', "-.-.-." },
  { '-', "-....-" },
  { '+', ".-.-." },
  { '/', "-..-." },
  { '\'', ".----." },
  { '=', "-...-" },
  { '\"', ".-..-." },
  { '!', "..--." },
  { ' ', "" }           // bewirkt die Ausgabe von ' ' bei "leerem" Morsezeichen
};

// die Wurzel = Anfang aller Morsezeichen
baumKnoten *baum;

// für Fehlermeldungen
const char *progName;

// lege einen neuen, leeren Knoten an
baumKnoten *neuerKnoten(void);
// verwandle die Code-Tabelle in einen Baum
void initCodeBaum(void);
// dekodiere str
void decode(const char *str);

// lege einen neuen, leeren Knoten an
baumKnoten *neuerKnoten(void)
{
  baumKnoten *p;

  p = (baumKnoten *) malloc(sizeof(baumKnoten));
  if (p == NULL) {
    fprintf(stderr, "%s: Zu wenig Speicher\n", progName);
    exit(EXIT_FAILURE);
  }
  p->zeichen = '\0';
  p->punkt = p->strich = NULL;
    
  return p;
}

// verwandle die Code-Tabelle in einen Baum
void initCodeBaum(void)
{

  int i, j;
  char c;
  baumKnoten *p;

  baum = neuerKnoten();
  
  for (i = 0; i < sizeof(codeTab) / sizeof(codeTab[0]); ++i) {
    // gehe das Morsezeichen zeichenweise durch und suche den entsprechenden Baumknoten
    // triffst du dabei einen NULL-Pointer, hänge dort einen neuen Knoten an
    p = baum;
    for (j = 0; ;++j) {
      c = codeTab[i].code[j];
      if (c == '\0') {
        // Ende des Morsezeichens ==> wir sind am richtigen Knoten
        // dieser Knoten sollte noch leer sein, wir tragen unser Zeichen ein
        if (p->zeichen != '\0') {
          fprintf(stderr, "%s: Doppelter Code in den Initialisierungsdaten (%s)\n",
                  progName, codeTab[i].code);
          exit(EXIT_FAILURE);
        }
        p->zeichen = codeTab[i].zeichen;
        break;
      } else if (c == '.') {
        if (p->punkt == NULL) p->punkt = neuerKnoten();
        p = p->punkt;
      } else if (c == '-') {
        if (p->strich == NULL) p->strich = neuerKnoten();
        p = p->strich;
      } else {
        fprintf(stderr, "%s: Falsches Zeichen in den Initialisierungsdaten (%c)\n",
                progName, c);
        exit(EXIT_FAILURE);
      }
    }
  }
}

// dekodiere str
void decode(const char *str)
{
  baumKnoten *p;
  int i;
  char c;
  
  // gehe den Morsecode zeichenweise durch und suche den entsprechenden Baumknoten
  p = baum;
  for (i = 0; ;++i) {
    c = str[i];
    if ((c == ' ' ) || (c == '\0')) {
      // Morsecode zu Ende, wir sind am richtigen Baumknoten
      // wenn der '\0' ist, gibt es kein Zeichen, das diesem Morsecode entspricht
      if (p->zeichen == '\0') {
        fprintf(stderr, "%s: Ungültiger Morsecode beim %d. Zeichen\n",
                progName, i + 1);
        exit(EXIT_FAILURE);
      }
      putchar(p->zeichen);
      // wenn noch ein Morsezeichen folgt, fange wieder oben an, sonst: fertig
      if (c == ' ') p = baum;
      else break;
    } else if (c == '.') p = p->punkt;
    else if (c == '-') p = p->strich;
    else {
      fprintf(stderr, "%s: Ungültiges Zeichen %c in der Eingabe\n",
              progName, c);
      exit(EXIT_FAILURE);
    }
    // es gibt keinen Teilbaum, der diesem Morsecode entspricht
    if (p == NULL) {
      fprintf(stderr, "%s: Ungültiger Morsecode beim %d. Zeichen\n",
              progName, i + 1);
      exit(EXIT_FAILURE);
    }
  }

  putchar('\n');
}

int main(int argc, const char *argv[])
{

  if (argc != 2) {
    fprintf(stderr, "Usage: %s morse_string\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  progName = argv[0];

  initCodeBaum();

  decode(argv[1]);

  exit(EXIT_SUCCESS);
}
