#include "config.h"

#include <cmocka.h>
#include <cmocka_private.h>

#include <stdlib.h>

int mock_function(void);
void mock_function_call_times(size_t times, int expectedValue);

int mock_function(void)
{
    return (int) mock();
}

intmax_t mock_function_int(void);
intmax_t mock_function_int(void)
{
    return mock_int();
}

uintmax_t mock_function_uint(void);
uintmax_t mock_function_uint(void)
{
    return mock_uint();
}

uint64_t mock_function_uint64(void);
uint64_t mock_function_uint64(void)
{
    return (uint64_t)mock_uint();
}

float mock_function_float(void);
float mock_function_float(void)
{
    return mock_float();
}

double mock_function_double(void);
double mock_function_double(void)
{
    return mock_double();
}

const char *mock_function_ptr(void);
const char *mock_function_ptr(void)
{
    return mock_ptr_type_checked(const char *);
}

char *mock_function_ptr_unchecked(void);
char *mock_function_ptr_unchecked(void)
{
    return mock_ptr_type(char *);
}

void mock_function_call_times(size_t times, int expectedValue)
{
    size_t i;
    for (i = 0u; i < times; ++i)
    {
        assert_int_equal(expectedValue, mock_function());
    }
}

static void test_will_return_maybe_for_no_calls(void **state)
{
    (void) state;

    will_return_maybe(mock_function, 32);
}

static void test_will_return_maybe_for_one_mock_call(void **state)
{
    int value;

    (void) state;

    value = rand();
    will_return_maybe(mock_function, value);
    mock_function_call_times(1u, value);
}

static void test_will_return_maybe_for_more_than_one_call(void **state)
{
    int value;
    size_t numberOfCalls;
    (void)state;

    value = rand();
    numberOfCalls = (size_t) ((rand()) % 20 + 2);
    will_return_maybe(mock_function, value);
    mock_function_call_times(numberOfCalls, value);
}

static void test_will_return_int(void **state)
{
    intmax_t value;

    (void)state; /* unused */

    value = rand();
    will_return_int(mock_function_int, value);
    assert_int_equal(value, mock_function_int());
}

static void test_will_return_int_count(void **state)
{
    intmax_t value;

    (void)state; /* unused */

    value = -42;
    will_return_int_count(mock_function_int, value, 3);
    assert_int_equal(value, mock_function_int());
    assert_int_equal(value, mock_function_int());
    assert_int_equal(value, mock_function_int());
}

static void test_will_return_uint(void **state)
{
    uintmax_t value;

    (void)state; /* unused */

    value = rand();
    will_return_uint(mock_function_uint, value);
    assert_uint_equal(value, mock_function_uint());
}

static void test_will_return_uint_count(void **state)
{
    uintmax_t value;

    (void)state; /* unused */

    value = 42;
    will_return_uint_count(mock_function_uint, value, 3);
    assert_uint_equal(value, mock_function_uint());
    assert_uint_equal(value, mock_function_uint());
    assert_uint_equal(value, mock_function_uint());
}

static void test_will_return_uint64(void **state)
{
    uint64_t value = 86405000000UL;
    uint64_t ret;

    (void)state; /* unused */

    will_return_uint(mock_function_uint64, value);
    ret = mock_function_uint64();
    assert_uint_equal(ret, 86405000000UL);
}

static void test_will_return_float(void **state)
{
    float value = 1.0f;

    (void)state; /* unused */

    will_return_float(mock_function_float, value);
    assert_float_equal(value, mock_function_float(), 0.0f);
}

static void test_will_return_float_count(void **state)
{
    float value = 3.14f;

    (void)state; /* unused */

    will_return_float_count(mock_function_float, value, 3);
    assert_float_equal(value, mock_function_float(), 0.01f);
    assert_float_equal(value, mock_function_float(), 0.01f);
    assert_float_equal(value, mock_function_float(), 0.01f);
}

static void test_will_return_double(void **state)
{
    double value = 2.5;

    (void)state; /* unused */

    will_return_double(mock_function_double, value);
    assert_double_equal(value, mock_function_double(), 0.0);
}

static void test_will_return_double_count(void **state)
{
    double value = 2.718281828;

    (void)state; /* unused */

    will_return_double_count(mock_function_double, value, 3);
    assert_double_equal(value, mock_function_double(), 0.001);
    assert_double_equal(value, mock_function_double(), 0.001);
    assert_double_equal(value, mock_function_double(), 0.001);
}

