/**********
Copyright 1990 Regents of the University of California.  All rights reserved.
Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
**********/

/* Wordlist manipulation stuff.  */

#include "ngspice/ngspice.h"
#include "ngspice/bool.h"
#include "ngspice/wordlist.h"


/* Determine the length of a word list. */
int
wl_length(const wordlist *wl)
{
    int i = 0;

    for (; wl; wl = wl->wl_next)
        i++;

    return (i);
}


/* Free the storage used by a word list. */
void
wl_free(wordlist *wl)
{
    while (wl) {
        wordlist *next = wl->wl_next;
        tfree(wl->wl_word);
        tfree(wl);
        wl = next;
    }
}


/* Copy a wordlist and the words. */
wordlist *
wl_copy(wordlist *wl)
{
    wordlist *first = NULL, *last = NULL;

    for (; wl; wl = wl->wl_next)
        wl_append_word(&first, &last, copy(wl->wl_word));

    return (first);
}


/* Substitute a wordlist for one element of a wordlist, and return a
 * pointer to the last element of the inserted list.  */
wordlist *
wl_splice(wordlist *elt, wordlist *list)
{

    if (list)
        list->wl_prev = elt->wl_prev;
    if (elt->wl_prev)
        elt->wl_prev->wl_next = list;
    if (list) {
        while (list->wl_next)
            list = list->wl_next;
        list->wl_next = elt->wl_next;
    }
    if (elt->wl_next)
        elt->wl_next->wl_prev = list;
    tfree(elt->wl_word);
    tfree(elt);
    return (list);
}



static void
printword(char *string, FILE *fp)
{
    if (string)
        while (*string)
            putc(strip(*string++), fp);
}


/* Print a word list. (No \n at the end...) */
void
wl_print(const wordlist *wl, FILE *fp)
{
    for (; wl; wl = wl->wl_next) {
        printword(wl->wl_word, fp);
        if (wl->wl_next)
            putc(' ', fp);
    }
}


/* Turn an array of char *'s into a wordlist. */
wordlist *
wl_build(char **v)
{
    wordlist *first = NULL;
    wordlist *last = NULL;

    while (*v)
        wl_append_word(&first, &last, copy(*v++));

    return (first);
}


char **
wl_mkvec(wordlist *wl)
{
    int  len = wl_length(wl);
    char **vec = TMALLOC(char *, len + 1);

    int i;

    for (i = 0; i < len; i++) {
        vec[i] = copy(wl->wl_word);
        wl = wl->wl_next;
    }
    vec[i] = NULL;

    return (vec);
}


/* Nconc two wordlists together. */
wordlist *
wl_append(wordlist *wlist, wordlist *nwl)
{
    wordlist *wl;
    if (wlist == NULL)
        return (nwl);
    if (nwl == NULL)
        return (wlist);
    for (wl = wlist; wl->wl_next; wl = wl->wl_next)
        ;
    wl->wl_next = nwl;
    nwl->wl_prev = wl;
    return (wlist);
}


/* Reverse a word list. */
wordlist *
wl_reverse(wordlist *wl)
{
    if (!wl)
        return (wl);

    for (;;) {
        SWAP(wordlist *, wl->wl_next, wl->wl_prev);
        if (!wl->wl_prev)
            return (wl);
        wl = wl->wl_prev;
    }
}


/* Convert a wordlist into a string. */
char *
wl_flatten(wordlist *wlist)
{
    char *buf;
    wordlist *wl;
    size_t len = 0;

    for (wl = wlist; wl; wl = wl->wl_next)
        len += strlen(wl->wl_word) + 1;

    buf = TMALLOC(char, len + 1);
    *buf = '\0';

    for (wl = wlist; wl; wl = wl->wl_next) {
        (void) strcat(buf, wl->wl_word);
        if (wl->wl_next)
            (void) strcat(buf, " ");
    }
    return (buf);

}


/* Return the nth element of a wordlist, or the last one if n is too
 * big.  Numbering starts at 0...  */
