#include <stdio.h>
#include <stdlib.h>
#include <stdint.h> // fixed sized integer
#include <stdbool.h> // booleans

typedef struct Tile {
    uint32_t x;
    uint32_t y;
} Tile;

typedef enum ReadResult {
    RdOk,
    RdEof,
    RdErrIntegerOverflow,
    RdErrNonDigitCharacter,
    RdErrTooFewNumbers,
    RdErrTooManyNumbers,
} ReadResult;

typedef struct Field {
    Tile t_min;
    Tile t_max;
    uint32_t num_tiles;
} Field;

/** Parsen einer Kachel(Tile) aus einer Zeile von StdIn
 *
 * Die Zahlen werden in das uebergebene Struct pKachel geschrieben, sind aber
 * nur gueltig, wenn der Rueckgabewert `Ok` ist.
 */
ReadResult read_line(Tile* p_tile){
    int c = getchar();
    if (c == EOF) {
        return RdEof;
    }
    int num_numbers = 0;
    bool cur_whitespace = true;
    while(1) {
        c = getchar();
        if ('0' <= c && c <= '9') {
            if (cur_whitespace) {
                num_numbers++;
                cur_whitespace = false;
            }
            uint32_t *p_cur;
            switch(num_numbers) {
            case 1:
                p_cur = &p_tile->x;
                break;
            case 2:
                p_cur = &p_tile->y;
                break;
            default:
                return RdErrTooManyNumbers;
            }
            // 429496730 = 2^32 / 10, daher wenn `p_cur` groesser ist, wird ein
            // overflow erzeugt
            if (*p_cur > 429496729) {
                printf("Multiplizieren ueberlauf %u\n", *p_cur);
                return RdErrIntegerOverflow;
            }
            (*p_cur) *= 10;
            int digit = c - '0';
            if (*p_cur > UINT32_MAX - digit) {
                printf("Addieren ueberlauf %u\n", *p_cur);
                return RdErrIntegerOverflow;
            }
            (*p_cur) += digit;
        } else if (c == ' ') {
            if (cur_whitespace == true) {
                cur_whitespace = false;
            }
        } else if (c == '\n') {
            if (num_numbers == 2) {
                return RdOk;
            } else {
                return RdErrTooFewNumbers;
            }
        } else {
            return RdErrNonDigitCharacter;
        }
    }
    return RdOk;
}

/**
 *
 */
inline uint32_t min(uint32_t a, uint32_t b) {
    return a < b ? a : b;
}
inline uint32_t max(uint32_t a, uint32_t b) {
    return a > b ? a : b;
}

/** Liest das Input von StdIn und schreibt das Ergebnis in das uebergebene Feld
 *
 * Parameter:
 *  - Field* f pointer auf ein uninitialisiertes Feld, nach dem ausfuehren der
 *    Funktion ist das Feld initialisiert, wenn es sich um eine gueltige Eingabe
 *    handelt. `f->num_tiles` Ist im Fehlerfall die Zeile, in dem die Eingabe
 *    ungueltig ist.
 *
 * Return:
 *  - ReadResult:
 *     RdOk, falls das lesen erfolgreich war
 *     RdErr..., falls die eingabe ungueltig ist. f ist dann nicht vollstaendig
 *               initialisiert
 */
ReadResult parse_input(Field *f) {
    f->t_min.x = UINT32_MAX;
    f->t_min.y = UINT32_MAX;
    f->t_max.x = 0;
    f->t_max.y = 0;
    f->num_tiles = 0;

    while (1) {
        Tile t;
        ReadResult result = read_line(&t);
        f->num_tiles++;
        switch (result) {
        case RdOk:
            // Reichweite des Feldes ermitteln
            f->t_min.x = min(f->t_min.x, t.x);
            f->t_min.y = min(f->t_min.y, t.y);
            f->t_max.x = max(f->t_max.x, t.x);
            f->t_max.y = max(f->t_max.x, t.y);
            break;
        case RdEof:
            if (f->num_tiles == 1) {
                return RdEof;
            } else {
                return RdOk;
            }
        default: // error
            return result;
        }
    }
}

int main (int argc,
          char *argv[])
{
    Field f;
    ReadResult result = parse_input(&f);
    switch (result) {
    case RdOk:
        printf("Valider Input");
        printf("Anzahl: %d", f.num_tiles);
        printf("Range(%d, %d), (%d, %d)",
               f.t_min.x, f.t_min.y,
               f.t_max.x, f.t_max.y);
        printf("Range x: %d, %d",
               f.t_max.x - f.t_min.x,
               f.t_max.y - f.t_min.y);
        break;
    case RdEof:
        // bei leerer Eingabe nichts ausgeben
        return EXIT_SUCCESS;
    case RdErrIntegerOverflow:
        fprintf(stderr, "Error in line %d: too big integer\n", f.num_tiles);
        return EXIT_FAILURE;
    case RdErrNonDigitCharacter:
        fprintf(stderr, "Error in line %d: invalid character\n", f.num_tiles);
        return EXIT_FAILURE;
    case RdErrTooFewNumbers:
        fprintf(stderr, "Error in line %d: Too few numbers, expected exactly 2\n",
               f.num_tiles);
        return EXIT_FAILURE;
    case RdErrTooManyNumbers:
        fprintf(stderr, "Error in line %d: Too much numbers, expected exactly 2\n",
               f.num_tiles);
        return EXIT_FAILURE;

    }
    return EXIT_SUCCESS;
}