#include "tests/ListTests.h"
#include "tests/Test.h"
#include "utils/List.h"
#include "utils/StringBuffer.h"

typedef List<int, 20> IntList;
typedef StringBuffer<50> String;

static void testAdd(Test& test) {
    IntList list;
    list.add(5);

    test.checkEqual(5, list[0], "contains added value");
    test.checkEqual(1, list.getLength(), "sizes is increased by add");
}

static void testMultipleAdd(Test& test) {
    IntList list;
    list.add(4);
    list.add(3);
    list.add(2);
    test.checkEqual(4, list[0], "contains added value 1");
    test.checkEqual(3, list[1], "contains added value 2");
    test.checkEqual(2, list[2], "contains added value 3");
    test.checkEqual(3, list.getLength(), "sizes is increased by add");
}

static void testAddReplace(Test& test) {
    IntList list;
    list.add(5);
    list[0] = 3;
    test.checkEqual(3, list[0], "value is overwritten");
}

static void testClear(Test& test) {
    IntList list;
    list.add(5);
    list.add(4);
    list.clear();
    test.checkEqual(0, list.getLength(), "length is 0 after clear");
}

static void testOverflow(Test& test) {
    IntList list;
    for(int i = 0; i < 20; i++) {
        test.checkEqual(false, list.add(i), "add returns false without overflow");
    }
    for(int i = 0; i < 1000000; i++) {
        test.checkEqual(true, list.add(i), "add returns true with overflow");
    }
    for(int i = 0; i < list.getLength(); i++) {
        test.checkEqual(i, list[i], "still contains values after overflow");
    }
    test.checkEqual(true, true, "survives overflow");
}

static void testCopy(Test& test) {
    IntList list;
    list.add(1);
    list.add(2);
    list.add(3);

    IntList copy(list);
    test.checkEqual(list.getLength(), copy.getLength(), "copy has same length");
    for(int i = 0; i < copy.getLength() && i < list.getLength(); i++) {
        test.checkEqual(list[i], copy[i], "copy has same values");
    }
}

static void testCopyAssignment(Test& test) {
    IntList list;
    list.add(1);
    list.add(2);
    list.add(3);

    IntList copy;
    copy = list;
    test.checkEqual(list.getLength(), copy.getLength(), "copy assignment has same length");
    for(int i = 0; i < copy.getLength() && i < list.getLength(); i++) {
        test.checkEqual(list[i], copy[i], "copy assignment has same values");
    }
}

static void testMove(Test& test) {
    IntList list;
    list.add(1);
    list.add(2);
    list.add(3);

    IntList move(std::move(list));
    test.checkEqual(0, list.getLength(), "moved has length 0");
    test.checkEqual(3, move.getLength(), "moved passes length");
    test.checkEqual(1, move[0], "moved passes values");
    test.checkEqual(2, move[1], "moved passes values");
    test.checkEqual(3, move[2], "moved passes values");
}

static void testMoveAssignment(Test& test) {
    IntList list;
    list.add(1);
    list.add(2);
    list.add(3);

    IntList move;
    move = std::move(list);
    test.checkEqual(0, list.getLength(), "assignment moved has length 0");
    test.checkEqual(3, move.getLength(), "assignment moved passes length");
    test.checkEqual(1, move[0], "assignment moved passes values");
    test.checkEqual(2, move[1], "assignment moved passes values");
    test.checkEqual(3, move[2], "assignment moved passes values");
}

static void testToString1(Test& test) {
    IntList list;
    list.add(1);
    list.add(243);
    list.add(-423);
    test.checkEqual(String("[1, 243, -423]"), String(list), "to string 1");
}

static void testToString2(Test& test) {
    IntList list;
    list.add(1);
    test.checkEqual(String("[1]"), String(list), "to string 2");
}

static void testToString3(Test& test) {
    IntList list;
    test.checkEqual(String("[]"), String(list), "to string 3");
}

static void testRemove(Test& test) {
    IntList list;
    list.add(4);
    list.add(3);
    list.add(2);
    list.remove(0);
    test.checkEqual(2, list[0], "remove 1");
    test.checkEqual(3, list[1], "remove 2");
    test.checkEqual(2, list.getLength(), "remove 3");
    list.remove(1);
    test.checkEqual(2, list[0], "remove 4");
    test.checkEqual(1, list.getLength(), "remove 5");
    list.remove(0);
    test.checkEqual(0, list.getLength(), "remove 6");
}

static void testRemoveIsSafe(Test& test) {
    IntList list;
    for(int i = -500000; i < 500000; i++) {
        test.checkEqual(true, list.remove(i), "removing from empty list is safe");
    }
}

void ListTests::test() {
    Test test("List");
    testAdd(test);
    testMultipleAdd(test);
    testAddReplace(test);
    testClear(test);
    testOverflow(test);
    testCopy(test);
    testCopyAssignment(test);
    testMove(test);
    testMoveAssignment(test);
    testToString1(test);
    testToString2(test);
    testToString3(test);
    testRemove(test);
    testRemoveIsSafe(test);
    test.finalize();
}