#include <iostream>
#include "data/UnsortedArrayList.h"
#include "data/HashMap.h"

using namespace std;

// -----------------------------------------------------------------------------
// test feedback
// -----------------------------------------------------------------------------

const char* RED = "\033[0;31m";
const char* GREEN = "\033[0;32m";

int tests = 0;
int successTests = 0;

void notifySuccess(string text)
{
    tests++;
    successTests++;
    cout << GREEN << tests << ". " << text << endl; 
}

void notifyFail(string text)
{
    tests++;
    cout << RED << tests << ". " << text << endl;
}

void finalizeTest()
{
    cout << successTests << " / " << tests << " succeeded" << endl;
    tests = 0;
    successTests = 0;
}

// -----------------------------------------------------------------------------
// checks
// -----------------------------------------------------------------------------

void checkEqual(int a, int b, string text)
{
    if(a == b)
    {
        notifySuccess(text);
    }
    else
    {
        notifyFail(text + " - expected " + std::to_string(a) + " got " + std::to_string(b));
    }
}

void checkBool(bool a, bool b, string text)
{
    if(a == b)
    {
        notifySuccess(text);
    }
    else
    {
        notifyFail(text + " - expected " + std::to_string(a) + " got " + std::to_string(b));
    }
}

void checkGreaterOrEqual(int a, int b, string text)
{
    if(a >= b)
    {
        notifySuccess(text);
    }
    else
    {
        notifyFail(text + " - expected " + std::to_string(a) + " >= " + std::to_string(b));
    }
}

// -----------------------------------------------------------------------------
// tests of UnsortedArrayList
// -----------------------------------------------------------------------------

void testUnsortedArrayList1(string text)
{
    UnsortedArrayList<int> list;
    
    list.add(1);
    list.add(5);
    list.add(10);
    
    checkEqual(3, list.getSize(), text);
    checkEqual(1, list.get(0, -1), text);
    checkEqual(5, list.get(1, -1), text);
    checkEqual(10, list.get(2, -1), text);
}

void testUnsortedArrayList2(string text)
{
    UnsortedArrayList<int> list;
    
    list.add(1);
    list.add(5);
    list.add(10);
    list.removeIndex(1);
    
    checkEqual(2, list.getSize(), text);
    checkEqual(1, list.get(0, -1), text);
    checkEqual(10, list.get(1, -1), text);
}

void testUnsortedArrayList3(string text)
{
    UnsortedArrayList<int> list;
    
    list.add(1);
    list.add(5);
    list.add(10);
    list.removeIndex(0);
    list.add(20);
    
    checkEqual(3, list.getSize(), text);
    checkEqual(10, list.get(0, -1), text);
    checkEqual(5, list.get(1, -1), text);
    checkEqual(20, list.get(2, -1), text);
}

void testUnsortedArrayList4(string text)
{
    UnsortedArrayList<int> list;
    
    list.add(1);
    list.add(5);
    list.add(10);
    list.removeIndex(-5);
    list.removeIndex(5);
    
    checkEqual(3, list.getSize(), text);
    checkEqual(1, list.get(0, -1), text);
    checkEqual(5, list.get(1, -1), text);
    checkEqual(10, list.get(2, -1), text);
}

void testUnsortedArrayList5(string text)
{
    UnsortedArrayList<int> list;
    
    list.add(1);
    list.add(5);
    list.add(1);
    list.add(20);
    list.remove(2);
    
    checkEqual(4, list.getSize(), text);
    checkEqual(1, list.get(0, -1), text);
    checkEqual(5, list.get(1, -1), text);
    checkEqual(1, list.get(2, -1), text);
    checkEqual(20, list.get(3, -1), text);
}

void testUnsortedArrayList6(string text)
{
    UnsortedArrayList<int> list;
    
    int r = rand();
    checkEqual(r, list.get(-100, r), text);
    r = rand();
    checkEqual(r, list.get(100, r), text);
}

void testUnsortedArrayList7(string text)
{
    UnsortedArrayList<int> list;
    
    list.add(1);
    list.add(1);
    list.add(5);
    list.add(1);
    list.add(20);
    list.remove(1);
    
    checkEqual(2, list.getSize(), text);
    checkEqual(20, list.get(0, -1), text);
    checkEqual(5, list.get(1, -1), text);
}

void testUnsortedArrayList()
{
    testUnsortedArrayList1("add, size and get");
    testUnsortedArrayList2("remove index");
    testUnsortedArrayList3("add after remove index, move last to first");
    testUnsortedArrayList4("remove of non existent element");
    testUnsortedArrayList5("remove index of non existent index");
    testUnsortedArrayList6("get returns error value");
    testUnsortedArrayList7("remove deletes all occurencies");
    finalizeTest();
}

// -----------------------------------------------------------------------------
// tests of HashMap
// -----------------------------------------------------------------------------

unsigned int hashInt(int i)
{
    return i;
}

