#include "tout.hpp"


TPool<tout_node_t> ToutQueue::pool;


TEST(ToutQueue) {
    ToutQueue q;
    q.push(1, 42);
    q.push(2, 42);
    q.push(3, 50);
    q.push(4, 100);
    if (!q.pop((int)3)) return false;
    if (q.pop((int)5)) return false;
    if (q.pop((time_t)50) != 1) return false;
    if (q.pop((time_t)50) != 2) return false;
    if (q.pop((time_t)50) != -1) return false;
    if (q.pop((time_t)1000) != 4) return false;
    if (q.pop((time_t)100000) != -1) return false;
    return true;
}


ToutQueue::ToutQueue(): head(NULL), tail(NULL) {
}


ToutQueue::~ToutQueue() {
    if (head) {
        log(notice, "aborting timeouts");
        do {
            tout_node_t* node = head;
            head = head->next;
            pool.push(node);
        } while (head);
    }
}


void ToutQueue::push(int fd, time_t now) {
    tout_node_t* node = pool.pop();
    assert(!fds[fd]);
    fds[fd] = node;
    node->fd = fd;
    node->at = now;
    node->next = NULL;

    if (!tail) {
        node->prev = NULL;
        head = tail = node;
    } else {
        node->prev = tail;
        tail->next = node;
        tail = node;
    }

    assert(!node->prev || node->prev->at <= node->at);
    assert(!node->next || node->next->at >= node->at);
}


int ToutQueue::pop(time_t tout) {
    if (!head || head->at >= tout) {
        return -1;
    }

    tout_node_t* node = head;
    head = head->next;
    if (head) {
        head->prev = NULL;
    } else {
        tail = NULL;
    }

    int fd = node->fd;
    pool.push(node);
    fds[fd] = NULL;
    return fd;
}


bool ToutQueue::pop(int fd) {
    tout_node_t* node = fds[fd];
    if (!node) return false;

    if (node == tail) {
        tail = node->prev;
    } else {
        node->next->prev = node->prev;
    }
    if (node == head) {
        head = node->next;
    } else {
        node->prev->next = node->next;
    }

    pool.push(node);
    fds[fd] = NULL;
    return true;
}