
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "unity.h"
#include "imageInput.h"
#define FILE_HEADER_STRING "__info2_image_file_format__"


static void prepareImageFile(const char *path, unsigned short int width, unsigned short int height, unsigned int short numberOfImages, unsigned char label)
{
    FILE *file = fopen(path, "wb");

    if(file != NULL)
    {
        const char *fileTag = "__info2_image_file_format__";
        GrayScalePixelType *zeroBuffer = (GrayScalePixelType *)calloc(numberOfImages * width * height, sizeof(GrayScalePixelType));

        if(zeroBuffer != NULL)
        {
            fwrite(fileTag, sizeof(fileTag[0]), strlen(fileTag), file);
            fwrite(&numberOfImages, sizeof(numberOfImages), 1, file);
            fwrite(&width, sizeof(width), 1, file);
            fwrite(&height, sizeof(height), 1, file);

            for(int i = 0; i < numberOfImages; i++)
            {
                fwrite(zeroBuffer, sizeof(GrayScalePixelType), width * height, file);
                fwrite(&label, sizeof(unsigned char), 1, file);
            }

            free(zeroBuffer);
        }

        fclose(file);
    }
}

static void prepareImageFileIncorrectTag(const char *path, unsigned short int width, unsigned short int height, unsigned int short numberOfImages, unsigned char label)
{
    FILE *file = fopen(path, "wb");

    if(file != NULL)
    {
        const char *fileTag = "__info2_image_file_format";
        GrayScalePixelType *zeroBuffer = (GrayScalePixelType *)calloc(numberOfImages * width * height, sizeof(GrayScalePixelType));

        if(zeroBuffer != NULL)
        {
            fwrite(fileTag, sizeof(fileTag[0]), strlen(fileTag), file);
            fwrite(&numberOfImages, sizeof(numberOfImages), 1, file);
            fwrite(&width, sizeof(width), 1, file);
            fwrite(&height, sizeof(height), 1, file);

            for(int i = 0; i < numberOfImages; i++)
            {
                fwrite(zeroBuffer, sizeof(GrayScalePixelType), width * height, file);
                fwrite(&label, sizeof(unsigned char), 1, file);
            }

            free(zeroBuffer);
        }

        fclose(file);
    }
}

void test_readImagesFailsOnIncompleteHeader(void) {
    const char *path = "testIncompleteHeader.info2";
    FILE *file = fopen(path, "wb");
    if (file) {
        fwrite(FILE_HEADER_STRING, 1, strlen(FILE_HEADER_STRING), file);

        unsigned char incompleteHeader[2] = {0x01, 0x00};
        fwrite(incompleteHeader, 1, 2, file);
        fclose(file);
    }
    TEST_ASSERT_NULL(readImages(path));
    remove(path);
}

void test_readImagesReturnsCorrectNumberOfImages(void)
{
    GrayScaleImageSeries *series = NULL;
    const unsigned short expectedNumberOfImages = 2;
    const char *path = "testFile.info2";
    prepareImageFile(path, 8, 8, expectedNumberOfImages, 1);
    series = readImages(path);
    TEST_ASSERT_NOT_NULL(series);
    TEST_ASSERT_EQUAL_UINT16(expectedNumberOfImages, series->count);
    clearSeries(series);
    remove(path);
}

void test_readImagesReturnsCorrectImageWidth(void)
{
    GrayScaleImageSeries *series = NULL;
    const unsigned short expectedWidth = 10;
    const char *path = "testFile.info2";
    prepareImageFile(path, expectedWidth, 8, 2, 1);
    series = readImages(path);
    TEST_ASSERT_NOT_NULL(series);
    TEST_ASSERT_NOT_NULL(series->images);
    TEST_ASSERT_EQUAL_UINT16(2, series->count);
    TEST_ASSERT_EQUAL_UINT16(expectedWidth, series->images[0].width);
    TEST_ASSERT_EQUAL_UINT16(expectedWidth, series->images[1].width);
    clearSeries(series);
    remove(path);
}

