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

#define MAXWORD 100 // Maximale Wortlänge

// Definition eines Baumknotens für ein Wort
typedef struct TreeNode {
    char *word;                // Das gespeicherte Wort
    int count;                 // Wie oft das Wort vorkam
    struct TreeNode *left;     // Linker Teilbaum (Wörter, die alphabetisch davor kommen)
    struct TreeNode *right;    // Rechter Teilbaum (Wörter, die alphabetisch danach kommen)
} TreeNode_t;

// Funktion zum Einfügen eines Wortes in den Baum
// Falls das Wort schon existiert, wird der Zähler erhöht
TreeNode_t* insert(TreeNode_t *root, const char *word) {
    if (root == NULL) {
        // Neuer Knoten, falls das Wort noch nicht im Baum ist
        TreeNode_t *newNode = malloc(sizeof(TreeNode_t));
        if (!newNode) {
            perror("Speicherfehler");
            exit(EXIT_FAILURE);
        }
        newNode->word = strdup(word); // Wort kopieren und speichern
        newNode->count = 1;           // Erstes Vorkommen
        newNode->left = newNode->right = NULL; // Noch keine Kinder
        return newNode;
    }

    int cmp = strcmp(word, root->word); // Alphabetischer Vergleich
    if (cmp == 0) {
        // Wort schon vorhanden: Zähler erhöhen
        root->count++;
    } else if (cmp < 0) {
        // Wort ist alphabetisch VOR dem aktuellen Knoten: links einfügen
        root->left = insert(root->left, word);
    } else {
        // Wort ist alphabetisch NACH dem aktuellen Knoten: rechts einfügen
        root->right = insert(root->right, word);
    }
    return root;
}

// Inorder-Ausgabe: Gibt die Wörter alphabetisch sortiert mit Häufigkeit aus
void printTree(TreeNode_t *root) {
    if (root == NULL)
        return;
    printTree(root->left); // Erst linker Teilbaum
    printf("%-12s : %d\n", root->word, root->count); // Dann aktueller Knoten
    printTree(root->right); // Dann rechter Teilbaum
}

// Gibt den belegten Speicher des Baumes wieder frei
void freeTree(TreeNode_t *root) {
    if (root == NULL)
        return;
    freeTree(root->left);  // Erst linker Teilbaum
    freeTree(root->right); // Dann rechter Teilbaum
    free(root->word);      // Erst das Wort freigeben
    free(root);            // Dann den Knoten selbst
}

// Prüft, ob ein Zeichen ein Buchstabe ist (a-z, A-Z)
int isLetter(char c) {
    return isalpha((unsigned char)c);
}

// Liest Wörter aus stdin, zählt sie und gibt die Statistik aus
void readTextAndPrintStats() {
    char word[MAXWORD]; // Zwischenspeicher für das aktuelle Wort
    int index = 0;      // Position im Wort
    int c;              // Aktuelles Zeichen
    TreeNode_t *tree = NULL; // Wurzel des Baumes

    // Zeichenweise Einlesen bis Dateiende (EOF)
    while ((c = getchar()) != EOF) {
        if (isLetter(c)) {
            // Nur Buchstaben werden akzeptiert, alles in Kleinbuchstaben
            if (index < MAXWORD - 1)
                word[index++] = tolower(c);
        } else if (index > 0) {
            // Wortende erreicht (kein Buchstabe mehr)
            word[index] = '\0'; // String abschließen
            tree = insert(tree, word); // Wort in Baum einfügen
            index = 0; // Für das nächste Wort zurücksetzen
        }
    }
    // Falls am Ende noch ein Wort übrig ist (z.B. Datei endet ohne Leerzeichen)
    if (index > 0) {
        word[index] = '\0';
        tree = insert(tree, word);
    }

    printTree(tree);    // Statistik ausgeben (alphabetisch sortiert)
    freeTree(tree);     // Speicher aufräumen
}

// Hauptfunktion: Startet das Programm
int main() {
    readTextAndPrintStats();
    return 0;
}

// Kompilieren: gcc -o 28.6.3_wortstat 28.6.3_wortstat.c

// Ausführen:   28.6.3_wortstat.exe < test.txt