/*
 * Copyright (C) Tristan Franssen, <tristanfranssen@strawhats.nl>.
 *
 * This software is licensed under the Apache License, Version 2.0 (the "License");
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License in the file LICENSE or at
 * http://www.apache.org/licenses/LICENSE-2.0
 */
#include "unit/file.h"

#include <cosms-core/file.h>

#include <stdlib.h>
#include <string.h>

COSMS_CORE_TEST_TEST(file_open_small_file_read,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/small-file.txt", COSMS_CORE_FILE_MODE_READ);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }
    
    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_open_large_file_read,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/large-file.txt", COSMS_CORE_FILE_MODE_READ);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }
    
    return NULL;
)

COSMS_CORE_TEST_TEST(file_open_non_existing_file_read,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "non-existing-file.cosms", COSMS_CORE_FILE_MODE_READ);

    if (error == COSMS_CORE_FILE_OK) {
        error = cosms_core_file_close(&file);
        if (error != COSMS_CORE_FILE_OK) {
            return cosms_core_file_error_string(error);
        }

        return cosms_core_file_error_string(error);
    }

    if (error != COSMS_CORE_FILE_NOT_FOUND) {
        return cosms_core_file_error_string(error);
    }
    
    return NULL;
)

COSMS_CORE_TEST_TEST(file_open_small_file_write,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/small-file.txt", COSMS_CORE_FILE_MODE_WRITE);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_open_large_file_write,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/large-file.txt", COSMS_CORE_FILE_MODE_WRITE);
    if (error != COSMS_CORE_FILE_OK) {
         return cosms_core_file_error_string(error);
    }
    
    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_open_small_file_append,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/small-file.txt", COSMS_CORE_FILE_MODE_APPEND);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }
    
    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_open_large_file_append,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/large-file.txt", COSMS_CORE_FILE_MODE_APPEND);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }
    
    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_open_small_file_read_write,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/small-file.txt", COSMS_CORE_FILE_MODE_READ | COSMS_CORE_FILE_MODE_WRITE);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_open_large_file_read_write,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/large-file.txt", COSMS_CORE_FILE_MODE_READ | COSMS_CORE_FILE_MODE_WRITE);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }
    
    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }
    
    return NULL;
)

COSMS_CORE_TEST_TEST(file_close, 
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/small-file.txt", COSMS_CORE_FILE_MODE_READ);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }
    
    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_close_non_existing_file, 
    struct cosms_core_file file;
    file.native_file = 0;

    CosmsCoreFileError error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_INVALID_FILE) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_size_small_file, 
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/small-file.txt", COSMS_CORE_FILE_MODE_READ);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }
            
    unsigned long long size = 0;
    error = cosms_core_file_get_size(&file, &size);
    if (error != COSMS_CORE_FILE_OK) {
        cosms_core_file_close(&file);
        return cosms_core_file_error_string(error);
    }
    
    if (size != 13) {
        cosms_core_file_close(&file);
        return "result does not match expected result";
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }
    
    return NULL;
)

