/* Copyright (c) 1993 Stephen F. White */

#include "cool.h"
#include "proto.h"

static List *doinsert (List * list, Var value, int pos);
static int	sort_up(const void *a, const void *b);
static int	sort_down(const void *a, const void *b);

List *list_new (int size)
{
  List *new;

  new = MALLOC (List, 1);
  new->len = size;
  new->ref = 1;
  if (size) {
    new->el = MALLOC (Var, size);
  } else {
    new->el = 0;
  }
  new->mem = size;
  return new;
}

List *list_setadd (List * list, Var value)
{
  List *ret;

  ret = list;
  if (list_ismember (value, list)) {
    var_free (value);
    return ret;
  }
  return list_append (list, value, 0);
}

List *list_setremove (List * list, Var value)
{
  List *ret;
  int t;

  ret = list;
  if ((t = list_ismember (value, list))) {
    ret = list_delete (list, t);
  }
  return ret;
}

int list_ismember (Var element, List * list)
{
  int i;

  for (i = 0; i < list->len; i++) {
    if (var_eq (element, list->el[i])) {
      return i + 1;
    }
  }
  return 0;
}

List *list_assign (List * list, Var value, int pos)
{
  List	*new;
  int i;

  if (list->ref == 1) {
    var_free (list->el[pos - 1]);
    list->el[pos - 1] = value;
    return list;
  }

  new = list_new(list->len);
  for (i = 0; i < list->len; i++) {
    if (i == pos - 1) {
      new->el[i] = value;
    } else {
      new->el[i] = var_dup (list->el[i]);
    }
  }

  list_free (list);
  return new;
}

static List *doinsert (List * list, Var value, int pos)
{
  List *new;
  int i;

  new = list_new (list->len + 1);
  for (i = 0; i < pos - 1; i++) {
    new->el[i] = var_dup (list->el[i]);
  }
  new->el[pos - 1] = value;
  for (i = pos; i < new->len; i++) {
    new->el[i] = var_dup (list->el[i - 1]);
  }
  list_free (list);
  return new;
}

List *list_insert (List * list, Var value, int pos)
{
  if (pos < 1 || pos > list->len) {
    pos = 1;
  }
  return (doinsert (list, value, pos));
}

List *list_append (List * list, Var value, int pos)
{
  if (pos < 1 || pos > list->len) {
    pos = list->len;
  }
  return (doinsert (list, value, pos + 1));
}

List *list_delete (List * list, int pos)
{
  List *new;
  int i;

  new = list_new (list->len - 1);
  for (i = 0; i < pos - 1; i++) {
    new->el[i] = var_dup (list->el[i]);
  }
  for (i = pos - 1; i < new->len; i++) {
    new->el[i] = var_dup (list->el[i + 1]);
  }
  list_free (list);             /* free old list */
  return new;
}

List *list_subset (List * list, int lower, int upper)
{
  List *r;
  int i;

  r = list_new (upper - lower + 1);
  for (i = lower - 1; i < upper; i++) {
    r->el[i - lower + 1] = var_dup (list->el[i]);
  }
  return r;
}

#ifdef INLINE
void list__free (List * list)
{
  int i;

  for (i = 0; i < list->len; i++) {
    var_free (list->el[i]);
  }
  if (list->len) {
    FREE (list->el);
  }
  FREE (list);
}

#else /* !INLINE */

void list_free (List * list)
{
  int i;

  if (!--list->ref) {
    for (i = 0; i < list->len; i++) {
      var_free (list->el[i]);
    }
    if (list->len) {
      FREE (list->el);
    }
    FREE (list);
  }
}

List *list_dup (List * list)
{
  list->ref++;
  return list;
}
#endif /* !INLINE */

List *explode (const char *str, const char *sep, int nwords)
{
  const char *word_start;
  List *list = list_new (nwords);
  int i;

  for (i = 0; i < nwords; i++) {
	while ( *str && strncmp( str, sep, strlen( sep ) ) == 0 )
	{
	    str += strlen( sep );
	}

    word_start = str;
	/* skip to next separator */
	while (*str && strncmp( str, sep, strlen( sep ) ) != 0 )
	{
      str++;
	}
    list->el[i].type = STR;
    list->el[i].v.str = string_ncpy (word_start, str - word_start);
  }
  return list;
}

List * list_sort(List *list, int reverse)
{
    List	*ret;
    int		i;

    if (list->ref == 1) {
	ret = list;
    } else {
	ret = list_new(list->len);
	for (i = 0; i < list->len; i++) {
	    ret->el[i] = var_dup(list->el[i]);
	}
	list_free(list);
    }
    qsort(ret->el, ret->len, sizeof(Var), (reverse ? sort_down : sort_up));
    return ret;
}

static int sort_up(const void *a, const void *b)
{
    return var_compare(*((const Var *) a), *((const Var *) b));
}

static int sort_down(const void *a, const void *b)
{
    return var_compare(*((const Var *) b), *((const Var *) a));
}