/* $Id: rtnewobj.c,v 1.9 2009/01/21 00:32:21 kiesling Exp $ -*-c-*-*/

/*
  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 General Public License
  along with this program; if not, write to the Free Software Foundation, 
  Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110-1301 USA.
*/

/* 
 * Run time object constructors, destructors, and basic 
 * garbage collection.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "object.h"
#include "message.h"
#include "ctalk.h"
#include "lex.h"
#include "typeof.h"

OBJECT *__ctalkCreateObjectInit (char *name, char *class, 
				    char *superclass, int scope,
				    char *value) {
  static OBJECT *o, *o_value;
  static char *o_value_ptr, *o_value_value_ptr;
  Boolean is_class_object = False;

  if (!strcmp (class, "Class"))
    is_class_object = True;

  o = (OBJECT *)__xalloc (sizeof (struct _object));
  o_value = (OBJECT *)__xalloc (sizeof (struct _object));
  o_value_ptr = (char *)__xalloc (strlen (NULLSTR) + 1);
  o_value_value_ptr = (char *)__xalloc (strlen (NULLSTR) + 1);

  o -> __o_value = o_value_ptr;
  o_value -> __o_value = o_value_value_ptr;
  o -> instancevars = o_value;
  o_value -> __o_p_obj = o;

  strcpy (o -> sig, "OBJECT");
  strcpy (o -> __o_name, name);
  strcpy (o -> __o_classname, class);
  o -> __o_class = __ctalkClassObject (o);
  if (superclass) {
    strcpy (o -> __o_superclassname, superclass);
    o -> __o_superclass = __ctalk_get_object (superclass, "Class");
  }
  o -> scope = scope;
  strcpy (o_value -> sig, "OBJECT");
  strcpy (o_value -> __o_name, "value");
  strcpy (o_value -> __o_classname, 
	  ((is_class_object) ? name: class));
  o_value -> __o_class = __ctalkClassObject (o);
  if (superclass) {
    strcpy (o_value -> __o_superclassname, superclass);
    o_value -> __o_superclass = __ctalk_get_object (superclass, "Class");
  }
  o_value -> scope = scope;
  strcpy (o_value_ptr, NULLSTR);
  strcpy (o_value_value_ptr, NULLSTR);

  if (value)
    __ctalkSetObjectValueVar (o, value);

  return o;
}

OBJECT *__ctalkCreateObject (char *name, char *class, char *superclass,
			       int scope) {

  static OBJECT *o;
  static char *o_value_ptr;

  o = (OBJECT *)__xalloc (sizeof (struct _object));
  o_value_ptr = (char *)__xalloc (strlen (NULLSTR) + 1);

  o -> __o_value = o_value_ptr;

  strcpy (o -> sig, "OBJECT");
  strcpy (o -> __o_name, name);
  strcpy (o -> __o_classname, class);
  o -> __o_class = __ctalkClassObject (o);
  if (superclass) {
    strcpy (o -> __o_superclassname, superclass);
    o -> __o_superclass = __ctalk_get_object (superclass, "Class");
  }
  o -> scope = scope;
  strcpy (o -> __o_value, NULLSTR);

  return o;
  
}

/* From ctalklib - 
   The name of the new object is on the argument stack.
   The new object's class is the class of the receiver
   object. 
*/
OBJECT *__ctalk_new_object (OBJECT **args) {

  OBJECT *rcvr;

  rcvr = __ctalk_receiver_pop ();
  __ctalk_receiver_push (rcvr);

  strcpy (args[0] -> __o_classname, rcvr -> __o_name);
  strcpy (args[0] -> __o_superclassname, rcvr -> __o_superclassname);
  __ctalkInstanceVarsFromClassObject (args[0]);
  __objRefCntSet (OBJREF(args[0]), 0);
  __ctalk_dictionary_add (args[0]);
  
  return args[0];
}

/*
 *   Set an object's value, making sure that the value member's
 *   buffer is large enough for the value.
 */

