/* $Id: List,v 1.16 2009/02/27 21:56:14 kiesling Exp $ -*-c-mode-*-*/

/*
  This file is part of Ctalk.
  Copyright  2005-2009  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.
 */

#define __LIST_HEAD(__o) ((__o)->instancevars->next)

require String;

Collection class List;

List instanceMethod new (char *__newListName) {

  List super new __newListName;

#ifdef __DJGPP__
  methodReturnObjectName(__newListName)
#else
  methodReturnObject(__newListName)
#endif
}

List instanceMethod value (void) {
  OBJECT *self_object;
  self_object = self;
  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 = self;
  if (!__LIST_HEAD(self_object)) {
    __LIST_HEAD(self_object) = elem_object;
  } else {
    for (t = __LIST_HEAD(self_object); t && t -> next; t = t -> next)
      ;
    t -> next = elem_object;
    elem_object -> prev = t;
  }
  methodReturnSelf
}

List instanceMethod pushItemRef (OBJECT *(*item)()) {
  Key new keyObject;
  OBJECT *key_alias_value_ptr, *item_alias;
  OBJECT *self_object, *t;
  char buf[MAXMSG];

  key_alias_value_ptr = keyObject;
  item_alias = ARG(0);
  /* FMT_0XHEX */
  sprintf (buf, "0x%p", item_alias);
  __ctalkSetObjectValue (key_alias_value_ptr, buf);
  __objRefCntInc(OBJREF(item_alias));
  self_object = self;
  if (!__LIST_HEAD(self_object)) {
    __LIST_HEAD(self_object) = key_alias_value_ptr -> __o_p_obj;
  } else {
    for (t = __LIST_HEAD(self_object); t && t -> next; t = t -> next)
      ;
    t -> next = key_alias_value_ptr -> __o_p_obj;
    key_alias_value_ptr -> __o_p_obj -> prev = t;
  }
  methodReturnSelf
}

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

  if (!__LIST_HEAD(self_object)) {
    methodReturnNULL
  } else {
    for (t = __LIST_HEAD(self_object); 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. */
     __LIST_HEAD(self_object) = 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 popItemRef (void) {
  OBJECT *self_object, *t, *symbol_object, *item_alias;
  self_object = self;

  if (!__LIST_HEAD(self_object)) {
    methodReturnNULL
  } else {
    for (t = __LIST_HEAD(self_object); 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. */
     __LIST_HEAD(self_object) = NULL;
  }
  sscanf (symbol_object -> instancevars -> __o_value, "0x%p", &item_alias);
  __objRefCntDec(OBJREF(item_alias));
  symbol_object -> instancevars -> __o_value[0] = '\0';
  __ctalkDeleteObject(symbol_object);
  return item_alias;
}

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 = self;

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

List instanceMethod unshift (void) {
  OBJECT *self_object, *symbol_object, *item_object;
  self_object = self;
  if (!__LIST_HEAD(self_object)) {
    methodReturnNULL
  } else {
    symbol_object = __LIST_HEAD(self_object);
    if (symbol_object) symbol_object -> prev = NULL;
    __LIST_HEAD(self_object) = symbol_object -> next;
  }
  /*
   *  TODO - Generate an exception (and simple walkback)
   *  if the element is not a KeyObject.
   */
  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;
}

#define FMT_0XHEX(__p) "0x%p",(__p)
#define IS_OBJECT(x) (!memcmp ((void *)x, "OBJECT", 6))

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

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

  fn = (OBJECT *(*)())__ctalkRtGetMethodFn ();
  rcvr_obj = rcvr_obj_p = __ctalk_self_internal ();

  if (((self_method = __ctalkRtGetMethod ()) == 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_p, 
			      self_method->args[0] -> __o_name, 0))
      == NULL) &&
      ((arg_method = __ctalkFindClassMethodByName (&rcvr_obj_p, 
				 self_method->args[0]->__o_name, 0))
       == NULL)) {
    __ctalkCriticalExceptionInternal (NULL, undefined_method_x, 
				      "from map (Class List)");
    return NULL;
  }

  for (t = __LIST_HEAD(rcvr_obj); t; t = t -> next) {
    sscanf (t -> instancevars -> __o_value, FMT_0XHEX(&list_elem));
    if (!list_elem || !IS_OBJECT(list_elem)) {
      __ctalkCriticalExceptionInternal (NULL, undefined_receiver_x, 
					"from map (Class List)");
      methodReturnNULL;
    } else {
      __ctalkInlineMethod (list_elem, arg_method);
    }
  }
  methodReturnNULL;
}

List instanceMethod isEmpty (void) {

  OBJECT *rcvr_obj;

  returnObjectClass Integer;

  rcvr_obj = __ctalkRtReceiverObject ();
  if (__LIST_HEAD(rcvr_obj) == NULL) {
    methodReturnTrue
  } else {
    methodReturnFalse
  }
}
