/* $Id: Array,v 1.1.1.1 2008/09/21 21:30:13 kiesling Exp $ -*-c-*-*/

/*
  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
*/

/*
 *    Array class.  
 */

/*
 *  If you change these definitions, make sure they match the
 *  definitions in the file, "ctalklib."
 */

#ifndef MAXMSG
#define MAXMSG 8192
#endif

#ifndef MAXLABEL
#define MAXLABEL 256
#endif

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE (!(FALSE))
#endif

#ifndef NULL
#define NULL ((void *)0)
#endif

Collection class Array;

/*
 *    atPut (int index, OBJECT *object)
 *    Add object to the array at index.  
 *
 *    Each element is a key object with he 
 *    name 0.. self size.  The, "value," instance
 *    variable of each key object is the object
 *    to be added.
 *
 *    Until there is a Symbol class implemented, or 
 *    something similar that will retrieve a complete 
 *    object, the method has to examine the argument 
 *    stack directly, and use library calls to create 
 *    the key object.
 *
 *    It should be sufficient to set the reference count
 *    of the key and value objects to the array object for 
 *    now.
 *
 *    Because we must maintain the name of the value object,
 *    the array element instance variables have only
 *    one instance variable - the value object. So an array's
 *    instance variables look like this.
 *
 *    Array_obj -> instancevars -> next -> next -> next ...
 *                 "value"         "0"     "1"     "2"
 *                                  |       |       |  <- instancevars
 *                                  v       v       v
 *                                 value   value   value
 *                                 object  object  object
 *                                 0 copy  1 copy  2 copy
 *
 *    For the moment, the value objects are copies of the 
 *    arguments. 
 */

Array instanceMethod atPut (int __array_index, OBJECT *__elem_object) {

  char buf[MAXLABEL];
  OBJECT *key_obj, *val_obj, *self_val, *__elem_alias;

  self_val = self;

  /*
   *  The method must use an alias for the __elem_object
   *  argument due to the way that OBJREF expands.
   */
  __elem_alias = ARG(0);
  __ctalkCopyObject (OBJREF(__elem_alias), OBJREF(val_obj));

  sprintf (buf, "%d", __ctalk_to_c_int (__array_index));

  key_obj = 
    __ctalkCreateObject (buf, val_obj -> __o_classname,
			   val_obj -> __o_superclassname,
			   val_obj -> scope);

  /*
   *  The addInstanceVariable function changes the reference count
   *  of val_obj, so we have to save it and restore it.
   *  
   *  The method also must manually add the value object here.
   */

  strcpy (val_obj -> __o_name, "value");

  key_obj -> instancevars = val_obj;

  self addInstanceVariable (buf, key_obj);

  __objRefCntSet (&key_obj, self_val -> nrefs);
  __objRefCntSet (&val_obj, self_val -> nrefs);

  return self;
}

/*
 *    at (int index)
 *    Return the object at index.
 *    
 */

Array instanceMethod at (int __array_index) {

  char buf[MAXLABEL];
  OBJECT *key_object;

  returnObjectClass Any;

  sprintf (buf, "%d", __ctalk_to_c_int (__array_index));
  key_object = __ctalkGetInstanceVariable (self, buf, FALSE);
  if (key_object) {
    return key_object -> instancevars;
  } else {
    return NULL;
  }
}

/*
 *  Here we need to traverse the array list.  The method can't evaluate
 *  each element, in case one of them evaluates to False.
 */
Array instanceMethod size (void) {
  OBJECT *self_val, *t;
  int i;
  self_val = self value;
  for (i = 0, t = self_val -> next; t; t = t -> next, ++i)
    ;
  methodReturnInteger(i)
}

Array instanceMethod = set_value (Array a) {

  Integer new argSize;
  Integer new i;

  argSize = a size;

  for (i = 0; i < argSize; i = i + 1) {
    self atPut i, (a at i);
  }
  
  return __ctalk_self_internal ();
}

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

  OBJECT *array_elem, *array_elem_value, *rcvr_obj, *(*fn)();
  METHOD *self_method, *arg_method;
  Integer new selfArraySize;
  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 Array)");
    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 Array)");
    return NULL;
  }

  selfArraySize = self size;

  for (i = 0; i < selfArraySize; i = i + 1) {
    array_elem = self at i;
    array_elem_value = __ctalkGetInstanceVariable (array_elem, "value", 1);
    __ctalkInlineMethod (array_elem_value, arg_method);
  }

  return NULL;
}
