#include "tests/LinkedListTests.h"

#include "data/LinkedList.h"
#include "test/Test.h"

struct LinkedListTester final {
    int a;
    LinkedListTester(int a_) : a(a_) {
    }
    LinkedListTester(const LinkedListTester&) = delete;
    LinkedListTester(LinkedListTester&&) = delete;
    LinkedListTester& operator=(const LinkedListTester&) = delete;
    LinkedListTester& operator=(LinkedListTester&&) = delete;
};

using IntList = Core::LinkedList<int>;

static void testWithoutCopyOrMove() {
    Core::LinkedList<LinkedListTester> list;
    CORE_TEST_ERROR(list.add(3));
}

static void testAdd() {
    IntList list;
    CORE_TEST_ERROR(list.add(5));
    auto iter = list.begin();
    if(CORE_TEST_TRUE(iter != list.end())) {
        CORE_TEST_EQUAL(5, *iter);
    }
    CORE_TEST_EQUAL(1, list.getLength());
}

static void testMultipleAdd() {
    IntList list;
    CORE_TEST_ERROR(list.add(4));
    CORE_TEST_ERROR(list.add(3));
    CORE_TEST_ERROR(list.add(2));
    auto iter = list.begin();
    CORE_TEST_EQUAL(4, *iter);
    CORE_TEST_EQUAL(3, *(++iter));
    CORE_TEST_EQUAL(2, *(++iter));
    CORE_TEST_EQUAL(3, list.getLength());
}

static void testClear() {
    IntList list;
    CORE_TEST_ERROR(list.add(5));
    CORE_TEST_ERROR(list.add(4));
    list.clear();
    CORE_TEST_EQUAL(0, list.getLength());
    CORE_TEST_FALSE(list.begin() != list.end());
}

static void testBigAdd() {
    IntList list;
    for(int i = 0; i < 100000; i++) {
        CORE_TEST_ERROR(list.add(i));
    }
    auto iter = list.begin();
    for(int i = 0; i < list.getLength(); i++) {
        CORE_TEST_EQUAL(i, *iter);
        ++iter;
    }
    CORE_TEST_EQUAL(100000, list.getLength());
}

static void testCopy() {
    IntList list;
    CORE_TEST_ERROR(list.add(1));
    CORE_TEST_ERROR(list.add(2));
    CORE_TEST_ERROR(list.add(3));

    IntList copy;
    CORE_TEST_ERROR(copy.copyFrom(list));
    CORE_TEST_EQUAL(list.getLength(), copy.getLength());
    auto iterA = list.begin();
    auto iterB = copy.begin();
    for(int i = 0; i < copy.getLength() && i < list.getLength(); i++) {
        CORE_TEST_EQUAL(*iterA, *iterB);
        ++iterA;
        ++iterB;
    }
}

static void testMove() {
    IntList list;
    CORE_TEST_ERROR(list.add(1));
    CORE_TEST_ERROR(list.add(2));
    CORE_TEST_ERROR(list.add(3));

    const IntList move(Core::move(list));
    CORE_TEST_EQUAL(0, list.getLength());
    CORE_TEST_EQUAL(3, move.getLength());
    auto iter = move.begin();
    CORE_TEST_EQUAL(1, *iter);
    CORE_TEST_EQUAL(2, *(++iter));
    CORE_TEST_EQUAL(3, *(++iter));
}

static void testMoveAssignment() {
    IntList list;
    CORE_TEST_ERROR(list.add(1));
    CORE_TEST_ERROR(list.add(2));
    CORE_TEST_ERROR(list.add(3));

    IntList move;
    move = Core::move(list);
    CORE_TEST_EQUAL(0, list.getLength());
    CORE_TEST_EQUAL(3, move.getLength());
    auto iter = move.begin();
    CORE_TEST_EQUAL(1, *iter);
    CORE_TEST_EQUAL(2, *(++iter));
    CORE_TEST_EQUAL(3, *(++iter));
}

static void testToString1() {
    IntList list;
    CORE_TEST_ERROR(list.add(1));
    CORE_TEST_ERROR(list.add(243));
    CORE_TEST_ERROR(list.add(-423));
    CORE_TEST_STRING("[1, 243, -423]", list);
}

static void testToString2() {
    IntList list;
    CORE_TEST_ERROR(list.add(1));
    CORE_TEST_STRING("[1]", list);
}

static void testToString3() {
    IntList list;
    CORE_TEST_STRING("[]", list);
}

