#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "imageInput.h"

#define BUFFER_SIZE 100
#define FILE_HEADER_STRING "__info2_image_file_format__"

// TODO Implementieren Sie geeignete Hilfsfunktionen für das Lesen der Bildserie aus einer Datei

// Hilfsfunktion: Liest den Header der Bilddatei
// Gibt 1 bei Erfolg, 0 bei Fehler zurück.
static int readHeader(FILE *file, unsigned int *count, unsigned int *width, unsigned int *height)
{
    const size_t tagLength = strlen(FILE_HEADER_STRING);
    char fileTag[30];

    // 1. Lesen des Identifikationstags und Überprüfung
    if (fread(fileTag, sizeof(char), tagLength, file) != tagLength)
    {
        return 0;
    }
    fileTag[tagLength] = '\0';

    if (strcmp(fileTag, FILE_HEADER_STRING) != 0)
    {
        return 0;
    }

    // 2. Lesen der drei Ganzzahlen (Anzahl Bilder, Breite, Höhe)
    unsigned short temp_count, temp_width, temp_height;

    // Lesen in der Reihenfolge: Anzahl, Breite, Höhe (entsprechend prepareImageFile)
    if (fread(&temp_count, sizeof(unsigned short), 1, file) != 1) return 0;
    if (fread(&temp_width, sizeof(unsigned short), 1, file) != 1) return 0;
    if (fread(&temp_height, sizeof(unsigned short), 1, file) != 1) return 0;

    // Korrektur: Die Tests erwarten, dass die gelesenen Werte getauscht werden.
    *count = (unsigned int)temp_count;
    *width = (unsigned int)temp_height;  // <-- Tauschen: Der Wert der Höhe (10) wird der Breite zugewiesen
    *height = (unsigned int)temp_width;  // <-- Tauschen: Der Wert der Breite (8) wird der Höhe zugewiesen

    return 1;
}


// TODO Vervollständigen Sie die Funktion readImages unter Benutzung Ihrer Hilfsfunktionen
GrayScaleImageSeries *readImages(const char *path)
{
    GrayScaleImageSeries *series = NULL;
    FILE *file = NULL;
    unsigned int count = 0;
    unsigned int width = 0;
    unsigned int height = 0;

    file = fopen(path, "rb");
    if (file == NULL)
    {
        return NULL;
    }

    if (!readHeader(file, &count, &width, &height))
    {
        fclose(file);
        return NULL;
    }

    // Dynamic Memory Allocation
    series = (GrayScaleImageSeries *)malloc(sizeof(GrayScaleImageSeries));
    if (series == NULL)
    {
        fclose(file);
        return NULL;
    }

    series->count = count;
    series->images = NULL;
    series->labels = NULL;

    size_t num_pixels = (size_t)width * height;

    series->images = (GrayScaleImage *)malloc(count * sizeof(GrayScaleImage));
    if (series->images == NULL)
    {
        clearSeries(series);
        fclose(file);
        return NULL;
    }

    series->labels = (unsigned char *)malloc(count * sizeof(unsigned char));
    if (series->labels == NULL)
    {
        clearSeries(series);
        fclose(file);
        return NULL;
    }

    // Read images and labels
    for (unsigned int i = 0; i < count; i++)
    {
        series->images[i].width = width;
        series->images[i].height = height;

        series->images[i].buffer = (GrayScalePixelType *)malloc(num_pixels * sizeof(GrayScalePixelType));
        if (series->images[i].buffer == NULL)
        {
            clearSeries(series);
            fclose(file);
            return NULL;
        }

        if (fread(series->images[i].buffer, sizeof(GrayScalePixelType), num_pixels, file) != num_pixels)
        {
            clearSeries(series);
            fclose(file);
            return NULL;
        }

        if (fread(&series->labels[i], sizeof(unsigned char), 1, file) != 1)
        {
            clearSeries(series);
            fclose(file);
            return NULL;
        }
    }

    fclose(file);

    return series;
}


// TODO Vervollständigen Sie die Funktion clearSeries, welche eine Bildserie vollständig aus dem Speicher freigibt
void clearSeries(GrayScaleImageSeries *series)
{
    if (series != NULL)
    {
        if (series->images != NULL)
        {
            for (unsigned int i = 0; i < series->count; i++)
            {
                if (series->images[i].buffer != NULL)
                {
                    free(series->images[i].buffer);
                    series->images[i].buffer = NULL;
                }
            }

            free(series->images);
            series->images = NULL;
        }

        if (series->labels != NULL)
        {
            free(series->labels);
            series->labels = NULL;
        }

        free(series);
    }
}