#pragma once
#include "common.hpp"
#include "pool.hpp"
#include "test.hpp"


template <class T> struct queue_node_s {
    struct queue_node_s<T>* next;
    T data;
};


/** simple FIFO queue with built-in pool support **/
template <class T> class Queue {
    private:
        TPool<queue_node_s<T> > pool; // TODO: use own pool list? (as we have multiple wrapper pointers atm). Isn't static as it needs to be threadsafe.
        queue_node_s<T>* head;
        queue_node_s<T>* tail;
        size_t len;

    public:
        Queue();
        ~Queue();

        T* push();
        INLINE T* peek();
        void pop();

        INLINE size_t size() { return len; }
};


/** simple FILO stack with pool and very simple iterators **/
template <class T> class Stack {
    private:
        TPool<queue_node_s<T> > pool; // TODO: use own pool list? (as we have multiple wrapper pointers atm)
        queue_node_s<T>* head;

    public:
        Stack();
        ~Stack();

        void push(T);
        T* push_get();
        T pop();

        INLINE bool empty() const { return head == NULL; }
        INLINE T* iterator() { return head? &head->data: NULL; }
        T* iterator_next(T*);
};


/** push/pop (-only) thread-safety **/
template <class T> class StackTS: private Stack<T> {
    private:
        atomic_t lock;

    public:
        INLINE StackTS(): lock(0) {};

        INLINE void push(T t) { spin_lock(&lock); Stack<T>::push(t); spin_unlock(&lock); } // TODO: use some cmpxchg approach?
        INLINE T pop() { spin_lock(&lock); T t = Stack<T>::pop(); spin_unlock(&lock); return t; }
        using Stack<T>::empty;
};


//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//


template <class T> Queue<T>::Queue(): head(NULL), tail(NULL), len(0) {
}


template <class T> Queue<T>::~Queue() { // cleanup will be done by pool
}


template <class T> T* Queue<T>::push() {
    queue_node_s<T>* node = pool.pop();
    node->next = NULL;
    if (!tail) {
        head = node;
        tail = node;
    } else {
        tail->next = node;
        tail = node;
    }
    ++len;
    return &node->data;
}


template <class T> T* Queue<T>::peek() {
    return head? &head->data: NULL;
}


template <class T> void Queue<T>::pop() {
    assert(head);
    queue_node_s<T>* node = head;
    head = head->next;
    if (!head) {
        assert(len == 1);
        tail = NULL;
    }
    pool.push(node);
    --len;
}


template <class T> Stack<T>::Stack(): head(NULL) {
}


template <class T> Stack<T>::~Stack() { // cleanup will be done by pool
}


template <class T> void Stack<T>::push(T t) {
    queue_node_s<T>* node = pool.pop();
    node->data = t;
    node->next = head;
    head = node;
}


template <class T> T* Stack<T>::push_get() {
    queue_node_s<T>* node = pool.pop();
    node->next = head;
    head = node;
    return &node->data;
}


template <class T> T Stack<T>::pop() {
    assert(head);
    queue_node_s<T>* node = head;
    head = head->next;
    T t = node->data;
    pool.push(node);
    return t;
}


template <class T> T* Stack<T>::iterator_next(T* t) {
    queue_node_s<T>* node = ((queue_node_s<T>*)((char*)t - sizeof(queue_node_s<T>*)))->next;
    return node? &node->data: NULL;
}