void test_readImagesReturnsCorrectImageHeight(void)
{
    GrayScaleImageSeries *series = NULL;
    const unsigned short expectedHeight = 10;
    const char *path = "testFile.info2";
    prepareImageFile(path, 8, expectedHeight, 2, 1);
    series = readImages(path);
    TEST_ASSERT_NOT_NULL(series);
    TEST_ASSERT_NOT_NULL(series->images);
    TEST_ASSERT_EQUAL_UINT16(2, series->count);
    TEST_ASSERT_EQUAL_UINT16(expectedHeight, series->images[0].height);
    TEST_ASSERT_EQUAL_UINT16(expectedHeight, series->images[1].height);
    clearSeries(series);
    remove(path);
}

void test_readImagesReturnsCorrectLabels(void)
{
    const unsigned char expectedLabel = 15;

    GrayScaleImageSeries *series = NULL;
    const char *path = "testFile.info2";
    prepareImageFile(path, 8, 8, 2, expectedLabel);
    series = readImages(path);
    TEST_ASSERT_NOT_NULL(series);
    TEST_ASSERT_NOT_NULL(series->labels);
    TEST_ASSERT_EQUAL_UINT16(2, series->count);
    for (int i = 0; i < 2; i++) {
        TEST_ASSERT_EQUAL_UINT8(expectedLabel, series->labels[i]);
    }
    clearSeries(series);
    remove(path);
}

void test_readImagesReturnsNullOnNotExistingPath(void)
{
    const char *path = "testFile.txt";
    remove(path);
    TEST_ASSERT_NULL(readImages(path));
}

void test_readImagesFailsOnWrongFileTag(void)
{
    const char *path = "testFile.info2";
    FILE *file = fopen(path, "w");
    if(file != NULL)
    {
        fprintf(file, "some_tag                  ");
        fclose(file);
        TEST_ASSERT_NULL(readImages(path));
    }
    remove(path);
}

void test_openImageFileIncorrectTag(void)
{
    GrayScaleImageSeries *series = NULL;
    const char *path = "testFileIncorrectTag.info2";
    prepareImageFileIncorrectTag(path, 8, 8, 1, 1);
    series = readImages(path);
    TEST_ASSERT_NULL(series);
    clearSeries(series);
    remove(path);
}

void test_clearSeriesFreesMemory(void)
{
  	// Dummy-Series erstellen
    GrayScaleImageSeries *series = (GrayScaleImageSeries*)malloc(sizeof(GrayScaleImageSeries));
    series->count = 2;

	// Labels allokieren
    series->labels = (unsigned char*)malloc(series->count * sizeof(unsigned char));
    for (unsigned int i = 0; i < series->count; i++) {
        series->labels[i] = (unsigned char)(i + 1);
    }

    // Images allokieren
    series->images = (GrayScaleImage*)malloc(series->count * sizeof(GrayScaleImage));
    for (unsigned int i = 0; i < series->count; i++) {
        series->images[i].width = 4;
        series->images[i].height = 4;
        series->images[i].buffer = (GrayScalePixelType*)malloc(series->images[i].width * series->images[i].height);

        for (unsigned int j = 0; j < series->images[i].width * series->images[i].height; j++){
          series->images[i].buffer[j] = 0;
        }
    }

    // clearSeries aufrufen
    clearSeries(series);

    // Test
    TEST_PASS();
}

void setUp(void) {
    // Falls notwendig, kann hier Vorbereitungsarbeit gemacht werden
}

void tearDown(void) {
    // Hier kann Bereinigungsarbeit nach jedem Test durchgeführt werden
}

int main()
{
    UNITY_BEGIN();

    printf("\n============================\nImage input tests\n============================\n");
    RUN_TEST(test_readImagesReturnsCorrectNumberOfImages);
    RUN_TEST(test_readImagesReturnsCorrectImageWidth);
    RUN_TEST(test_readImagesReturnsCorrectImageHeight);
    RUN_TEST(test_readImagesReturnsCorrectLabels);
    RUN_TEST(test_readImagesReturnsNullOnNotExistingPath);
    RUN_TEST(test_readImagesFailsOnWrongFileTag);
    RUN_TEST(test_openImageFileIncorrectTag);
    RUN_TEST(test_readImagesFailsOnIncompleteHeader);
    RUN_TEST(test_clearSeriesFreesMemory);

    return UNITY_END();
}