#include "chain.hpp"


CPoolPTS<buf_t> buf_pool;


void buf_t::debug() {
    log(io, "%p: %zu/%zu -> %p", this, off, len, next);
}


buf_t* chain_t::pop_head() {
    assert(head);
    buf_t* node = head;
    if (head == tail) {
        head = NULL;
        tail = NULL;
    } else {
        head = head->next;
    }
    --bufs;
    node->next = NULL;
    return node;
}


bool chain_t::add_data(chain_t* c) {
    if (!c->head) {
        return false;
    }
    c->head->rewind(); // only for first buf
    if (!tail) {
        assert(!bufs && !head);
        head = c->head;
        tail = c->tail;
        bufs = c->bufs;
    } else if (tail->has_space()) {
        return false; // should not happen in our scenario
    } else {
        tail->next = c->head;
        tail = c->tail;
        bufs += c->bufs;
    }
    is_initial_data = false; // not sure about that
    c->head = NULL;
    c->tail = NULL;
    c->bufs = 0;
    return true;
}


void chain_t::add_data(const char* buf, size_t len) {
    char* b;
    size_t l;
    while (len) {
        buf_t* nbuf = NULL;
        if (!get_space(b, l)) {
            nbuf = buf_pool.pop();
            nbuf->get_space(b, l);
        }
        l = MIN(l, len);
        memcpy(b, buf, l);
        len -= l;
        buf += l;
        if (nbuf) {
            nbuf->add_data(l);
            add_data(nbuf);
        } else {
            add_data(l);
        }
    }
}


/*void chain_t::prepend(chain_t* c) {
    if (!c->head) {
        return;
    } else {
        assert(c->bufs && c->tail);
    }

    bufs += c->bufs;
    c->tail->next = head;
    head = c->head;
    is_initial_data = c->is_initial_data;

    c->head = NULL;
    c->tail = NULL;
    c->bufs = 0;
    c->is_initial_data = true;
}*/


void chain_t::prepend(buf_t* b) {
    assert(is_initial_data);

    if (!b->has_data()) {
        buf_pool.push(b);
        return;
    }

    if (!tail) {
        b->next = NULL;
        head = b;
        tail = b;
    } else {
        assert(false); // TODO: have to review is_done() checks if we need this
        b->next = head;
        head = b;
    }
    ++bufs;
}


tristate_t chain_t::startswith(const char* s, size_t sl) const {
    if (!is_initial_data) return TRI_FALSE; // cannot know (anymore)
    if (!head || !head->has_data()) return TRI_NONE; // cannot know (yet)
    const char* b;
    size_t bl;
    head->get_data(b, bl);
    assert(b == (const char*)head); // as buf is first member and its the initial data
    if (strncmp(b, s, MIN(bl, sl)) != 0) return TRI_FALSE; // cannot be
    if (bl < sl) return TRI_NONE; // cannot know (yet)
    return TRI_TRUE; // can be sure now
}


tristate_t chain_t::is_http_req() const {
    return startswith("GET /", cstrlen("GET /"));
}


tristate_t chain_t::is_http_resp() const {
    return startswith("HTTP/1", cstrlen("HTTP/1"));
}


void chain_t::debug() {
    log(io, "chain %p (%p->%p #%zu):", this, head, tail, bufs);
    buf_t* node = head;
    while (node) {
        node->debug();
        node = node->next;
    }
}