#include "../Tests.hpp"
#include "core/thread/Thread.hpp"

static int runDone = 0;

struct IntHolder {
    int value;
};

static int run(void*) {
    runDone = 1;
    return 7;
}

static void testStart() {
    runDone = 0;
    Core::Thread t;
    CORE_TEST_ERROR(t.start(run, nullptr));
    int returnValue = 0;
    CORE_TEST_ERROR(t.join(&returnValue));
    CORE_TEST_EQUAL(1, runDone);
    CORE_TEST_EQUAL(7, returnValue);
}

static void testLambda() {
    IntHolder i(0);
    Core::Thread t;
    CORE_TEST_ERROR(t.start(
        [](void* p) {
            IntHolder* ip = static_cast<IntHolder*>(p);
            ip->value = 2;
            return 0;
        },
        &i));
    CORE_TEST_ERROR(t.join(nullptr));
    CORE_TEST_EQUAL(2, i.value);
}

static void testJoinWithoutStart() {
    Core::Thread t;
    CORE_TEST_EQUAL(Core::Error::THREAD_ERROR, t.join(nullptr));
}

static void testAutoJoin() {
    Core::Thread t;
    CORE_TEST_ERROR(t.start([](void*) { return 0; }, nullptr));
}

static void testMove() {
    Core::Thread t;
    CORE_TEST_ERROR(t.start([](void*) { return 0; }, nullptr));
    Core::Thread m = Core::move(t);
    CORE_TEST_ERROR(m.join());
}

static void testMoveAssignment() {
    Core::Thread t;
    CORE_TEST_ERROR(t.start([](void*) { return 0; }, nullptr));
    Core::Thread m;
    m = Core::move(t);
    CORE_TEST_ERROR(m.join());
}

static void testMoveIntoActive() {
    Core::Thread t;
    CORE_TEST_ERROR(t.start([](void*) { return 0; }, nullptr));
    Core::Thread m;
    t = Core::move(m);
}

static void testDoubleJoin() {
    Core::Thread t;
    CORE_TEST_ERROR(t.start([](void*) { return 0; }, nullptr));
    CORE_TEST_ERROR(t.join(nullptr));
    CORE_TEST_EQUAL(Core::Error::THREAD_ERROR, t.join(nullptr));
}

void Core::testThread() {
    testStart();
    testLambda();
    testJoinWithoutStart();
    testAutoJoin();
    testMove();
    testMoveAssignment();
    testMoveIntoActive();
    testDoubleJoin();
}