#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(3 * sizeof(unsigned int) + 3*sizeof(headerString), amountOfStatusInfoToRead);
        series->images          = calloc(2 * sizeof(unsigned int) + sizeof(headerString),   amountOfStatusInfoToRead);
        
        numberOfBytesToRead     = readStatusInfo(readSource, series, headerString, sizeOfStausInfoElementsInBytes, amountOfStatusInfoToRead);
        
        expectedHeader          = checkHeaderString(headerString);

        printf("llu %llu\n", series->count * numberOfBytesToRead * sizeof(GrayScalePixelType));
        series->images->buffer  = calloc((series->count) * numberOfBytesToRead, sizeof(GrayScalePixelType));
        series->labels          = calloc((series->count),                       sizeof(&(series->labels)));

        if (expectedHeader)
        {
            // reallocate memory so that each image width can be saved seperately
            series->images      = realloc(series->images, series->count * (2 * sizeof(unsigned int) + sizeof(headerString)));
            
            // for loop was previously running with i < series->count, wich caused programm to crash.
            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;

    
    // Write NULL into all memory spaces
    for (i = 0; i < ((series->count) * (series->images->width) * (series->images->height) / 4); i++)
    {   
        *(series->images->buffer + i * 4 * (series->images->width) * (series->images->height)) = '\0';
    }
    
    for (i = 0; i < (series->count); i++)
    {
        *(series->labels + i) = '\0';
    }
    
    series->count           = 0;
    series->images->width   = 0;
    series->images->height  = 0;

    // Closse all allocated memory
    // AND write NULL into every pointer
    // so they can't be accessed
    free(series->images->buffer);
    series->images->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 amountOfBytes)
{
    int i = 0;


    for (i = 0; i < series->count ; i++)
    {
        fread(&(series->images->buffer[i]), sizeof(*series->images->buffer), amountOfBytes,             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;
}

