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

#define BUFFER_SIZE 100
#define FILE_HEADER_STRING "__info2_image_file_format__"


// DONE Implementieren Sie geeignete Hilfsfunktionen für das Lesen der Bildserie aus einer Datei
unsigned int    readStatusInfo      (FILE *const source, GrayScaleImageSeries *const series, char *const headerString, int const sizeToRead, int const amountToRead);
void            readImagedata       (FILE *const source, GrayScaleImageSeries *const series, int const amountToRead);
unsigned int    checkHeaderString   (const char *const header);


// DONE Vervollständigen Sie die Funktion readImages unter Benutzung Ihrer Hilfsfunktionen
GrayScaleImageSeries *readImages(const char *path)
{
    GrayScaleImageSeries    *series                                     = NULL;
    FILE                    *readSource                                 = 0;
    const unsigned int      sizeOfStausInfoElementsInBytes              = sizeof(unsigned short);
    const unsigned int      amountOfStatusInfoToRead                    = 1;
    unsigned int            numberOfBytesToRead                         = 0;
    unsigned int            expectedHeader                              = 0;
    char                    headerString[sizeof(FILE_HEADER_STRING)]    = "";


    readSource = fopen(path, "rb");
    
    if (readSource != NULL)
    {
        series              = calloc            (amountOfStatusInfoToRead, sizeof(GrayScaleImageSeries));
        series->images      = calloc            (amountOfStatusInfoToRead, sizeof(GrayScaleImage));

        numberOfBytesToRead = readStatusInfo    (readSource, series, headerString, sizeOfStausInfoElementsInBytes, amountOfStatusInfoToRead);
        expectedHeader      = checkHeaderString (headerString);

        series->images      = realloc           (series->images,    series->count * sizeof(GrayScaleImage));
        series->labels      = calloc            (series->count,     sizeof(&(series->labels)));
        
        if (expectedHeader)
        {
            for (int i = 0; i < series->count; i++)
            {
                series->images[i].buffer    = calloc(numberOfBytesToRead, sizeof(unsigned char));
            }
            
            for (int i = 0; i < series->count; i++)
            {
                series->images[i].width     = series->images->width;
                series->images[i].height    = series->images->height;
            }
            
            readImagedata(readSource, series, numberOfBytesToRead);
        }
        else
        {
            series = NULL;
        }
        
        fclose(readSource);
    }
    
    
    return series;
}


// DONE Vervollständigen Sie die Funktion clearSeries, welche eine Bildserie vollständig aus dem Speicher freigibt
void clearSeries(GrayScaleImageSeries * series)
{
    int i = 0;
    int j = 0;


    for (i = 0; i < series->count; i++)
    {
        if (i >= 0)
        {
            for (j = 0; j < series->images[i].width * series->images[i].height; j++)
            {
                series->images[i].buffer[j] = 0;
            }
        }
        
        series->labels[i]           = 0;
        series->images[i].width     = 0;
        series->images[i].height    = 0;  
    }
    
    for (i = 0; i < series->count; i++)
    {
        free(series->images[i].buffer);
        series->images[i].buffer = NULL;
    }
    
    free(series->labels);
    series->labels  = NULL;
    free(series->images);
    series->images  = NULL;
    free(series);
    series          = NULL;
}


//  reads headerString, pictureCount, pictureWidth, pictureHight
unsigned int readStatusInfo(FILE *const source, GrayScaleImageSeries *const series, char *const headerString, int const sizeToRead, int const amountToRead)
{
    unsigned int bytesToRead = 0;


    fread(headerString,                 sizeof(FILE_HEADER_STRING) - 1,     amountToRead, source);
    fread(&(series->count),             sizeToRead,                         amountToRead, source);
    fread(&(series->images->width),     sizeToRead,                         amountToRead, source);
    fread(&(series->images->height),    sizeToRead,                         amountToRead, source);

    bytesToRead = (series->images->width) * (series->images->height);


    return bytesToRead;
}


//  reads the imagebytes and the label of all images
void readImagedata(FILE *const source, GrayScaleImageSeries *const series, int const amountToRead)
{
    int i           = 0;


    for (i = 0; i < series->count ; i++)
    {       
        fread(&series->images[i].buffer[0], sizeof(*series->images->buffer), amountToRead,              source);
        fread(&series->labels[i],           sizeof(*series->images->buffer), sizeof(*series->labels),   source);
    }
}


// checks if the read headerString matches the expected headerString
unsigned int checkHeaderString(const char *const header)
{
    int i                                           = 0;
    int notIdenticall                               = 0;
    char expectedHeader[sizeof(FILE_HEADER_STRING)] = FILE_HEADER_STRING;


    for (i = 0; i < sizeof(FILE_HEADER_STRING); i++)
    {
        if (header[i] != expectedHeader[i])
        {
            notIdenticall = 1;
        }
    }
    

    return !notIdenticall;
}

