/* $Id: List,v 1.1.1.1 2008/11/28 17:57:26 kiesling Exp $ -*-c-mode-*-*/

/*
  This file is part of ctalk.
  Copyright  2005-2008  Robert Kiesling, ctalk@ctalklang.org.
  Permission is granted to copy this software provided that this copyright
  notice is included in all source code modules.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*/

/*
 *   List Class
 *
 *   Lists have no "value" instance variable, which makes adding and 
 *   removing from the instance variable list much simpler.  The class 
 *   uses its own value method, which returns the list itself.
 */

require String;

Collection class List;

List instanceMethod new (char *__newListName) {

  List super new __newListName;
  OBJECT *l_obj;

  l_obj = __ctalk_get_object(__ctalk_to_c_char_ptr(__newListName), "List");
  __ctalkDeleteObject (l_obj -> instancevars);
  l_obj -> instancevars = NULL;
#ifdef __DJGPP__
  methodReturnObjectName(__newListName)
#else
  methodReturnObject(__newListName)
#endif
}

List instanceMethod value (void) {
  OBJECT *self_object;
  self_object = __ctalk_self_internal ();
  return self_object -> instancevars;
}

List instanceMethod push (OBJECT *(*item)()) {
  Key new keyObject;

  /* 
   *  C alias for the List object key.
   */
  OBJECT *key_alias_ptr;
  OBJECT *self_object, *t, *elem_object;

  keyObject setValue item;
  key_alias_ptr = keyObject getKeyObject;
  __ctalkCopyObject (OBJREF(key_alias_ptr), OBJREF(elem_object));
  self_object = __ctalk_self_internal ();
  if (!self_object -> instancevars) {
    self_object -> instancevars = elem_object;
  } else {
    for (t = self_object -> instancevars; t && t -> next; t = t -> next)
      ;
    t -> next = elem_object;
    elem_object -> prev = t;
  }
  methodReturnSelf
}

List instanceMethod pop (void) {
  OBJECT *self_object, *t, *symbol_object, *item_object;
  self_object = __ctalk_self_internal ();

  if (!self_object -> instancevars) {
    methodReturnNULL
  } else {
    for (t = self_object -> instancevars; t && t -> next; t = t -> next)
      ;
    symbol_object = t;
    if (t -> prev) 
      t -> prev -> next = NULL;
    else  /* Removed first item, so list is now empty. */
      self_object -> instancevars = NULL;
  }
  sscanf (symbol_object -> instancevars -> __o_value, "0x%p", &item_object);
  /*
   *  Unref item_object so its reference count isn't decremented
   *  when symbol_object is deleted.
   */
  symbol_object -> instancevars -> __o_value[0] = '\0';
  __ctalkDeleteObject(symbol_object);
  return item_object;
}

List instanceMethod shift (OBJECT *(*item)()) {
  Key new keyObject;

  /* 
   *  C alias for the List object key.
   */
  OBJECT *key_alias_ptr;
  OBJECT *self_object, *elem_object;

  keyObject setValue item;
  key_alias_ptr = keyObject getKeyObject;
  __ctalkCopyObject (OBJREF(key_alias_ptr), OBJREF(elem_object));
  self_object = __ctalk_self_internal ();

  if (!self_object -> instancevars) {
    self_object -> instancevars = elem_object;
  } else {
    elem_object -> next = self_object -> instancevars;
    self_object -> instancevars -> prev = elem_object;
    self_object -> instancevars = elem_object;
  }
  methodReturnSelf
}

List instanceMethod unshift (void) {
  OBJECT *self_object, *t, *symbol_object, *item_object;
  self_object = __ctalk_self_internal ();

  if (!self_object -> instancevars) {
    methodReturnNULL
  } else {
    symbol_object = self_object -> instancevars;
    t = self_object -> instancevars -> next;
    if (t) t -> prev = NULL;
    self_object -> instancevars = t;  /* Will be NULL after last item
                                         is unshifted. */
  }
  sscanf (symbol_object -> instancevars -> __o_value, "0x%p", &item_object);
  /*
   *  Unref item_object so its reference count isn't decremented
   *  when symbol_object is deleted.  Then decrement item_object's
   *  reference count separately.
   */
  symbol_object -> instancevars -> __o_value[0] = '\0';
  __ctalkDeleteObject(symbol_object);
  __objRefCntDec(OBJREF(item_object));
  return item_object;
}

List instanceMethod map (OBJECT *(*methodfn)()) {

  OBJECT *list_elem, *rcvr_obj, *(*fn)(), *t;
  METHOD *self_method, *arg_method;
  Integer new i;

  fn = (OBJECT *(*)())__ctalkRtGetMethodFn ();
  rcvr_obj = __ctalkRtReceiverObject ();

  if (((self_method = __ctalkFindInstanceMethodByFn (&rcvr_obj, fn, 0))
      == NULL) &&
      ((self_method = __ctalkFindClassMethodByFn (&rcvr_obj, fn, 0))
       == NULL)) {
    __ctalkCriticalExceptionInternal (NULL, undefined_method_x, 
				      "from map (Class List)");
    return NULL;
  }

  if (((arg_method = __ctalkFindInstanceMethodByName (&rcvr_obj, 
			      self_method->args[0] -> __o_name, 0))
      == NULL) &&
      ((arg_method = __ctalkFindClassMethodByName (&rcvr_obj, 
				 self_method->args[0]->__o_name, 0))
       == NULL)) {
    __ctalkCriticalExceptionInternal (NULL, undefined_method_x, 
				      "from map (Class List)");
    return NULL;
  }


  for (t = rcvr_obj -> instancevars; t; t = t -> next) {
    sscanf (t -> instancevars -> __o_value, "%p", &list_elem);
    __ctalkInlineMethod (list_elem, arg_method);
  }

  methodReturnNULL
}

List instanceMethod isEmpty (void) {

  OBJECT *rcvr_obj;

  returnObjectClass Integer;

  rcvr_obj = __ctalkRtReceiverObject ();

  if (rcvr_obj -> instancevars == NULL) {
    methodReturnTrue
  } else {
    methodReturnFalse
  }
}
