#include "ll.h"

ll::ll(): _size(0), head(NULL), tail(NULL) {
}

ll::~ll() {
    clear();
}

void ll::insertHead(int value) {
    if(value < 0 or find(value)) {
        return;
    }
    if(head == NULL) {
        assert(tail == NULL);
        assert(_size == 0);
        head = new node(value);
        tail = head;
    }
    else {
        node * tmp = head;
        head = new node(value);
        head->next = tmp;
    }
    _size++;
}

void ll::insertTail(int value) {
    if(value < 0 or find(value)) {
        return;
    }
    if(tail == NULL) {
        assert(head == NULL);
        assert(_size == 0);
        tail = new node(value);
        head = tail;
    }
    else {
        node * tmp = tail;
        tmp->next = new node(value);
        tail = tmp->next;
    }
    _size++;
}

void ll::insertAfter(int value, int insertionNode) {
    if(value < 0 or find(value)) {
        return;
    }
    node * inlist = find(insertionNode);
    if(not inlist) {
        return;
    }
    node * toinsert = new node(value);
    if(inlist == tail) {
        tail = toinsert;
    }
    toinsert->next = inlist->next;
    inlist->next = toinsert;
    _size++;
}

void ll::remove(int value) {
    if(not find(value)) {
        // should deal with empty list, and not found
        return;
    }
    else if(head == tail) {
        delete head;
        head = tail = NULL;
        _size--;
    }
    else {
        node * cur = head;
        if(head->value == value) {
            head = head->next;
            delete cur;
            _size--;
        }
        else {
            while(cur) {
                if(cur->next == NULL) {
                    // removing end ...
                    break;
                }
                if(cur->next->value == value) {
                    node * to_remove = cur->next;
                    if(to_remove == tail) {
                        tail = cur;
                    }
                    cur->next = to_remove->next;
                    delete to_remove;
                    _size--;
                }
                cur = cur->next;
            }
        }
    }
}

void ll::clear() {
    node * tmp = head;
    while(head) {
        tmp = head;
        head = head->next;
        delete tmp;
        _size--;
    }
    head = tail = NULL;
    assert(_size == 0);
}

int ll::at(int index) {
    if (index >= _size or index < 0) {
        return -1;
    }
    node * tmp = head;
    for(int i = 0; i < index; i++) {
        tmp = tmp->next;
    }
    if(tmp == NULL) {
        return -1;
    }
    else {
        return tmp->value;
    }
}

int  ll::size() {
    return _size;
}

node * ll::find(int value) {
    node * tmp = head;
    while(tmp) {
        if(tmp->value == value) {
            return tmp;
        }
        tmp = tmp->next;
    }
    return NULL;
}

ostream & operator<<(ostream & os, const node & n) {
    os << n.value;
    return os;
}

ostream & operator<<(ostream & os, const ll & l) {
    os << "ll {(" << l._size << ") ";
    node * cur = l.head;
    while (cur) {
        os << *cur << ", ";
        cur = cur->next;
    }
    os << "}";
    return os;
}