COSMS_CORE_TEST_TEST(file_size_large, 
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/large-file.txt", COSMS_CORE_FILE_MODE_READ);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }
    
    unsigned long long size = 0;
    error = cosms_core_file_get_size(&file, &size);
    if (error != COSMS_CORE_FILE_OK) {
        cosms_core_file_close(&file);
        return cosms_core_file_error_string(error);
    }
    
    if (size != 5242880000ULL) {
        cosms_core_file_close(&file);
        return "result does not match expected result";
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_size_non_existing_file, 
    struct cosms_core_file file;
    file.native_file = 0;
        
    unsigned long long size = 0;
    CosmsCoreFileError error = cosms_core_file_get_size(&file, &size);
    if (error != COSMS_CORE_FILE_INVALID_FILE) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_read_small_file, 
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/small-file.txt", COSMS_CORE_FILE_MODE_READ);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    unsigned int bytes_to_read = 13; 
    unsigned int bytes_read = 0;

    char buffer[14];
    error = cosms_core_file_read(&file, buffer, bytes_to_read, &bytes_read);
    if (error != COSMS_CORE_FILE_OK) {
        cosms_core_file_close(&file);
        return cosms_core_file_error_string(error);
    }

    if (bytes_to_read != bytes_read || strcmp(buffer, "Hello, World!") != 0) {
        cosms_core_file_close(&file);
        return "result does not match expected result";
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_read_large_file, 
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/large-file.txt", COSMS_CORE_FILE_MODE_READ);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    unsigned long long bytes_to_read = 5242880000ULL;
    unsigned long long bytes_read = 0;

    char *buffer = NULL;
    error = cosms_core_file_read_all(&file, &buffer, &bytes_read);
    if (error != COSMS_CORE_FILE_OK) {
        cosms_core_file_close(&file);
        return cosms_core_file_error_string(error);
    }

    char *expected_result = (char*)malloc((bytes_to_read + 1) * sizeof(char));
    if (expected_result == NULL) {
        cosms_core_file_close(&file);
        free(buffer);

        return "Failed to allocate memory";
    }

    for (unsigned long long index = 0; index < bytes_to_read; index += 1) {
        expected_result[index] = 'a';
    }

    expected_result[bytes_to_read] = '\0';
    if (bytes_to_read != bytes_read || strcmp(buffer, expected_result) != 0) {
        cosms_core_file_close(&file);

        free(expected_result);
        free(buffer);

        return "result does not match expected result";
    }

    free(expected_result);
    free(buffer);

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_read_non_existing_file, 
    struct cosms_core_file file;
    file.mode = COSMS_CORE_FILE_MODE_READ;
    file.native_file = 0;

    unsigned int bytes_to_read = 13;
    unsigned int bytes_read = 0;

    char buffer[14];
    CosmsCoreFileError error = cosms_core_file_read(&file, buffer, bytes_to_read, &bytes_read);
    if (error != COSMS_CORE_FILE_FAILED_TO_READ) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_read_full_non_existing_file,
    struct cosms_core_file file;
    file.mode = COSMS_CORE_FILE_MODE_READ;
    file.native_file = 0;

    unsigned long long bytes_read = 0;
    char *buffer = NULL;
    CosmsCoreFileError error = cosms_core_file_read_all(&file, &buffer, &bytes_read);
    if (error != COSMS_CORE_FILE_COULD_NOT_READ_SIZE) {
        if (error == COSMS_CORE_FILE_OK) {
            free(buffer);
        }

        return cosms_core_file_error_string(error);
    }
    
    return NULL;
)

COSMS_CORE_TEST_TEST(file_read_file_using_wrong_mode,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/small-file.txt", COSMS_CORE_FILE_MODE_APPEND);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    unsigned int bytes_to_read = 13;
    unsigned int bytes_read = 0;

    char buffer[14];
    error = cosms_core_file_read(&file, buffer, bytes_to_read, &bytes_read);
    if (error != COSMS_CORE_FILE_INVALID_OPERATION) {
        cosms_core_file_close(&file);
        return cosms_core_file_error_string(error);
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_read_full_file_using_wrong_mode,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/small-file.txt", COSMS_CORE_FILE_MODE_APPEND);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    unsigned long long bytes_read = 0;
    char *buffer = NULL;
    error = cosms_core_file_read_all(&file, &buffer, &bytes_read);
    if (error != COSMS_CORE_FILE_INVALID_OPERATION) {
        cosms_core_file_close(&file);
        if (error == COSMS_CORE_FILE_OK) {
            free(buffer);
        }

        return cosms_core_file_error_string(error);
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_write_small_file,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/small-write-file.txt", COSMS_CORE_FILE_MODE_WRITE | COSMS_CORE_FILE_MODE_CREATE);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    unsigned int bytes_to_write = 13;
    unsigned int bytes_written = 0;

    char *write_buffer = "Hello, World!";
    error = cosms_core_file_write(&file, write_buffer, bytes_to_write, &bytes_written);
    if (error != COSMS_CORE_FILE_OK) {
        cosms_core_file_close(&file);
        return cosms_core_file_error_string(error);
    }

    if (bytes_to_write != bytes_written) {
        cosms_core_file_close(&file);
        return "did not write all bytes to file";
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    error = cosms_core_file_open(&file, "tests/data/small-write-file.txt", COSMS_CORE_FILE_MODE_READ);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    char read_buffer[14] = { 0 };
    error = cosms_core_file_read(&file, read_buffer, bytes_to_write, NULL);
    if (error != COSMS_CORE_FILE_OK) {
        cosms_core_file_close(&file);
        return cosms_core_file_error_string(error);
    }

    if (strcmp(read_buffer, write_buffer) != 0) {
        cosms_core_file_close(&file);
        return "result does not match expected result";
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_write_large_file,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/large-write-file.txt", COSMS_CORE_FILE_MODE_WRITE | COSMS_CORE_FILE_MODE_CREATE);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    unsigned long long bytes_to_write = 5242880000ULL;
    char *write_buffer = (char*)malloc((bytes_to_write + 1) * sizeof(char));
    for (unsigned long long index = 0; index < bytes_to_write; index += 1) {
        write_buffer[index] = 'a';
    }

    write_buffer[bytes_to_write] = '\0';
    error = cosms_core_file_write_all(&file, write_buffer, bytes_to_write);
    if (error != COSMS_CORE_FILE_OK) {
        cosms_core_file_close(&file);
        free(write_buffer);

        return cosms_core_file_error_string(error);
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        free(write_buffer);
        return cosms_core_file_error_string(error);
    }

    error = cosms_core_file_open(&file, "tests/data/large-write-file.txt", COSMS_CORE_FILE_MODE_READ);
    if (error != COSMS_CORE_FILE_OK) {
        free(write_buffer);
        return cosms_core_file_error_string(error);
    }

    char *read_buffer = NULL;
    error = cosms_core_file_read_all(&file, &read_buffer, NULL);
    if (error != COSMS_CORE_FILE_OK) {
        cosms_core_file_close(&file);
        free(write_buffer);

        return cosms_core_file_error_string(error);
    }

    if (strcmp(read_buffer, write_buffer) != 0) {
        cosms_core_file_close(&file);

        free(read_buffer);
        free(write_buffer);

        return "result does not match expected result";
    }

    free(read_buffer);
    free(write_buffer);

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_write_append_small_file,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/small-write-file.txt", COSMS_CORE_FILE_MODE_WRITE | COSMS_CORE_FILE_MODE_APPEND);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    unsigned int bytes_to_write = 13;
    unsigned int bytes_written = 0;

    char *write_buffer = "Hello, World!";
    error = cosms_core_file_write(&file, write_buffer, bytes_to_write, &bytes_written);
    if (error != COSMS_CORE_FILE_OK) {
        cosms_core_file_close(&file);
        return cosms_core_file_error_string(error);
    }

    if (bytes_to_write != bytes_written) {
        cosms_core_file_close(&file);
        return "did not write all bytes to file";
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    error = cosms_core_file_open(&file, "tests/data/small-write-file.txt", COSMS_CORE_FILE_MODE_READ);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    unsigned int bytes_to_read = 26;
    char read_buffer[27] = { 0 };
    error = cosms_core_file_read(&file, read_buffer, bytes_to_read, NULL);
    if (error != COSMS_CORE_FILE_OK) {
        cosms_core_file_close(&file);
        return cosms_core_file_error_string(error);
    }

    if (strcmp(read_buffer, "Hello, World!Hello, World!") != 0) {
        cosms_core_file_close(&file);
        return "result does not match expected result";
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_write_append_large_file,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/large-write-file.txt", COSMS_CORE_FILE_MODE_WRITE | COSMS_CORE_FILE_MODE_APPEND);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    unsigned long long bytes_to_write = 2;
    char *write_buffer = "bb";
    error = cosms_core_file_write_all(&file, write_buffer, bytes_to_write);
    if (error != COSMS_CORE_FILE_OK) {
        cosms_core_file_close(&file);
        return cosms_core_file_error_string(error);
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    error = cosms_core_file_open(&file, "tests/data/large-write-file.txt", COSMS_CORE_FILE_MODE_READ);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    unsigned long long bytes_to_read = 5242880002ULL;
    unsigned long long bytes_read = 0;
    char *read_buffer = NULL;
    error = cosms_core_file_read_all(&file, &read_buffer, &bytes_read);
    if (error != COSMS_CORE_FILE_OK) {
        cosms_core_file_close(&file);
        return cosms_core_file_error_string(error);
    }

    char *expected_result = (char*)malloc((bytes_to_read + 1) * sizeof(char));
    for (unsigned long long index = 0; index < bytes_to_read; index += 1) { 
        expected_result[index] = 'a';
    }

    expected_result[bytes_to_read - 2] = 'b';
    expected_result[bytes_to_read - 1] = 'b';
    expected_result[bytes_to_read] = '\0';

    if (strcmp(read_buffer, expected_result) != 0) {
        cosms_core_file_close(&file);

        free(expected_result);
        free(read_buffer);
        
        return "result does not match expected result";
    }

    free(expected_result);
    free(read_buffer);

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_write_using_wrong_mode,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/small-write-file.txt", COSMS_CORE_FILE_MODE_READ);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    unsigned int bytes_to_write = 13;
    unsigned int bytes_written = 0;

    char *write_buffer = "Hello, World!";
    error = cosms_core_file_write(&file, write_buffer, bytes_to_write, &bytes_written);
    if (error != COSMS_CORE_FILE_INVALID_OPERATION) {
        cosms_core_file_close(&file);
        return cosms_core_file_error_string(error);
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        free(write_buffer);
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_write_full_file_using_wrong_mode,
    struct cosms_core_file file;
    CosmsCoreFileError error = cosms_core_file_open(&file, "tests/data/large-write-file.txt", COSMS_CORE_FILE_MODE_READ);
    if (error != COSMS_CORE_FILE_OK) {
        return cosms_core_file_error_string(error);
    }

    unsigned long long bytes_to_write = 5242880000ULL;
    char *write_buffer = (char*)malloc((bytes_to_write + 1) * sizeof(char));
    for (unsigned long long index = 0; index < bytes_to_write; index += 1) {
        write_buffer[index] = 'a';
    }

    write_buffer[bytes_to_write] = '\0';
    error = cosms_core_file_write_all(&file, write_buffer, bytes_to_write);
    if (error != COSMS_CORE_FILE_INVALID_OPERATION) {
        cosms_core_file_close(&file);
        free(write_buffer);

        return cosms_core_file_error_string(error);
    }

    error = cosms_core_file_close(&file);
    if (error != COSMS_CORE_FILE_OK) {
        free(write_buffer);
        return cosms_core_file_error_string(error);
    }

    return NULL;
)

COSMS_CORE_TEST_TEST(file_delete,
    return NULL;
)
