// vim: set ts=4 sw=4 tw=80 et:
#include "./collections.h"

size_t dl_list_length(const dl_list_t* self) {
    size_t length = 0;
    dl_node_t* i = self->first;

    while (i != NULL) {
        length++;
        i = i->next;
    }

    return length;
}

dl_node_t* dl_list_ith(const dl_list_t* self, const size_t position) {
    size_t found;
    dl_node_t* elem = dl_list_ith_or_last(self, position, &found);
    return found != position ? NULL : elem;
}

dl_node_t* dl_list_ith_or_last(const dl_list_t* self, const size_t position,
    size_t* returned) {
    dl_node_t* to_reach = self->first;
    size_t i;
    for (i = 0; i < position && to_reach->next != NULL; i++) {
        to_reach = to_reach->next;
    }
    *returned = i;
    return to_reach;
}

inline dl_node_t* dl_list_alloc_node(void* element) {
    dl_node_t* node = malloc(sizeof(node));
    node->element = element;
    node->next = NULL;
    node->prev = NULL;
}

inline void dl_list_free_node(dl_node_t* node) {
    if (node->element != NULL) {
        free(node->element);
    }
    free(node);
}

inline void dl_list_init(dl_list_t* self) {
    self->first = NULL;
}

inline void dl_list_destroy(dl_list_t* self) {
    while(self->first != NULL) {
        free(dl_list_remove(self, 0));
    }
}

size_t dl_list_copy_slice(const dl_list_t* self, dl_list_t* destination,
    const size_t from, const size_t to) {
    if (from >= to) {
        return 0;
    }
    dl_list_destroy(destination);
    size_t start, i;
    dl_node_t* to_copy = dl_list_ith_or_last(self, to, &start);
    if (to_copy == NULL) {
        return 0;
    }

    for(i = start; i > from; i--) {
        dl_node_t* copied = malloc(sizeof(dl_node_t));
        memcpy(copied, self, sizeof(dl_node_t));
        dl_list_insert(destination, 0, copied);
    }

    return start - from;
}

int dl_list_insert(dl_list_t* self, const size_t position, dl_node_t* node) {
    if (position == 0) {
        node->next = self->first;
        node->prev = NULL;
        self->first = node;
        return 0;
    }

    dl_node_t* to_reach = dl_list_ith(self, position - 1);

    if (to_reach == NULL) {
        return 2;
    }

    to_reach->next->prev = node;
    node->next = to_reach->next;
    node->prev = to_reach;
    to_reach->next = node;
    return 0;
}

dl_node_t* dl_list_remove(dl_list_t* self, const size_t position) {
    if(position == 0) {
        dl_node_t* to_remove = self->first;
        self->first = to_remove->next;
        return to_remove;
    }

    dl_node_t* to_reach = dl_list_ith(self, position - 1);
    if (to_reach == NULL || to_reach->next == NULL) {
        return NULL;
    }

    dl_node_t* to_remove = to_reach->next;
    to_reach->next = to_reach->next->next;
    to_reach->next->prev = to_reach;
    return to_remove;
}
