#include "imageInput.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "mnistVisualization.h"
#include "neuralNetwork.h"

#define LABEL "__info2_image_file_format__"
#define LABEL_SIZE (sizeof(LABEL))
#define DEBUG 2

// Hilfsfunktion: liest Header aus Datei
static int read_header(uint16_t *size, uint16_t *width, uint16_t *height, char *header_label, FILE *file) {

    if (fread(header_label, 1, LABEL_SIZE - 1, file) != LABEL_SIZE - 1)
    return 0;

    header_label[LABEL_SIZE - 1] = '\0';

    if (fread(size, sizeof(uint16_t), 1, file) != 1) return 0;
    if (fread(width, sizeof(uint16_t), 1, file) != 1) return 0;
    if (fread(height, sizeof(uint16_t), 1, file) != 1) return 0;

    return 1;
}

// Liest alle Bilder nacheinander ein
static int read_images(GrayScaleImageSeries *series, FILE *file, unsigned int width, unsigned int height, unsigned int count) {

    for (unsigned int i = 0; i < count; i++) {
        //Bildbreite und -höhe setzen 
        series->images[i].width = width;
        series->images[i].height = height;
        // Anzahl der Pixel (= Anzahl Bytes im Graustufenbild) 
        size_t image_bytes = width * height;
        // Anlegen des Bildspeichers
        series->images[i].buffer =
        malloc(image_bytes * sizeof(GrayScalePixelType));

        if (!series->images[i].buffer) {
            fprintf(stderr, "Speicherfehler bei Einzelbild.\n");

            // Bereits eingelesene Bilder freigeben
            for (unsigned int j = 0; j < i; j++)
            free(series->images[j].buffer);

            return 0;
        }
        // Bildpixel einlesen
        if (fread(series->images[i].buffer, 1, image_bytes, file) != image_bytes)
        return 0;
        // Label einlesen
        if (fread(&series->labels[i], 1, 1, file) != 1)
        return 0;
    }

    return 1;
}

// Hauptfunktion: Bilddatei einlesen
GrayScaleImageSeries *readImages(const char *path) {

    FILE *file = fopen(path, "rb");
    if (!file) {
        fprintf(stderr, "Fehler: Datei konnte nicht geoeffnet werden.\n");
        return NULL;
    }
    // Speicher für die gesamte Serie anlegen
    GrayScaleImageSeries *series = malloc(sizeof(GrayScaleImageSeries));
    if (!series) {
        fprintf(stderr, "Speicherfehler für Bilderliste.\n");
        fclose(file);
        return NULL;
    }

    uint16_t size16 = 0, width16 = 0, height16 = 0;
    char header_label[LABEL_SIZE];

    if (!read_header(&size16, &width16, &height16, header_label, file)) {
        fprintf(stderr, "Fehler beim Lesen des Headers.\n");
        free(series);
        fclose(file);
        return NULL;
    }
    // Prüfen auf Korrektheit der Header-Datei
    if (strcmp(header_label, LABEL) != 0) {
        fprintf(stderr, "Falsches Dateiformat.\n");
        free(series);
        fclose(file);
        return NULL;
    }

    // Prüfen auf ungültige Werte
    if (size16 == 0 || width16 == 0 || height16 == 0) {
        fprintf(stderr, "Ungueltige Headerwerte.\n");
        free(series);
        fclose(file);
        return NULL;
    }

    series->count = size16;
    // Speicher für Bilder reservieren
    series->images = malloc(size16 * sizeof(GrayScaleImage));
    if (!series->images) {
        fprintf(stderr, "Speicherfehler für Bildarray.\n");
        free(series);
        fclose(file);
        return NULL;
    }
    // Speicher für Labels reservieren
    series->labels = malloc(size16 * sizeof(unsigned char));
    if (!series->labels) {
        fprintf(stderr, "Speicherfehler für Labelarray.\n");
        free(series->images);
        free(series);
        fclose(file);
        return NULL;
    }
    // Einlesen der Bilder
    if (!read_images(series, file, width16, height16, size16)) {
        fprintf(stderr, "Fehler beim Lesen der Bilder.\n");

        // Nur das freigeben, was wirklich existiert 
        for (unsigned int i = 0; i < size16; i++)
        free(series->images[i].buffer);

        free(series->labels);
        free(series->images);
        free(series);
        fclose(file);
        return NULL;
    }

    fclose(file);
    return series;
}

//Speicher freigeben

void clearSeries(GrayScaleImageSeries *series) {
    if (!series) return;

    for (int i = 0; i < series->count; i++)
    free(series->images[i].buffer);

    free(series->images);
    free(series->labels);
    free(series);
}