#ifndef QUEUE_H
#define QUEUE_H

#include "definition.h"

template <typename T>
class Queue
{
public:
    Queue(): _next(NULL), _head(this), _value(NULL)
    {
    }
    ~Queue()
    {
        if(_head == this)
            dispose();
    }
    boolean add(T value)
    {
        Queue *queueNode = new Queue(new T(value));
        return addNewNodeAtTheEnd(queueNode);
    }
    boolean add(T *value)
    {
        Queue *queueNode = new Queue(value);
        return addNewNodeAtTheEnd(queueNode);
    }
    unsigned int count()
    {
        unsigned int counter(0);
        if(isQueueEmpty(_head->_next))return counter;

        Queue *cursor = _head->_next;
        while(!isQueueEmpty(cursor))
        {
            counter++;
            cursor = cursor->_next;
        }
        return counter;
    }
    T* removeRef()
    {
        if(isQueueEmpty(_head->_next))return NULL;

        T *refToReturn = _head->_next->_value;
        Queue *toDelete(_head->_next);
        _head->_next = _head->_next->_next;
        
        delete toDelete;
        
        return refToReturn;
    }
    T remove()
    {
        T *ref = removeRef();
        if(ref != NULL)
        {
            T value(*ref);
            delete ref;
            return value;
        }else
        {
            T value;
            return value;
        }
    }
    void clear(){_head->dispose();}
    void dispose()
    {
        if(isQueueEmpty(_head->_next))return;

        Queue *cursor = _head->_next, *toDelete(NULL);

        while(!isQueueEmpty(cursor))
        {
            toDelete = cursor;
            cursor = cursor->_next;

            delete toDelete->_value;
            delete toDelete;
        }
        _head = this;
        _next = NULL;
    }
protected:
    Queue(T *value): Queue()
    {
        _value = value;
    }
    boolean isQueueEmpty(Queue *node){return node == NULL;}
    boolean addNewNodeAtTheEnd(Queue *node)
    {
        if(node == NULL) return false;

        node->_head = _head; //Every node should point to the first node

        if(_next == NULL) //This is our first node then
        {
            _next = node;
            return true;
        }

        // /!\ We have to work with the _next reference in the loop, if we don't it won't work as expected
        Queue *cursor = _head;
        while(!isQueueEmpty(cursor->_next))
        {
            cursor = cursor->_next;
        }
        cursor->_next = node;

        return true;
    }

    Queue *_next;
    Queue *_head;
    T *_value;
private:
};


#endif //QUEUE_H