int __ctalkSetObjectValue (OBJECT *__o, char *__value) {

  if (strlen (__value) >= strlen (__o -> __o_value)) {
    free (__o -> __o_value);
    if ((__o -> __o_value = (char *)calloc (strlen (__value) + 1, sizeof (char)))
	== NULL) {
      _error ("__ctalkSetObjectValue (): %s.\n", strerror (errno));
      return ERROR;
    }
  }
  strcpy (__o -> __o_value, __value);

  return SUCCESS;
}

/*
 *  This should replace the function above.
 */

static char *new_value_ptr;
static char *new_value_value_ptr;

int __ctalkSetObjectValueVar (OBJECT *__o, char *__value) {

  OBJECT *__o_value_obj;

  if (strlen (__value) > strlen (__o -> __o_value)) {
    new_value_ptr = __o->__o_value;
    new_value_ptr = realloc (new_value_ptr, strlen (__value) + 1);
    memset (new_value_ptr, '\0', strlen (__value) + 1);
    __o -> __o_value = new_value_ptr;
  }
  strcpy (__o -> __o_value, __value);

  if ((__o_value_obj = __ctalkGetInstanceVariable (__o, "value", FALSE)) != NULL) {
     if (strlen (__value) >= strlen (__o_value_obj -> __o_value)) {
       new_value_value_ptr = __o_value_obj->__o_value;
       new_value_value_ptr = realloc (new_value_value_ptr, 
				      strlen (__value) + 1);
       memset (new_value_value_ptr, '\0', strlen (__value) + 1);
       __o_value_obj -> __o_value = new_value_value_ptr;
     }
     strcpy (__o_value_obj -> __o_value, __value);
  }

  return SUCCESS;
}

/*
 *  Add an empty buffer to the object's value variable.
 */
int __ctalkSetObjectValueBuf (OBJECT *__o, char *__buf) {

  OBJECT *__o_value_obj;

  if ((__o_value_obj = __ctalkGetInstanceVariable (__o, "value", FALSE)) 
      != NULL) {
    free (__o_value_obj -> __o_value);
    __o_value_obj -> __o_value = __buf;
  } else {
    _warning 
      ("__ctalkSetObjectValueBuf: Instance variable, \"value,\" not found.\n");
    return ERROR;
  }

  return SUCCESS;
}

/*
 *  This function is only used by __ctalkDefineInstanceVariable and
 *  __ctalkDefineClassVariable, so there should be no need to print
 *  a warning, only return an ERROR.  Some classes don't have value
 *  variables, which the functions don't check for.
 */
int __ctalkSetObjectValueClass (OBJECT *__o, OBJECT *value_class_obj) {
  OBJECT *value_var;
  if ((value_var = __ctalkGetInstanceVariable (__o, "value", FALSE)) == NULL)
    return ERROR;
  strcpy (value_var -> __o_classname, value_class_obj -> __o_name);
  strcpy (value_var -> __o_superclassname, value_class_obj -> __o_superclassname);
  value_var -> __o_class = value_class_obj;
  value_var -> __o_superclass = value_class_obj -> __o_superclass;
  return SUCCESS;
}

int __ctalkSetObjectName (OBJECT *__o, char *__name) {

  if (strlen(__name) > MAXLABEL)
    __name[MAXLABEL] = 0;
  strcpy (__o -> __o_name, __name);
  return SUCCESS;
}

/*
 *  Delete an object after making sure that it is not in
 *  a dictionary or class library.  
 *
 *  NOTE - This is iffy, in case we want to delete a class 
 *  or a global object, in which case the application should
 *  call __ctalkDeleteObject, so it isn't checked here.
 *
 *  The function __ctalk_arg_cleanup checks that an object
 *  is not global before trying to delete it.
 *
 *  If the object is an instance variable, make sure that its
 *  reference count is the same as the dictionary object's 
 *  reference count.
 */