void testHashMap1(string text)
{
    HashMap<int, int> map(16, hashInt);
    int cap = map.getCapacity();
    checkGreaterOrEqual(cap, 16, text);
    HashMap<int, int> map2(cap, hashInt);
    HashMap<int, int> map3(cap + 1, hashInt);
    checkEqual(cap, map2.getCapacity(), text);
    checkGreaterOrEqual(map3.getCapacity(), cap + 1, text);
}

void testHashMap2(string text)
{
    HashMap<int, int> map(16, hashInt);
    
    for(int i = 0; i < 5; i++)
    {
        map.put(i, i);
    }
    
    checkEqual(5, map.getSize(), text);
}

void testHashMap3(string text)
{
    HashMap<int, int> map(16, hashInt);
    int cap = map.getCapacity();
    if(cap < 0)
    {
        cap = 0;
    }
    cap += 3;
    
    for(int i = 0; i < cap; i++)
    {
        map.put(i, i);
    }
    
    checkEqual(cap, map.getSize(), text);
}

void testHashMap4(string text)
{
    HashMap<int, int> map(16, hashInt);
    for(int i = 0; i < 3; i++)
    {
        map.put(i, i + 20);
    }
    for(int i = 0; i < 3; i++)
    {
        checkEqual(i + 20, map.get(i, -1), text);
    }
}

void testHashMap5(string text)
{
    HashMap<int, int> map(16, hashInt);
    int cap = map.getCapacity();
    if(cap < 0)
    {
        cap = 0;
    }
    cap += 3;
    for(int i = 0; i < cap; i++)
    {
        map.put(i, i + 20);
    }
    
    checkBool(true, map.remove(cap / 2), text);
    checkEqual(-1, map.get(cap / 2, -1), text);
    checkEqual(cap - 1, map.getSize(), text);
}

void testHashMap6(string text)
{
    HashMap<int, int> map(16, hashInt);
    int cap = map.getCapacity();
    if(cap < 0)
    {
        cap = 0;
    }
    cap += 3;
    for(int i = 0; i < cap; i++)
    {
        map.put(i, i + 20);
    }
    checkBool(false, map.remove(cap + 5), text);
    checkBool(false, map.remove(cap + 20), text);
    checkBool(false, map.remove(cap + 25), text);
    checkEqual(cap, map.getSize(), text);
}

void testHashMap7(string text)
{
    HashMap<int, int> map(16, hashInt);
    for(int i = 0; i < 3; i++)
    {
        map.put(i, i + 20);
    }
    checkEqual(3, map.getSize(), text);
    map.clear();
    checkEqual(0, map.getSize(), text);
    checkEqual(-1, map.get(0, -1), text);
    checkEqual(-1, map.get(1, -1), text);
    checkEqual(-1, map.get(2, -1), text);
}

void testHashMap8(string text)
{
    HashMap<int, int> map(16, hashInt);
    for(int i = 0; i < 3; i++)
    {
        map.put(i, i + 20);
    }
    for(int i = 0; i < 3; i++)
    {
        map.put(i, i + 25);
    }
    for(int i = 0; i < 3; i++)
    {
        checkEqual(i + 25, map.get(i, -1), text);
    }
}

void testHashMap9(string text)
{
    HashMap<int, int> map(16, hashInt);
    int cap = map.getCapacity();
    if(cap < 0)
    {
        cap = 0;
    }
    cap += 3;
    for(int i = 0; i < cap; i++)
    {
        map.put(i, i + 20);
    }
    
    checkEqual(cap / 2 + 20, map.remove(cap / 2, -1), text);
    checkEqual(cap - 1, map.getSize(), text);
}

void testHashMap10(string text)
{
    HashMap<int, int> map(16, hashInt);
    int cap = map.getCapacity();
    if(cap < 0)
    {
        cap = 0;
    }
    cap += 3;
    for(int i = 0; i < cap; i++)
    {
        map.put(i, i + 20);
    }
    checkEqual(-1, map.remove(cap + 5, -1), text);
    checkEqual(-1, map.remove(cap + 20, -1), text);
    checkEqual(-1, map.remove(cap + 25, -1), text);
    checkEqual(cap, map.getSize(), text);
}

void testHashMap()
{
    testHashMap1("capacity greater or equal than inital value");
    testHashMap2("put increases size");
    testHashMap3("put over capacity");
    testHashMap4("get");
    testHashMap5("remove of existent value");
    testHashMap6("remove of non existent value");
    testHashMap7("clear");
    testHashMap8("put overwrites old value");
    testHashMap9("remove with return value of existent value");
    testHashMap10("remove with return value of non existent value");
    finalizeTest();
}

// -----------------------------------------------------------------------------
// main
// -----------------------------------------------------------------------------

int main(int argc, char** argv)
{
    testUnsortedArrayList();
    testHashMap();
    return 0;
}