#include "../../src/ErrorSimulator.hpp"
#include "../Tests.hpp"
#include "core/data/LinkedList.hpp"

template struct Core::LinkedList<size_t>;
using IntList = Core::LinkedList<size_t>;

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;
};

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

static void testAdd() {
    IntList list;
    list.add(5u);
    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;
    list.add(4u).add(3u).add(2u);
    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;
    list.add(5u).add(4u);
    list.clear();
    CORE_TEST_EQUAL(0, list.getLength());
    CORE_TEST_FALSE(list.begin() != list.end());
}

static void testBigAdd(bool light) {
    size_t limit = light ? 10000 : 100000;
    IntList list;
    for(size_t i = 0; i < limit; i++) {
        list.add(i);
    }
    auto iter = list.begin();
    for(size_t i = 0; i < list.getLength(); i++) {
        CORE_TEST_EQUAL(i, *iter);
        ++iter;
    }
    CORE_TEST_EQUAL(limit, list.getLength());
}

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

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

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

    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;
    list.add(1u).add(2u).add(3u);

    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;
    list.add(1u).add(243u).add(423u);
    CORE_TEST_STRING("[1, 243, 423]", list);
}

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

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

static void testRemove() {
    IntList list;
    IntList::Node* a = list.put(4u);
    IntList::Node* b = list.put(3u);
    IntList::Node* c = list.put(2u);
    IntList::Node* d = list.put(1u);
    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;
    list.add(4u).add(3u).add(2u).add(1u);

    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;
    list.add(4u).add(3u).add(2u).add(1u);

    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::testLinkedList(bool light) {
    testWithoutCopyOrMove();
    testAdd();
    testMultipleAdd();
    testClear();
    testBigAdd(light);
    testCopy();
    testMove();
    testMoveAssignment();
    testToString1();
    testToString2();
    testToString3();
    testRemove();
    testRemoveFirst();
    testRemoveLast();
}