extern int __cleanup_deletion;

void __ctalkDeleteObjectInternal (OBJECT *__o) {
  OBJECT *obj;

  if (!__o || !IS_OBJECT (__o)) {
#ifdef DEBUG_OBJECT_DESTRUCTION
    _warning ("__ctalkDeleteObjectInternal: Not an object.\n");
#endif
    return;
  }
  if (__o -> nrefs <= 0) {
    if (__o -> instancevars) {
      OBJECT *o_ptr, *o_ptr_prev;
      for (o_ptr = __o -> instancevars; 
	   o_ptr && IS_OBJECT(o_ptr) && o_ptr -> next; 
	   o_ptr = o_ptr -> next) 
	;
      if (o_ptr == __o -> instancevars) {
	if (o_ptr) __ctalkDeleteObject (o_ptr);
	__o -> instancevars = NULL;
      } else {
	while (o_ptr && (o_ptr != __o -> instancevars)) {
	  o_ptr_prev = (o_ptr) ? o_ptr -> prev : NULL;
	  __ctalkDeleteObject (o_ptr);
	  o_ptr = o_ptr_prev;
	  if (o_ptr) o_ptr -> next = NULL;
	}
	__ctalkDeleteObject(__o -> instancevars);
	__o -> instancevars = NULL;
      }
    }
    if (!__cleanup_deletion) {
      if ((obj = 
	   __ctalk_get_object (__o -> __o_name, __o -> __o_classname)) != NULL) {
	/*
	 *  TODO - The call above depends too much on 
	 *  __ctalk_get_object's search for objects, and
	 *  the searches should be factored out.
	 */
	if (__o == obj)
	  __ctalk_remove_object (obj);
      }
    }
    if (__o && IS_OBJECT (__o)) {
     if (__o -> __o_value)  {
       if (obj_ref_str (__o -> __o_value)) {
	 OBJECT *__r;
	 sscanf (__o -> __o_value, "0x%p", &__r);
	 if (__r && IS_OBJECT (__r)) {
	 __ctalkSetObjectScope (__r, __r -> scope & !VAR_REF_OBJECT);
	   if (!__cleanup_deletion) {
	     __objRefCntDec(OBJREF(__r));
	     _cleanup_temporary_objects (__r, NULL, NULL);
	   } else {
	     __ctalkDeleteObject (__r);
	     __o -> __o_value[0] = '\0';
	   }
	 }
       }
       __xfree (__o -> __o_value);
       __o ->__o_value = NULL;
     }
     __o->sig[0] = '\0';
     __xfree (__o);
     __o = NULL;
    }
  } else {
    __objRefCntDec (OBJREF (__o));
  }
}

/*
 *   Set an object's reference count to 0.  Also set the reference
 *   count of the object's instance variables to 0, unless they 
 *   are in the dictionary or the class dictionary.
 */

void __ctalkDeleteObject (OBJECT *__o) {
  OBJECT *var;
  __objRefCntSet (OBJREF(__o), 0);
  for (var = __o -> instancevars; var && var->next && IS_OBJECT(var); var = var -> next)
    __objRefCntSet(OBJREF(var), 0);
  __ctalkDeleteObjectInternal (__o);
}

OBJECT *_dup_object (OBJECT *o) {

  static OBJECT *new_object, *var_object, *s, *t;

  new_object = __ctalkCreateObject (o -> __o_name, o -> __o_classname, 
				   o -> __o_superclassname, o -> scope);
  __ctalkSetObjectValue (new_object, o -> __o_value);

  if (o -> instancevars) {
    for (t = o -> instancevars; t; t = t -> next) {
      var_object = _dup_object (t);
      if (!new_object -> instancevars) {
	new_object -> instancevars = var_object;
      } else {
	for (s = new_object -> instancevars; s -> next; s = s -> next)
	  ;
	s -> next = var_object;
	var_object -> prev = s;
      }
    }
  }
    
  return new_object;
}