static void testRemove() {
    IntList list;
    IntList::Node* a = nullptr;
    IntList::Node* b = nullptr;
    IntList::Node* c = nullptr;
    IntList::Node* d = nullptr;
    CORE_TEST_ERROR(list.put(a, 4));
    CORE_TEST_ERROR(list.put(b, 3));
    CORE_TEST_ERROR(list.put(c, 2));
    CORE_TEST_ERROR(list.put(d, 1));
    CORE_TEST_NOT_NULL(a);
    CORE_TEST_NOT_NULL(b);
    CORE_TEST_NOT_NULL(c);
    CORE_TEST_NOT_NULL(c);

    list.remove(b);
    auto iter = list.begin();
    CORE_TEST_EQUAL(4, *iter);
    CORE_TEST_EQUAL(2, *(++iter));
    CORE_TEST_EQUAL(1, *(++iter));
    CORE_TEST_EQUAL(3, list.getLength());

    list.remove(a);
    iter = list.begin();
    CORE_TEST_EQUAL(2, *iter);
    CORE_TEST_EQUAL(1, *(++iter));
    CORE_TEST_EQUAL(2, list.getLength());

    list.remove(d);
    iter = list.begin();
    CORE_TEST_EQUAL(2, *iter);
    CORE_TEST_EQUAL(1, list.getLength());

    list.remove(c);
    CORE_TEST_FALSE(list.begin() != list.end());
    CORE_TEST_EQUAL(0, list.getLength());

    CORE_TEST_NULL(a);
    CORE_TEST_NULL(b);
    CORE_TEST_NULL(c);
    CORE_TEST_NULL(c);
}

static void testRemoveFirst() {
    IntList list;
    CORE_TEST_ERROR(list.add(4));
    CORE_TEST_ERROR(list.add(3));
    CORE_TEST_ERROR(list.add(2));
    CORE_TEST_ERROR(list.add(1));

    list.removeFirst();
    auto iter = list.begin();
    if(CORE_TEST_TRUE(iter != list.end())) {
        CORE_TEST_EQUAL(3, *iter);
        ++iter;
    }
    if(CORE_TEST_TRUE(iter != list.end())) {
        CORE_TEST_EQUAL(2, *iter);
        ++iter;
    }
    if(CORE_TEST_TRUE(iter != list.end())) {
        CORE_TEST_EQUAL(1, *iter);
    }
    CORE_TEST_EQUAL(3, list.getLength());

    list.removeFirst();
    iter = list.begin();
    if(CORE_TEST_TRUE(iter != list.end())) {
        CORE_TEST_EQUAL(2, *iter);
        ++iter;
    }
    if(CORE_TEST_TRUE(iter != list.end())) {
        CORE_TEST_EQUAL(1, *iter);
    }
    CORE_TEST_EQUAL(2, list.getLength());

    list.removeFirst();
    iter = list.begin();
    if(CORE_TEST_TRUE(iter != list.end())) {
        CORE_TEST_EQUAL(1, *iter);
    }
    CORE_TEST_EQUAL(1, list.getLength());

    list.removeFirst();
    CORE_TEST_FALSE(list.begin() != list.end());
    CORE_TEST_EQUAL(0, list.getLength());

    list.removeFirst();
    CORE_TEST_FALSE(list.begin() != list.end());
    CORE_TEST_EQUAL(0, list.getLength());
}

static void testRemoveLast() {
    IntList list;
    CORE_TEST_ERROR(list.add(4));
    CORE_TEST_ERROR(list.add(3));
    CORE_TEST_ERROR(list.add(2));
    CORE_TEST_ERROR(list.add(1));

    list.removeLast();
    auto iter = list.begin();
    if(CORE_TEST_TRUE(iter != list.end())) {
        CORE_TEST_EQUAL(4, *iter);
        ++iter;
    }
    if(CORE_TEST_TRUE(iter != list.end())) {
        CORE_TEST_EQUAL(3, *iter);
        ++iter;
    }
    if(CORE_TEST_TRUE(iter != list.end())) {
        CORE_TEST_EQUAL(2, *iter);
    }
    CORE_TEST_EQUAL(3, list.getLength());

    list.removeLast();
    iter = list.begin();
    if(CORE_TEST_TRUE(iter != list.end())) {
        CORE_TEST_EQUAL(4, *iter);
        ++iter;
    }
    if(CORE_TEST_TRUE(iter != list.end())) {
        CORE_TEST_EQUAL(3, *iter);
    }
    CORE_TEST_EQUAL(2, list.getLength());

    list.removeLast();
    iter = list.begin();
    if(CORE_TEST_TRUE(iter != list.end())) {
        CORE_TEST_EQUAL(4, *iter);
    }
    CORE_TEST_EQUAL(1, list.getLength());

    list.removeLast();
    CORE_TEST_FALSE(list.begin() != list.end());
    CORE_TEST_EQUAL(0, list.getLength());

    list.removeLast();
    CORE_TEST_FALSE(list.begin() != list.end());
    CORE_TEST_EQUAL(0, list.getLength());
}

void Core::LinkedListTests::test() {
    testWithoutCopyOrMove();
    testAdd();
    testMultipleAdd();
    testClear();
    testBigAdd();
    testCopy();
    testMove();
    testMoveAssignment();
    testToString1();
    testToString2();
    testToString3();
    testRemove();
    testRemoveFirst();
    testRemoveLast();
}