/***************************************************************************
                          list.c  -  description
                             -------------------
    begin                : Sun Sep 2 2001
    copyright            : (C) 2001 by Michael Speck
    email                : kulkanie@gmx.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <stdlib.h>

#include "list.h"

/*
====================================================================
Create a new list
  auto_delete:  Free memory of data pointer when deleting entry
  callback:     Use this callback to free memory of data including
                the data pointer itself.
Return Value: List pointer
====================================================================
*/
List *list_create( int auto_delete, void (*callback)(void*) )
{
    List *list = calloc( 1, sizeof( List ) );
    list->head = calloc( 1, sizeof( ListEntry ) );
    list->tail = calloc( 1, sizeof( ListEntry ) );
    list->head->next = list->tail;
    list->head->prev = list->head;
    list->tail->next = list->tail;
    list->tail->prev = list->head;
    list->auto_delete = auto_delete;
    list->callback = callback;
    list->cur_entry = list->head;
    return list;
}
/*
====================================================================
Delete list and entries.
====================================================================
*/
void list_delete( List *list )
{
    list_clear( list );
    free( list->head );
    free( list->tail );
    free( list );
}
/*
====================================================================
Delete all entries but keep the list. Reset current_entry to head
pointer.
====================================================================
*/
void list_clear( List *list )
{
    while( !list_empty( list ) ) list_delete_pos( list, 0 );
}
/*
====================================================================
Insert new item at position.
Return Value: True if successful else False.
====================================================================
*/
int list_insert( List *list, void *item, int pos )
{
    int         i;
    ListEntry    *cur = list->head;
    ListEntry    *new_entry = 0;

    /* check if insertion possible */
    if ( pos < 0 || pos > list->count ) return 0;
    if ( item == 0 ) return 0;
    /* get to previous entry */
    for (i = 0; i < pos; i++) cur = cur->next;
    /* create and anchor new entry */
    new_entry = calloc( 1, sizeof( ListEntry ) );
    new_entry->item = item;
    new_entry->next = cur->next;
    new_entry->prev = cur;
    cur->next->prev = new_entry;
    cur->next = new_entry;
    list->count++;
    return 1;
}
/*
====================================================================
Add new item at the end of the list.
====================================================================
*/
int list_add( List *list, const void *item )
{
    ListEntry    *new_entry = 0;
    /* check if insertion possible */
    if ( item == 0 ) return 0;
    /* create and anchor new entry */
    new_entry = calloc( 1, sizeof( ListEntry ) );
    new_entry->item = item;
    new_entry->next = list->tail;
    new_entry->prev = list->tail->prev;
    list->tail->prev->next = new_entry;
    list->tail->prev = new_entry;
    list->count++;
    return 1;
}
/*
====================================================================
Delete item at position. If this was the current entry update
current_entry to valid previous pointer.
Return Value: True if successful else False.
====================================================================
*/
int list_delete_pos( List *list, int pos )
{
    int         i;
    ListEntry  *cur = list->head;

    /* check if deletion possbile */
    if ( list_empty( list ) ) return 0;
    if ( pos < 0 || pos >= list->count ) return 0;
    /* get to correct entry */
    for ( i = 0; i <= pos; i++ ) cur = cur->next;
    /* modify anchors */
    cur->next->prev = cur->prev;
    cur->prev->next = cur->next;
    /* decrease counter */
    list->count--;
    /* check current_entry */
    if ( list->cur_entry == cur )
        list->cur_entry = list->cur_entry->prev;
    /* free memory */
    if ( list->auto_delete ) {
        if ( list->callback )
            (list->callback)( cur->item );
        else
            free( cur->item );
    }
    free( cur );
    return 1;
}
/*
====================================================================
Delete item if in list. If this was the current entry update
current_entry to valid previous pointer.
Return Value: True if successful else False.
====================================================================
*/
int list_delete_item( List *list, void *item )
{
    return list_delete_pos( list, list_check( list, item ) );
}
/*
====================================================================
Delete entry.
Return Value: True if successful else False.
====================================================================
*/
int list_delete_entry( List *list, ListEntry *entry )
{
    /* delete possible? */
    if ( entry == 0 ) return 0;
    if ( list->count == 0 ) return 0;
    if ( entry == list->head || entry == list->tail ) return 0;
    /* adjust anchor and counter */
    entry->prev->next = entry->next;
    entry->next->prev = entry->prev;
    list->count--;
    /* check current_entry */
    if ( list->cur_entry == entry )
        list->cur_entry = list->cur_entry->prev;
    /* free memory */
    if ( list->auto_delete ) {
        if ( list->callback )
            (list->callback)( entry->item );
        else
            free( entry->item );
    }
    free( entry );
    return 1;
}
/*
====================================================================
Get item from position if in list.
Return Value: Item pointer if found else Null pointer.
====================================================================
*/
void* list_get( List *list, int pos )
{
    int i;
    ListEntry *cur = list->head;

    if ( pos < 0 || pos >= list->count ) return 0;
    for ( i = 0; i <= pos; i++ ) cur = cur->next;
    return cur->item;
}
/*
====================================================================
Check if item's in list.
Return Value: Position of item else -1.
====================================================================
*/
int list_check( List *list, void *item )
{
    int pos = -1;
    ListEntry *cur = list->head->next;
    while ( cur != list->tail ) {
        pos++;
        if ( cur->item == item ) break;
        cur = cur->next;
    }
    if ( cur == list->tail ) pos = -1; /* item not found */
    return pos;
}
/*
====================================================================
Return first item stored in list and set current_entry to this
entry.
Return Value: Item pointer if found else Null pointer.
====================================================================
*/
void* list_first( List *list )
{
    list->cur_entry = list->head->next;
    return list->head->next->item;
}
/*
====================================================================
Return last item stored in list and set current_entry to this
entry.
Return Value: Item pointer if found else Null pointer.
====================================================================
*/
void* list_last( List *list )
{
    list->cur_entry = list->tail->prev;
    return list->tail->prev->item;
}
/*
====================================================================
Return item in current_entry.
Return Value: Item pointer if found else Null pointer.
====================================================================
*/
void* list_current( List *list )
{
    return list->cur_entry->item;
}
/*
====================================================================
Reset current_entry to head of list.
====================================================================
*/
void list_reset( List *list )
{
    list->cur_entry = list->head;
}
/*
====================================================================
Get next item and update current_entry (reset if tail reached)
Return Value: Item pointer if found else Null (if tail of list).
====================================================================
*/
void* list_next( List *list )
{
    list->cur_entry = list->cur_entry->next;
    if ( list->cur_entry == list->tail ) list_reset( list );
    return list->cur_entry->item;
}
/*
====================================================================
Get previous item and update current_entry.
Return Value: Item pointer if found else Null (if head of list).
====================================================================
*/
void* list_prev( List *list )
{
    list->cur_entry = list->cur_entry->prev;
    return list->cur_entry->item;
}
/*
====================================================================
Delete the current entry if not tail or head. This is the entry
that contains the last returned item by list_next/prev().
Return Value: True if it  was a valid deleteable entry.
====================================================================
*/
int list_delete_current( List *list )
{
	if ( list->cur_entry == 0 || list->cur_entry == list->head || list->cur_entry == list->tail ) return 0;
	list_delete_entry( list, list->cur_entry );
	return 1;
}
/*
====================================================================
Check if list is empty.
Return Value: True if list counter is 0 else False.
====================================================================
*/
int list_empty( List *list )
{
    return list->count == 0;
}
/*
====================================================================
Return entry containing the passed item.
Return Value: True if entry found else False.
====================================================================
*/
ListEntry *list_entry( List *list, void *item )
{
    ListEntry *entry = list->head->next;
    while ( entry != list->tail ) {
        if ( entry->item == item ) return entry;
        entry = entry->next;
    }
    return 0;
}
/*
====================================================================
Transfer an entry from one list to another list by removing from
'source' and adding to 'dest' thus if source does not contain
the item this is equvalent to list_add( dest, item ).
====================================================================
*/
void list_transfer( List *source, List *dest, void *item )
{
    int old_auto_flag;
    /* add to destination */
    list_add( dest, item );
    /* as the pointer is added to dest without changes only the empty
    entry must be deleted in source */
    old_auto_flag = source->auto_delete;
    source->auto_delete = LIST_NO_AUTO_DELETE;
    list_delete_item( source, item );
    source->auto_delete = old_auto_flag;
}
/*
====================================================================
Deqeue the first list entry. (must not use auto_delete therefore)
====================================================================
*/
void *list_dequeue( List *list )
{
    void *item;
    if ( list->count > 0 ) {
        item = list->head->next->item;
        list_delete_pos( list, 0 );
        return item;
    }
    else
        return 0;
}
