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

DEFINE_TEST(memset)
{
    START_TEST(memset);

    char test[10];
    char* ptr = memset(test, 0, 10);

    EXPECT_EQ(ptr, test);

    for (int i = 0; i < 10; i++) { EXPECT_EQ(test[i], 0); }

    ptr = memset(test, 42, 10);

    EXPECT_EQ(ptr, test);

    for (int i = 0; i < 10; i++) { EXPECT_EQ(test[i], 42); }

    TEST_SUCCESS();
}

DEFINE_TEST(memcpy)
{
    START_TEST(memcpy);

    char buf[20] = "Nothing is going on";
    const char* str = "Something is going!";

    char* ptr = memcpy(buf, str, 20);

    EXPECT_EQ(ptr, buf);

    for (int i = 0; i < 20; i++) { EXPECT_EQ(buf[i], str[i]); }

    const char* new = "Well...";

    ptr = memcpy(buf, new, 7);

    EXPECT_EQ(ptr, buf);

    for (int i = 0; i < 7; i++) { EXPECT_EQ(buf[i], new[i]); }
    for (int i = 7; i < 20; i++) { EXPECT_EQ(buf[i], str[i]); }

    TEST_SUCCESS();
}

DEFINE_TEST(memchr)
{
    START_TEST(memchr);

    char buf[20] = "abcdefghijklmnopqrs";

    char* ptr = memchr(buf, 'z', 20);

    EXPECT_EQ(ptr, NULL);

    ptr = memchr(buf, 'd', 20);

    EXPECT_EQ(ptr, buf + 3);

    ptr = memchr(buf, 's', 20);

    EXPECT_EQ(ptr, buf + 18);

    TEST_SUCCESS();
}

DEFINE_TEST(memcmp)
{
    START_TEST(memcmp);

    char buf[20] = "abcdefghijklmnopqrs";
    char buf2[20] = "abcdefghijklmnopqrp";

    int val = memcmp(buf, buf, 20);

    EXPECT_EQ(val, 0);

    val = memcmp(buf, buf2, 20);

    EXPECT(val > 0);

    val = memcmp(buf2, buf, 20);

    EXPECT(val < 0);

    TEST_SUCCESS();
}

DEFINE_TEST(memmove)
{
    START_TEST(memmove);

    char buf[20] = "Nothing is going on";
    const char* str = "Something is going!";

    char* ptr = memmove(buf, str, 20);

    EXPECT_EQ(ptr, buf);

    for (int i = 0; i < 20; i++) { EXPECT_EQ(buf[i], str[i]); }

    const char* new = "Well...";

    ptr = memmove(buf, new, 7);

    EXPECT_EQ(ptr, buf);

    for (int i = 0; i < 7; i++) { EXPECT_EQ(buf[i], new[i]); }
    for (int i = 7; i < 20; i++) { EXPECT_EQ(buf[i], str[i]); }

    char newbuf[16] = "part_a is part_b";
    char result[16] = "is part_b part_b";

    ptr = memmove(newbuf, newbuf + 7, 9);

    EXPECT_EQ(ptr, newbuf);

    EXPECT(memcmp(newbuf, result, 16) == 0); // we have tested memcmp at this point

    TEST_SUCCESS();
}

DEFINE_TEST(strlen)
{
    START_TEST(strlen);

    const char* str = "Hello, World!";

    size_t len = strlen(str);

    EXPECT_EQ(len, 13);

    char null[] = {'\0'};

    len = strlen(null);

    EXPECT_EQ(len, 0);

    TEST_SUCCESS();
}

DEFINE_TEST(strnlen)
{
    START_TEST(strnlen);

    const char* str = "What is going on?";

    size_t len = strnlen(str, 20);

    EXPECT_EQ(len, 17);

    len = strnlen(str, 15);

    EXPECT_EQ(len, 15);

    char buf[] = {'H', 'e', 'l', 'l', 'o'};

    len = strnlen(buf, sizeof(buf));

    EXPECT_EQ(len, sizeof(buf));

    TEST_SUCCESS();
}

DEFINE_TEST(strcmp)
{
    START_TEST(strcmp);

    const char* str1 = "Hello, World!";
    const char* str2 = "Hello, Planet!";

    int rc = strcmp(str1, str1);

    EXPECT_EQ(rc, 0);

    rc = strcmp(str1, str2);

    EXPECT(rc > 0);

    rc = strcmp(str2, str1);

    EXPECT(rc < 0);

    TEST_SUCCESS();
}

