/***************************************************************************
                          dynlist.c  -  description
                             -------------------
    begin                : Sat Apr 8 2000
    copyright            : (C) 2000 by 
    email                : 
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "dynlist.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void DL_Init(DList *dlist)
{
    dlist->counter = 0;
    dlist->head.prev = dlist->tail.next = 0;
    dlist->head.next = &dlist->tail;
    dlist->tail.prev = &dlist->head;
    dlist->head.data = dlist->tail.data = 0;
    dlist->flags = DL_NONE;
    dlist->cb_destroy = 0;
}

int DL_Insert(DList *dlist, unsigned int index, void *item)
{
    int i;
    DL_Entry    *cur = &dlist->head;
    DL_Entry    *new_entry;

    if (index > dlist->counter) {
        fprintf(stderr, "ERR: dl_insert: index %i out of range...\n", index);
        return 1;
    }
    if (item == 0) {
        fprintf(stderr, "ERR: dl_insert: item is NULL...\n");
        return 1;
    }

    for (i = 0; i < index; i++)
        cur = cur->next;
    new_entry = (DL_Entry*)malloc(sizeof(DL_Entry));
    new_entry->data = item;
    new_entry->next = cur->next;
    new_entry->prev = cur;
    cur->next->prev = new_entry;
    cur->next = new_entry;
    dlist->counter++;

    return 0;
}

int DL_Add(DList *dlist, void *item)
{
    return DL_Insert(dlist, dlist->counter, item);
}

int  DL_DeleteEntry(DList *dlist, DL_Entry *e)
{
    if (e == 0) {
        fprintf(stderr, "ERR: dl_delete: entry is NULL...\n");
        return 1;
    }
    if (dlist->counter == 0) {
        fprintf(stderr, "ERR: dl_delete: list is empty...\n");
        return 1;
    }
    if (e == &dlist->head || e == &dlist->tail) {
        fprintf(stderr, "ERR: dl_delete: trying to delete head or tail..\n");
        return 1;
    }

    e->prev->next = e->next;
    e->next->prev = e->prev;
    dlist->counter--;

    if (dlist->flags & DL_AUTODELETE) {
        if (dlist->flags & DL_NOCALLBACK)
            free(e->data);
        else {
            if (dlist->cb_destroy == 0) {
                fprintf(stderr, "ERR: dl_delete: no destroy callback installed...\n");
                free(e);
                return 1;
            }
            (dlist->cb_destroy)(e->data);
        }
    }
    free(e);

    return 0;
}

int DL_DeletePtr(DList *dlist, void *item)
{
    int         i;
    DL_Entry    *cur = &dlist->head;

    if (item == 0) {
        fprintf(stderr, "ERR: dl_delete: item is NULL...\n");
        return 1;
    }
    if (dlist->counter == 0) {
        fprintf(stderr, "ERR: dl_delete: list is empty...\n");
        return 1;
    }

    for (i = 0; i <= dlist->counter; i++)
        if (cur->next != &dlist->tail) {
            cur = cur->next;
            if (cur->data == item)
                break;
        }
        else {
            fprintf(stderr, "ERR: dl_delete: list does not contain item 0x%x...\n", (int)item);
            return 1;
        }

    cur->next->prev = cur->prev;
    cur->prev->next = cur->next;
    dlist->counter--;
    cur->next = cur->prev = 0;

    if (dlist->flags & DL_AUTODELETE) {
        if (dlist->flags & DL_NOCALLBACK)
            free(cur->data);
        else {
            if (dlist->cb_destroy == 0) {
                fprintf(stderr, "ERR: dl_delete: no destroy callback installed...\n");
                free(cur);
                return 1;
            }
            (dlist->cb_destroy)(cur->data);
        }
    }
    free(cur);

    return 0;
}

int DL_Delete(DList *dlist, unsigned int index)
{
    int         i;
    DL_Entry    *cur = &dlist->head;

    if (index >= dlist->counter) {
        fprintf(stderr, "ERR: dl_delete: index %i out of range...\n", index);
        return 1;
    }
    if (dlist->counter == 0) {
        fprintf(stderr, "ERR: dl_delete: list is empty...\n");
        return 1;
    }

    for (i = 0; i <= index; i++)
        cur = cur->next;

    cur->next->prev = cur->prev;
    cur->prev->next = cur->next;
    dlist->counter--;
    cur->next = cur->prev = 0;

    if (dlist->flags & DL_AUTODELETE) {
        if (dlist->flags & DL_NOCALLBACK)
            free(cur->data);
        else {
            if (dlist->cb_destroy == 0) {
                fprintf(stderr, "ERR: dl_delete: no destroy callback installed...\n");
                free(cur);
                return 1;
            }
            (dlist->cb_destroy)(cur->data);
        }
    }
    free(cur);

    return 0;
}

void* DL_Get(DList *dlist, int index)
{
    unsigned int i;
    DL_Entry *cur = &dlist->head;

    if (index >= dlist->counter) {
        fprintf(stderr, "ERR: dl_get: index %i out of range...\n", index);
        return 0;
    }

    for (i = 0; i <= (unsigned)index; i++)
        cur = cur->next;

    return cur->data;
}

void DL_Clear(DList *dlist)
{
    DL_Entry    *cur = dlist->head.next;
    DL_Entry    *next;

    while (cur != &dlist->tail) {
        next = cur->next;
        DL_DeleteEntry(dlist, cur);
        cur = next;
    }

}
