#include <iostream>
#include <cassert>
using namespace std;

#include "Factory.h"
#include "ll.h"

int main() {
    LinkedListInterface * lli = Factory::getLinkedList();
    ll * myll = (ll*)lli;
    myll->insertHead(8);
    myll->insertHead(5);
    myll->insertHead(3);
    myll->insertHead(2);
    myll->insertHead(1);
    // should be duplicate
    myll->insertHead(1);
    myll->insertHead(0);
    assert(myll->size() == 6);
    assert(myll->head->value == 0);
    assert(myll->tail->value == 8);
    assert(myll->at(-8) == -1);
    assert(myll->at(3238) == -1);
    assert(myll->at(3) == 3);
    delete myll;

    {
        ll ll2 = ll();
        ll2.insertTail(5);
        ll2.insertTail(6);
        assert(ll2.size() == 2);
        assert(ll2.head->value == 5);
        assert(ll2.tail->value == 6);
    }

    {
        // test for negative insertion
        ll ll4 = ll();
        ll4.insertHead(-1);
        ll4.insertTail(-1);
        assert(ll4.size() == 0);
    }

    {
        // test for duplicate insertion
        ll ll3 = ll();
        ll3.insertTail(1);
        ll3.insertTail(2);
        ll3.insertTail(3);
        ll3.insertTail(4);
        ll3.insertTail(4);
        ll3.insertTail(4);
        ll3.insertTail(5);
        assert(ll3.size() == 5);
    }

    {
        // test find
        ll ll5 = ll();
        ll5.insertHead(0);
        ll5.insertTail(1);
        ll5.insertTail(2);
        assert(ll5.find(2));
        assert(ll5.find(200) == NULL);
    }

    {
        // test insertAfter
        ll ll6 = ll();
        ll6.insertHead(0);
        ll6.insertTail(2);
        ll6.insertTail(3);
        assert(ll6.size() == 3);
        ll6.insertAfter(1, 0);
        assert(ll6.size() == 4);
        ll6.insertAfter(1, 23);
        assert(ll6.size() == 4);
    }

    {
        ll ll7 = ll();
        ll7.insertAfter(1, 0);
    }

    {
        // test deletion
        ll ll8 = ll();
        ll8.insertTail(1);
        ll8.insertTail(2);
        ll8.insertTail(3);
        ll8.insertTail(4);
        ll8.insertTail(5);
        ll8.remove(4);
        assert(ll8.find(4) == NULL);
        assert(ll8.size() == 4);
        assert(ll8.at(0) == 1);
        assert(ll8.at(1) == 2);
        assert(ll8.at(2) == 3);
        assert(ll8.at(3) == 5);
        assert(ll8.at(4) == -1);
        assert(ll8.at(5) == -1);
    }

    {
        ll ll9 = ll();
        ll9.remove(4);
        assert(ll9.size() == 0);
        assert(ll9.at(1) == -1);
    }

    {
        ll ll10 = ll();
        ll10.insertTail(4);
        assert(ll10.size() == 1);
        ll10.remove(4);
        assert(ll10.size() == 0);
    }

    {
        ll ll11 = ll();
        ll11.insertHead(4);
        assert(ll11.size() == 1);
        assert(ll11.at(0) == 4);
        ll11.remove(4);
        assert(ll11.size() == 0);
    }

    {
        ll ll12 = ll();
        ll12.insertTail(1);
        ll12.insertTail(9);
        assert(ll12.size() == 2);
        ll12.remove(1);
        assert(ll12.size() == 1);
    }

    {
        ll ll13 = ll();
        ll13.insertTail(9);
        ll13.insertTail(3);
        assert(ll13.size() == 2);
        ll13.insertTail(16);
        assert(ll13.size() == 3);
    }

    {
        ll ll_14 = ll();
        ll_14.insertHead(9);
        ll_14.insertHead(3);
        assert(ll_14.size() == 2);
        ll_14.insertTail(16);
        assert(ll_14.size() == 3);
    }
    
    {
        // test enforced by assert in clear
        ll l = ll();
        l.insertHead(16);
        l.insertTail(32);
        l.insertTail(38);
        l.remove(38);
        l.insertTail(9);
    }

    {
        // test enforced by asserts in ll
        ll l = ll();
        l.insertHead(16);
        l.insertAfter(14, 16);
        l.insertTail(17);
    }

    cout << "You passed" << endl;
}