DEFINE_TEST(strncmp)
{
    START_TEST(strncmp);

    const char* str1 = "Hello, World!";
    const char* str2 = "Hello, Planet!";

    int rc = strncmp(str1, str1, 14);

    EXPECT_EQ(rc, 0);

    rc = strncmp(str1, str2, 14);

    EXPECT(rc > 0);

    rc = strncmp(str2, str1, 14);

    EXPECT(rc < 0);

    rc = strncmp(str1, str2, 6);

    EXPECT_EQ(rc, 0);

    TEST_SUCCESS();
}

DEFINE_TEST(strcspn)
{
    START_TEST(strcspn);

    const char* str = "This string has vowels";
    const char* vowels = "aeiou";

    size_t len = strcspn(str, vowels);

    EXPECT_EQ(len, 2);

    str = "WWWWW";
    len = strcspn(str, vowels);

    EXPECT_EQ(len, 5);

    TEST_SUCCESS();
}

DEFINE_TEST(strspn)
{
    START_TEST(strspn);

    const char* str = "This is a test string";
    const char* accept = "This ";

    size_t len = strspn(str, accept);

    EXPECT_EQ(len, 8);

    str = "WWWWW";
    len = strspn(str, accept);

    EXPECT_EQ(len, 0);

    str = "This is hi";
    len = strspn(str, accept);

    EXPECT_EQ(len, 10);

    TEST_SUCCESS();
}

DEFINE_TEST(strchr)
{
    START_TEST(strchr);

    const char* str = "Hello, world!";

    char* ptr = strchr(str, 'l');

    EXPECT_EQ(ptr, str + 2);

    ptr = strchr(str, 'u');

    EXPECT_EQ(ptr, NULL);

    ptr = strchr(str, '!');

    EXPECT_EQ(ptr, str + 12);

    TEST_SUCCESS();
}

DEFINE_TEST(strchrnul)
{
    START_TEST(strchrnul);

    const char* str = "Hello, world!";

    char* ptr = strchrnul(str, 'l');

    EXPECT_EQ(ptr, str + 2);

    ptr = strchrnul(str, 'u');

    EXPECT_EQ(ptr, str + 13);

    ptr = strchrnul(str, '!');

    EXPECT_EQ(ptr, str + 12);

    TEST_SUCCESS();
}

DEFINE_TEST(strrchr)
{
    START_TEST(strrchr);

    const char* str = "Hello, world!";

    char* ptr = strrchr(str, 'l');

    EXPECT_EQ(ptr, str + 10);

    ptr = strrchr(str, 'u');

    EXPECT_EQ(ptr, NULL);

    ptr = strrchr(str, '!');

    EXPECT_EQ(ptr, str + 12);

    TEST_SUCCESS();
}

DEFINE_TEST(strpbrk)
{
    START_TEST(strpbrk);

    const char* str = "Hello, world!";
    const char* vowels = "aeiou";

    char* ptr = strpbrk(str, vowels);

    EXPECT_EQ(ptr, str + 1);

    str = "There are more vowels";
    ptr = strpbrk(str, vowels);

    EXPECT_EQ(ptr, str + 2);

    str = "zzzzzz";
    ptr = strpbrk(str, vowels);

    EXPECT_EQ(ptr, NULL);

    TEST_SUCCESS();
}

DEFINE_TEST(strdup)
{
    START_TEST(strdup);

    const char* orig = "Well hello there!";

    char* copy = strdup(orig);
    if (!copy) { TEST_NOT_SURE(copy); }

    EXPECT(memcmp(orig, copy, 17) == 0);

    free(copy);

    TEST_SUCCESS();
}

DEFINE_TEST(strndup)
{
    START_TEST(strndup);

    const char* orig = "Well hello there!";

    char* copy = strndup(orig, 17);
    if (!copy) { TEST_NOT_SURE(copy); }

    EXPECT(memcmp(orig, copy, 17) == 0);

    free(copy);

    copy = strndup(orig, 12);
    if (!copy) { TEST_NOT_SURE(copy); }

    EXPECT_NOT_EQ(memcmp(orig, copy, 14),
                  0); // FIXME: This is undefined behaviour and the memory could also be by chance identical.

    free(copy);

    TEST_SUCCESS();
}