wordlist *
wl_nthelem(int i, wordlist *wl)
{
    while ((i-- > 0) && wl->wl_next)
        wl = wl->wl_next;

    return (wl);
}


static int
wlcomp(const void *a, const void *b)
{
    const char **s = (const char **) a;
    const char **t = (const char **) b;
    return (strcmp(*s, *t));
}


void
wl_sort(wordlist *wl)
{
    size_t i = 0;
    wordlist *ww = wl;
    char **stuff;

    for (i = 0; ww; i++)
        ww = ww->wl_next;
    if (i < 2)
        return;
    stuff = TMALLOC(char *, i);
    for (i = 0, ww = wl; ww; i++, ww = ww->wl_next)
        stuff[i] = ww->wl_word;
    qsort(stuff, i, sizeof (char *), wlcomp);
    for (i = 0, ww = wl; ww; i++, ww = ww->wl_next)
        ww->wl_word = stuff[i];
    tfree(stuff);
}


/* Return a range of wordlist elements... */
wordlist *
wl_range(wordlist *wl, int low, int up)
{
    wordlist *tt;
    bool rev = FALSE;

    if (low > up) {
        SWAP(int, up, low);
        rev = TRUE;
    }
    up -= low;
    while (wl && (low > 0)) {
        tt = wl->wl_next;
        tfree(wl->wl_word);
        tfree(wl);
        wl = tt;
        if (wl)
            wl->wl_prev = NULL;
        low--;
    }
    tt = wl;
    while (tt && (up > 0)) {
        tt = tt->wl_next;
        up--;
    }
    if (tt && tt->wl_next) {
        wl_free(tt->wl_next);
        tt->wl_next = NULL;
    }
    if (rev)
        wl = wl_reverse(wl);
    return (wl);
}


/*
 *  prepend a new `word'
 *     to the front of the given `wlist' wordlist
 *  and return this new list
 */

wordlist *
wl_cons(char *word, wordlist *wlist)
{
    wordlist *w = TMALLOC(wordlist, 1);
    w->wl_next = wlist;
    w->wl_prev = NULL;
    w->wl_word = word;

    if (wlist)
        wlist->wl_prev = w;

    return (w);
}


/*
 * given a wordlist
 *   described by a `first' and `last' wordlist element
 * append a new `word'
 *   and update the given `first' and `last' pointers accordingly
 */

void
wl_append_word(wordlist **first, wordlist **last, char *word)
{
    wordlist *w = TMALLOC(wordlist, 1);
    w->wl_next = NULL;
    w->wl_prev = (*last);
    w->wl_word = word;

    if (*last)
        (*last)->wl_next = w;
    else
        (*first) = w;

    (*last) = w;
}


/*
 * given a pointer `wl' into a wordlist
 *   cut off this list from its preceding elements
 *   and return itself
 */

wordlist *
wl_chop(wordlist *wl)
{
    if (wl && wl->wl_prev) {
        wl->wl_prev->wl_next = NULL;
        wl->wl_prev = NULL;
    }
    return (wl);
}


/*
 * given a pointer `wl' into a wordlist
 *   cut off the rest of the list
 *   and return this rest
 */

wordlist *
wl_chop_rest(wordlist *wl)
{
    wordlist *rest = wl->wl_next;
    wl->wl_next = NULL;
    if(rest)
        rest->wl_prev = NULL;
    return (rest);
}


/*
 * search for a string in a wordlist
 */

wordlist *
wl_find(const char *string, const wordlist *wl)
{
    if (!string)
        return NULL;

    for (; wl; wl = wl->wl_next)
        if (eq(string, wl->wl_word))
            break;
    return ((wordlist *) wl);
}


/*
 * delete elements from a wordlist
 *   from inclusive `from'
 *   up to exclusive `to'
 */

void
wl_delete_slice(wordlist *from, wordlist *to)
{
    wordlist *prev;

    if (from == to)
        return;

    prev = from->wl_prev;

    if(prev)
        prev->wl_next = to;

    if (to) {
        to->wl_prev->wl_next = NULL;
        to->wl_prev = prev;
    }

    wl_free(from);
}
