/* 
   list.c
   List specific functions.

   Datablk is a library that provides a "typeless" type for
   programmable systems.

   (C) Copyright 1999 by Nicholas Rusnov
*/

#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <stdarg.h>

#include "flags.h"
#include "datablk.h"

/* FIXME: These routines are feeelthy like de pig. Cleaan. */

/* COMPOUND AND AGRIGATE TYPES (LISTS)
   indexes are 0 aligned, element $ is length(list) - 1
   The C functions accept an index of -1 as the last element
*/

/* accepts as many Datablks as you want to be in the new list.. if any
   elements have tails they are merged in as appropriate (same with insert)
   this is all done by value.
 */
Datablk *dblk_mkpacklist(const Datablk *ablk,...)
{
  Datablk *myblk,*i,*j;
  va_list myargs;
  char good = 1;

  va_start(myargs, ablk);

  DBLK_INIT(myblk,DBLK_T_LIST);

  if (ablk)
    {
      myblk->Data = dblk_mkrclone(ablk);
      i = (Datablk *)(myblk->Data);      
      
      while (good)
	{
	  while (i->tail)
	    i = (Datablk *)(i->tail);
       
	  if ((j = va_arg(myargs,Datablk *)))
	    {
	      i->tail = (void *)dblk_mkrclone(j);
	    }
	  else
	    good = 0;
	}
    }

  va_end(myargs);
  return myblk;
}

int dblk_llength(const Datablk *ablk)
{
  Datablk *i;
  int myind = 0;

  if ((ablk) && (ablk->type == DBLK_T_LIST))
    {
      if (ablk->Data)
	{
	  i = (Datablk *)(ablk->Data);
	  while (i)
	    {
	      myind++;
	      i = (Datablk *)(i->tail);
	    }
	}
    }
  else
    myind = -1;
  return myind;
}

/* get an active pointer to a specific index in an array. This can
   be used to either read or write that index data. Don't destroy it
   though!
*/
Datablk *dblk_lindex(const int index, Datablk *ablk)
{
  Datablk *i=0;
  int myind = 0, len;

  if ((len = dblk_llength(ablk)) == -1)
    return 0;

  if (index == -1)
    {
      return dblk_lindex(len-1, ablk);
    }

  if (index < len)
    {  
      i = (Datablk *)(ablk->Data);
      for (myind = 0; myind < index; myind++)
	{
	  i = (Datablk *)(i->tail);
	}
    }
  return i;
}

/* does the same as index, but returns a newly allocated blk of the same value 
   (that you /should/ destroy when you're done with it) 
*/
Datablk *dblk_lindexv(const int index, Datablk *ablk)
{
  Datablk *myblk;

  myblk = dblk_lindex(index, ablk);
  return dblk_mkclone(myblk);
}

/* walk down X, using Y as a temporary variable */
#define DBLK_LWALK(X,Y) Y=X; while (Y->tail) Y = (Datablk *)(Y->tail)

/* insert: displace the blk at index and put elm there (by value)
   the elm at index becomes index+1
   remove: return the value at index         */
int dblk_linsert(const int index, Datablk *list, const Datablk *elm)
{
  Datablk *begblk, *folblk, *elmend, *myelm;
  int len, myind;

  len = dblk_llength(list);

  if ((index < -1) || (index >len))
    return -1;
  
  if (!(myelm = dblk_mkrclone(elm)))
    return 0;

  DBLK_LWALK(myelm,elmend);

  myind = (index == len) ? -1 : index;

  begblk = dblk_lindex(myind,list);

  switch (myind)
    {
    case -1:
      /* last element.. */
      begblk->tail = (void *)myelm;
      break;
    case 0:
      /* first element */
      elmend->tail = (void *)begblk;
      list->Data = (void *)myelm;
      break;
    default:
      folblk = dblk_lindex(myind - 1,list);
      folblk->tail = (void *)myelm;
      elmend->tail = (void *)begblk;
    }
  return 1;
}

Datablk *dblk_lremove(const int index, Datablk *list)
{
  Datablk *begblk, *folblk, *myblk;

  if (myblk = dblk_lindex(index,list))
    folblk = (myblk->tail) ? (Datablk *)(myblk->tail) : 0;
  else
    return 0;

  if ((index > 0) && (begblk = dblk_lindex(index-1,list)))
    begblk->tail = (void *)folblk;      
  else if (index == 0)
    list->Data = (void *)folblk;

  myblk->tail = 0;
  return myblk;
}

/* these actually add and remove elements to the list under the blk. They add
   by value, not reference, but in removing, give you /the/ blk (these are sugar functions
   for insert and remove). */
int dblk_lpush(Datablk *list, const Datablk *elm)
{
  return dblk_linsert(0,list,elm);
}

Datablk *dblk_lpop(Datablk *list)
{
  return dblk_lremove(0,list);
}

int dblk_lunshift(Datablk *list, const Datablk *elm)
{
  return dblk_linsert(-1,list,elm);
}

Datablk *dblk_lshift(Datablk *list)
{
  return dblk_lremove(0,list);
}

