/*
 * Copyright 1993, 1995 Christopher Seiwald.
 *
 * This file is part of Jam - see jam.c for Copyright information.
 */

# include "jam.h"
# include "newstr.h"
# include "lists.h"

/*
 * lists.c - maintain lists of strings
 *
 * This implementation essentially uses a singly linked list, but
 * guarantees that the head element of every list has a valid pointer
 * to the tail of the list, so the new elements can efficiently and
 * properly be appended to the end of a list.
 *
 * To avoid massive allocation, list_free() just tacks the whole freed
 * chain onto freelist and list_new() looks on freelist first for an
 * available list struct.  list_free() does not free the strings in the
 * chain: it lazily lets list_new() do so.
 *
 * 08/23/94 (seiwald) - new list_append()
 * 09/07/00 (seiwald) - documented lol_*() functions
 */

static LIST *freelist = 0;  /* junkpile for list_free() */

/*
 * list_append() - append a list onto another one, returning total
 */

LIST * list_append( LIST * l, LIST * nl )
{
    if ( !nl )
    {
        /* Just return l */
    }
    else if ( !l )
    {
        l = nl;
    }
    else
    {
        /* Graft two non-empty lists. */
        l->tail->next = nl;
        l->tail = nl->tail;
    }

    return l;
}

/*
 * list_new() - tack a string onto the end of a list of strings
 */

LIST * list_new( LIST * head, char * string )
{
    LIST * l;

    if ( DEBUG_LISTS )
        printf( "list > %s <\n", string );

    /* Get list struct from freelist, if one available.  */
    /* Otherwise allocate. */
    /* If from freelist, must free string first */

    if ( freelist )
    {
        l = freelist;
        freestr( l->string );
        freelist = freelist->next;
    }
    else
    {
        l = (LIST *)BJAM_MALLOC( sizeof( LIST ) );
    }

    /* If first on chain, head points here. */
    /* If adding to chain, tack us on. */
    /* Tail must point to this new, last element. */

    if ( !head ) head = l;
    else head->tail->next = l;
    head->tail = l;
    l->next = 0;

    l->string = string;

    return head;
}


/*
 * list_copy() - copy a whole list of strings (nl) onto end of another (l).
 */

LIST * list_copy( LIST * l, LIST * nl )
{
    for ( ; nl; nl = list_next( nl ) )
        l = list_new( l, copystr( nl->string ) );
    return l;
}


/*
 * list_sublist() - copy a subset of a list of strings.
 */

LIST * list_sublist( LIST * l, int start, int count )
{
    LIST * nl = 0;
    for ( ; l && start--; l = list_next( l ) );
    for ( ; l && count--; l = list_next( l ) )
        nl = list_new( nl, copystr( l->string ) );
    return nl;
}


static int str_ptr_compare( void const * va, void const * vb )
{
    char * a = *( (char * *)va );
    char * b = *( (char * *)vb );
    return strcmp(a, b);
}


LIST * list_sort( LIST * l )
{
    int len;
    int ii;
    char * * strings;
    LIST * listp;
    LIST * result = 0;

    if ( !l )
        return L0;

    len = list_length( l );
    strings = (char * *)BJAM_MALLOC( len * sizeof(char*) );

    listp = l;
    for ( ii = 0; ii < len; ++ii )
    {
        strings[ ii ] = listp->string;
        listp = listp->next;
    }

    qsort( strings, len, sizeof( char * ), str_ptr_compare );

    for ( ii = 0; ii < len; ++ii )
        result = list_append( result, list_new( 0, strings[ ii ] ) );

    BJAM_FREE( strings );

    return result;
}


/*
 * list_free() - free a list of strings
 */

void list_free( LIST * head )
{
    /* Just tack onto freelist. */
    if ( head )
    {
        head->tail->next = freelist;
        freelist = head;
    }
}


/*
 * list_pop_front() - remove the front element from a list of strings
 */

LIST * list_pop_front( LIST * l )
{
    LIST * result = l->next;
    if ( result )
    {
        result->tail = l->tail;
        l->next = L0;
        l->tail = l;
    }
    list_free( l );
    return result;
}


/*
 * list_print() - print a list of strings to stdout
 */

void list_print( LIST * l )
{
    LIST * p = 0;
    for ( ; l; p = l, l = list_next( l ) )
        if ( p )
            printf( "%s ", p->string );
    if ( p )
        printf( "%s", p->string );
}


/*
 * list_length() - return the number of items in the list
 */

int list_length( LIST * l )
{
    int n = 0;
    for ( ; l; l = list_next( l ), ++n );
    return n;
}


int list_in( LIST * l, char * value )
{
    for ( ; l; l = l->next )
        if ( strcmp( l->string, value ) == 0 )
            return 1;
    return 0;
}


LIST * list_unique( LIST * sorted_list )
{
    LIST * result = 0;
    LIST * last_added = 0;

    for ( ; sorted_list; sorted_list = sorted_list->next )
    {
        if ( !last_added || strcmp( sorted_list->string, last_added->string ) != 0 )
        {
            result = list_new( result, sorted_list->string );
            last_added = sorted_list;
        }
    }
    return result;
}


/*
 * lol_init() - initialize a LOL (list of lists).
 */

void lol_init( LOL * lol )
{
    lol->count = 0;
}


/*
 * lol_add() - append a LIST onto an LOL.
 */

void lol_add( LOL * lol, LIST * l )
{
    if ( lol->count < LOL_MAX )
        lol->list[ lol->count++ ] = l;
}


/*
 * lol_free() - free the LOL and its LISTs.
 */

void lol_free( LOL * lol )
{
    int i;
    for ( i = 0; i < lol->count; ++i )
        list_free( lol->list[ i ] );
    lol->count = 0;
}


/*
 * lol_get() - return one of the LISTs in the LOL.
 */

LIST * lol_get( LOL * lol, int i )
{
    return i < lol->count ? lol->list[ i ] : 0;
}


/*
 * lol_print() - debug print LISTS separated by ":".
 */

void lol_print( LOL * lol )
{
    int i;

    for ( i = 0; i < lol->count; ++i )
    {
        if ( i )
            printf( " : " );
        list_print( lol->list[ i ] );
    }
}

#ifdef HAVE_PYTHON

PyObject *list_to_python(LIST *l)
{
    PyObject *result = PyList_New(0);

    for (; l; l = l->next)
    {
        PyObject* s = PyString_FromString(l->string);
        PyList_Append(result, s);
        Py_DECREF(s);
    }

    return result;
}

LIST *list_from_python(PyObject *l)
{
    LIST * result = 0;

    Py_ssize_t i, n;
    n = PySequence_Size(l);
    for (i = 0; i < n; ++i)
    {
        PyObject *v = PySequence_GetItem(l, i);        
        result = list_new (result, newstr (PyString_AsString(v)));
        Py_DECREF(v);
    }

    return result;
}

#endif