static void test_mock_double(void **state)
{
    double value = 3.14159;

    (void)state; /* unused */

    will_return_double(mock_function_double, value);
    assert_double_equal(value, mock_function_double(), 0.001);
}

static void test_will_return_ptr(void **state)
{
    const char *value = "What a Wurst!";

    (void)state; /* unused */

    will_return_ptr_type(mock_function_ptr, value, const char *);
    assert_string_equal(value, mock_function_ptr());
}

static void test_will_return_ptr_unchecked(void **state)
{
    char value[] = "Wurst without const!";

    (void)state; /* unused */

    will_return_ptr(mock_function_ptr_unchecked, value);
    assert_string_equal(value, mock_function_ptr_unchecked());
}

static void test_will_return_int_always(void **state)
{
    intmax_t value = -100;

    (void)state; /* unused */

    will_return_int_always(mock_function_int, value);
    assert_int_equal(value, mock_function_int());
    assert_int_equal(value, mock_function_int());
    assert_int_equal(value, mock_function_int());
}

static void test_will_return_uint_always(void **state)
{
    uintmax_t value = 999;

    (void)state; /* unused */

    will_return_uint_always(mock_function_uint, value);
    assert_uint_equal(value, mock_function_uint());
    assert_uint_equal(value, mock_function_uint());
    assert_uint_equal(value, mock_function_uint());
}

static void test_will_return_float_always(void **state)
{
    float value = 1.414f;

    (void)state; /* unused */

    will_return_float_always(mock_function_float, value);
    assert_float_equal(value, mock_function_float(), 0.001f);
    assert_float_equal(value, mock_function_float(), 0.001f);
    assert_float_equal(value, mock_function_float(), 0.001f);
}

static void test_will_return_double_always(void **state)
{
    double value = 1.732050808;

    (void)state; /* unused */

    will_return_double_always(mock_function_double, value);
    assert_double_equal(value, mock_function_double(), 0.0001);
    assert_double_equal(value, mock_function_double(), 0.0001);
    assert_double_equal(value, mock_function_double(), 0.0001);
}

static void test_will_return_int_maybe(void **state)
{
    intmax_t value = -555;

    (void)state; /* unused */

    will_return_int_maybe(mock_function_int, value);
    /* Maybe variants don't require the value to be consumed */
}

static void test_will_return_uint_maybe(void **state)
{
    uintmax_t value = 777;

    (void)state; /* unused */

    will_return_uint_maybe(mock_function_uint, value);
    /* Maybe variants don't require the value to be consumed */
}

static void test_will_return_float_maybe(void **state)
{
    float value = 2.236f;

    (void)state; /* unused */

    will_return_float_maybe(mock_function_float, value);
    /* Maybe variants don't require the value to be consumed */
}

static void test_will_return_double_maybe(void **state)
{
    double value = 2.23606798;

    (void)state; /* unused */

    will_return_double_maybe(mock_function_double, value);
    /* Maybe variants don't require the value to be consumed */
}

int main(int argc, char **argv) {
    const struct CMUnitTest alloc_tests[] = {
        cmocka_unit_test(test_will_return_maybe_for_no_calls),
        cmocka_unit_test(test_will_return_maybe_for_one_mock_call),
        cmocka_unit_test(test_will_return_maybe_for_more_than_one_call),
        cmocka_unit_test(test_will_return_int),
        cmocka_unit_test(test_will_return_int_count),
        cmocka_unit_test(test_will_return_uint),
        cmocka_unit_test(test_will_return_uint_count),
        cmocka_unit_test(test_will_return_uint64),
        cmocka_unit_test(test_will_return_float),
        cmocka_unit_test(test_will_return_float_count),
        cmocka_unit_test(test_will_return_double),
        cmocka_unit_test(test_will_return_double_count),
        cmocka_unit_test(test_mock_double),
        cmocka_unit_test(test_will_return_ptr),
        cmocka_unit_test(test_will_return_ptr_unchecked),
        cmocka_unit_test(test_will_return_int_always),
        cmocka_unit_test(test_will_return_uint_always),
        cmocka_unit_test(test_will_return_float_always),
        cmocka_unit_test(test_will_return_double_always),
        cmocka_unit_test(test_will_return_int_maybe),
        cmocka_unit_test(test_will_return_uint_maybe),
        cmocka_unit_test(test_will_return_float_maybe),
        cmocka_unit_test(test_will_return_double_maybe),
    };

    (void)argc;
    (void)argv;

    return cmocka_run_group_tests(alloc_tests, NULL, NULL);
}
