/*
	pr_obj.c

	Progs Obj runtime support

	Copyright (C) 2001 Bill Currie

	Author: Bill Currie <bill@taniwha.org>
	Date: 2002/7/21

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

	This program 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 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:

		Free Software Foundation, Inc.
		59 Temple Place - Suite 330
		Boston, MA  02111-1307, USA

*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

static __attribute__ ((unused)) const char rcsid[] =
	"$Id$";

#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif

#include "QF/cvar.h"
#include "QF/dstring.h"
#include "QF/hash.h"
#include "QF/pr_obj.h"
#include "QF/progs.h"
#include "QF/sys.h"

#include "compat.h"

static void
call_function (progs_t *pr, func_t func)
{
	dfunction_t *newf;

	if (!func)
		PR_RunError (pr, "NULL function");
	newf = pr->pr_functions + func;
	if (newf->first_statement < 0) {
		// negative statements are built in functions
		int         i = -newf->first_statement;

		if (i >= pr->numbuiltins || !pr->builtins[i]
			|| !pr->builtins[i]->proc)
			PR_RunError (pr, "Bad builtin call number");
		pr->builtins[i]->proc (pr);
	} else {
		PR_EnterFunction (pr, newf);
	}
}

static const char *
selector_get_key (void *s, void *pr)
{
	return PR_GetString ((progs_t *)pr, ((pr_sel_t *)s)->sel_id);
}

static const char *
class_get_key (void *c, void *pr)
{
	return PR_GetString ((progs_t *)pr, ((pr_class_t *)c)->name);
}

static unsigned long
category_get_hash (void *_c, void *_pr)
{
	progs_t    *pr = (progs_t *) _pr;
	pr_category_t *cat = (pr_category_t *) _c;
	const char *category_name = PR_GetString (pr, cat->category_name);
	const char *class_name = PR_GetString (pr, cat->class_name);

	return Hash_String (category_name) ^ Hash_String (class_name);
}

static int
category_compare (void *_c1, void *_c2, void *_pr)
{
	progs_t    *pr = (progs_t *) _pr;
	pr_category_t *c1 = (pr_category_t *) _c1;
	pr_category_t *c2 = (pr_category_t *) _c2;
	const char *cat1 = PR_GetString (pr, c1->category_name);
	const char *cat2 = PR_GetString (pr, c2->category_name);
	const char *cls1 = PR_GetString (pr, c1->class_name);
	const char *cls2 = PR_GetString (pr, c2->class_name);
	return strcmp (cat1, cat2) == 0 && strcmp (cls1, cls2) == 0;
}


static void
dump_ivars (progs_t *pr, pointer_t _ivars)
{
	pr_ivar_list_t *ivars;
	int         i;

	if (!_ivars)
		return;
	ivars = &G_STRUCT (pr, pr_ivar_list_t, _ivars);
	for (i = 0; i < ivars->ivar_count; i++) {
		Sys_Printf ("        %s %s %d\n",
					PR_GetString (pr, ivars->ivar_list[i].ivar_name),
					PR_GetString (pr, ivars->ivar_list[i].ivar_type),
					ivars->ivar_list[i].ivar_offset);
	}
}

static int
object_is_instance (progs_t *pr, pr_id_t *object)
{
	pr_class_t *class;

	if (object) {
		class = &G_STRUCT (pr, pr_class_t, object->class_pointer);
		return PR_CLS_ISCLASS (class);
	}
	return 0;
}

static string_t
object_get_class_name (progs_t *pr, pr_id_t *object)
{
	pr_class_t *class;

	if (object) {
		class = &G_STRUCT (pr, pr_class_t, object->class_pointer);
		if (PR_CLS_ISCLASS (class)) {
			R_INT (pr) = class->name;
			return class->name;
		}
		if (PR_CLS_ISMETA (class)) {
			R_INT (pr) = ((pr_class_t *)object)->name;
			return ((pr_class_t *)object)->name;
		}
	}
	return PR_SetString (pr, "Nil");
}

//====================================================================

static void
finish_class (progs_t *pr, pr_class_t *class, pointer_t object_ptr)
{
	pr_class_t *meta = &G_STRUCT (pr, pr_class_t, class->class_pointer);
	pr_class_t *val;

	meta->class_pointer = object_ptr;
	if (class->super_class) {
		const char *super_class = PR_GetString (pr, class->super_class);
		const char *class_name = PR_GetString (pr, class->name);
		val = Hash_Find (pr->classes, super_class);
		if (!val)
			PR_Error (pr, "broken class %s: super class %s not found",
					  class_name, super_class);
		meta->super_class = val->class_pointer;
		class->super_class = POINTER_TO_PROG (pr, val);
	} else {
		pointer_t  *ml = &meta->methods;
		while (*ml)
			ml = &G_STRUCT (pr, pr_method_list_t, *ml).method_next;
		*ml = class->methods;
	}
	Sys_DPrintf ("    %d %d %d\n", meta->class_pointer, meta->super_class,
				 class->super_class);
}

static void
finish_category (progs_t *pr, pr_category_t *category)
{
	const char *class_name = PR_GetString (pr, category->class_name);
	const char *category_name = PR_GetString (pr, category->category_name);
	pr_class_t *class = Hash_Find (pr->classes, class_name);
	pr_method_list_t *method_list;

	if (!class)
		PR_Error (pr, "broken category %s (%s): class %s not found",
				  class_name, category_name, class_name);
	if (category->instance_methods) {
		method_list = &G_STRUCT (pr, pr_method_list_t,
								 category->instance_methods);
		method_list->method_next = class->methods;
		class->methods = category->instance_methods;
	}
	if (category->class_methods) {
		pr_class_t *meta = &G_STRUCT (pr, pr_class_t, class->class_pointer);
		method_list = &G_STRUCT (pr, pr_method_list_t,
								 category->class_methods);
		method_list->method_next = meta->methods;
		meta->methods = category->class_methods;
	}
}

static void
pr___obj_exec_class (progs_t *pr)
{
	pr_module_t *module = &P_STRUCT (pr, pr_module_t, 0);
	pr_symtab_t *symtab;
	pr_sel_t   *sel;
	pointer_t  *ptr;
	int         i;
	//int         d = developer->int_val;

	if (!module)
		return;
	//developer->int_val = 1;
	symtab = &G_STRUCT (pr, pr_symtab_t, module->symtab);
	if (!symtab)
		return;
	Sys_DPrintf ("Initializing %s module\n"
				 "symtab @ %d : %d selector%s, %d class%s and %d categor%s\n",
				 PR_GetString (pr, module->name), module->symtab,
				 symtab->sel_ref_cnt, symtab->sel_ref_cnt == 1 ? "" : "s",
				 symtab->cls_def_cnt, symtab->cls_def_cnt == 1 ? "" : "es",
				 symtab->cat_def_cnt, symtab->cat_def_cnt == 1 ? "y" : "ies");
	sel = &G_STRUCT (pr, pr_sel_t, symtab->refs);
	for (i = 0; i < symtab->sel_ref_cnt; i++) {
		Sys_DPrintf ("    %s\n", PR_GetString (pr, sel->sel_id));
		Hash_Add (pr->selectors, sel);
		sel++;
	}
	ptr = symtab->defs;
	for (i = 0; i < symtab->cls_def_cnt; i++) {
		pr_class_t *class = &G_STRUCT (pr, pr_class_t, *ptr);
		pr_class_t *meta = &G_STRUCT (pr, pr_class_t, class->class_pointer);
		Sys_DPrintf ("Class %s @ %d\n", PR_GetString (pr, class->name), *ptr);
		Sys_DPrintf ("    class pointer: %d\n", class->class_pointer);
		Sys_DPrintf ("    super class: %s\n",
					 PR_GetString (pr, class->super_class));
		Sys_DPrintf ("    instance variables: %d @ %d\n", class->instance_size,
					 class->ivars);
		if (developer->int_val)
			dump_ivars (pr, class->ivars);
		Sys_DPrintf ("    instance methods: %d\n", class->methods);
		Sys_DPrintf ("    protocols: %d\n", class->protocols);

		Sys_DPrintf ("    class methods: %d\n", meta->methods);
		Sys_DPrintf ("    instance variables: %d @ %d\n", meta->instance_size,
					 meta->ivars);
		if (developer->int_val)
			dump_ivars (pr, meta->ivars);

		Hash_Add (pr->classes, class);
		ptr++;
	}
	for (i = 0; i < symtab->cat_def_cnt; i++) {
		pr_category_t *category = &G_STRUCT (pr, pr_category_t, *ptr);
		Sys_DPrintf ("Category %s (%s) @ %d\n",
					 PR_GetString (pr, category->class_name),
					 PR_GetString (pr, category->category_name), *ptr);
		Sys_DPrintf ("    instance methods: %d\n", category->instance_methods);
		Sys_DPrintf ("    class methods: %d\n", category->class_methods);
		Sys_DPrintf ("    protocols: %d\n", category->protocols);
		Hash_AddElement (pr->categories, category);
		ptr++;
	}
	//developer->int_val = d;
}

//====================================================================

static pr_method_t *
obj_find_message (progs_t *pr, pr_class_t *class, pr_sel_t *selector)
{
	pr_class_t *c = class;
	pr_method_list_t *method_list;
	pr_method_t *method;
	int         i;

	while (c) {
		method_list = &G_STRUCT (pr, pr_method_list_t, c->methods);
		while (method_list) {
			for (i = 0, method = method_list->method_list;
				 i < method_list->method_count; i++, method++) {
				if (method->method_name.sel_id == selector->sel_id)
					return method;
			}
			method_list = &G_STRUCT (pr, pr_method_list_t,
									 method_list->method_next);
		}
		c = c->super_class ? &G_STRUCT (pr, pr_class_t, c->super_class) : 0;
	}
	return 0;
}

static pr_method_t *
obj_msg_lookup (progs_t *pr, pr_id_t *receiver, pr_sel_t *op)
{
	pr_class_t *class;
	if (!receiver)
		return 0;
	class = &G_STRUCT (pr, pr_class_t, receiver->class_pointer);
	return obj_find_message (pr, class, op);
}

static pr_method_t *
obj_msg_lookup_super (progs_t *pr, pr_super_t *super, pr_sel_t *op)
{
	pr_class_t *class;

	if (!super->self)
		return 0;

	class = &G_STRUCT (pr, pr_class_t, super->class);
	return obj_find_message (pr, class, op);
}

static void
obj_verror (progs_t *pr, pr_id_t *object, int code, const char *fmt, int count,
			pr_type_t *args)
{
	pr_type_t **arglist = (pr_type_t **) alloca (count * sizeof (pr_type_t *));
	dstring_t  *dstr = dstring_newstr ();
	int         i;

	for (i = 0; i < count; i++)
		arglist[i] = args + i * 3;
	PR_Sprintf (pr, dstr, "obj_verror", fmt, count, arglist);
	PR_RunError (pr, "%s", dstr->str);
}

static void
pr_obj_error (progs_t *pr)
{
	pr_id_t    *object = &P_STRUCT (pr, pr_id_t, 0);
	int         code = P_INT (pr, 1);
	const char *fmt = P_GSTRING (pr, 2);
	int         count = pr->pr_argc - 3;
	pr_type_t  *args = pr->pr_params[3];
	
	obj_verror (pr, object, code, fmt, count, args);
}

static void
pr_obj_verror (progs_t *pr)
{
	pr_id_t    *object = &P_STRUCT (pr, pr_id_t, 0);
	int         code = P_INT (pr, 1);
	const char *fmt = P_GSTRING (pr, 2);
	pr_va_list_t *val = (pr_va_list_t *) pr->pr_params[3];

	obj_verror (pr, object, code, fmt, val->count,
				&G_STRUCT (pr, pr_type_t, val->list));
}

static void
pr_obj_set_error_handler (progs_t *pr)
{
	//func_t      func = P_INT (pr, 0);
	//arglist
	//XXX
	PR_RunError (pr, "%s, not implemented", __FUNCTION__);
}

static void
pr_obj_msg_lookup (progs_t *pr)
{
	pr_id_t    *receiver = &P_STRUCT (pr, pr_id_t, 0);
	pr_sel_t   *op = &P_STRUCT (pr, pr_sel_t, 1);
	pr_method_t *method = obj_msg_lookup (pr, receiver, op);
	R_INT (pr) = method ? method->method_imp : 0;
}

static void
pr_obj_msg_lookup_super (progs_t *pr)
{
	pr_super_t *super = &P_STRUCT (pr, pr_super_t, 0);
	pr_sel_t   *_cmd = &P_STRUCT (pr, pr_sel_t, 1);
	pr_method_t *method = obj_msg_lookup_super (pr, super, _cmd);
	R_INT (pr) = method ? method->method_imp : 0;
}

static void
pr_obj_msg_sendv (progs_t *pr)
{
	pr_id_t    *receiver = &P_STRUCT (pr, pr_id_t, 0);
	pr_sel_t   *op = &P_STRUCT (pr, pr_sel_t, 1);
	pr_va_list_t args = P_STRUCT (pr, pr_va_list_t, 2);
	pr_method_t *method = obj_msg_lookup (pr, receiver, op);

	if (!method)
		PR_RunError (pr, "%s does not respond to %s",
					 PR_GetString (pr, object_get_class_name (pr, receiver)),
					 PR_GetString (pr, op->sel_id));
	if (args.count > 6)
		args.count = 6;
	memcpy (pr->pr_params[2], G_GPOINTER (pr, args.list),
			args.count * 4 * pr->pr_param_size);
	call_function (pr, method->method_imp);
}

static void
pr_obj_malloc (progs_t *pr)
{
	int         size = P_INT (pr, 0) * sizeof (pr_type_t);
	void       *mem = PR_Zone_Malloc (pr, size);

	R_INT (pr) = POINTER_TO_PROG (pr, mem);
}

static void
pr_obj_atomic_malloc (progs_t *pr)
{
	int         size = P_INT (pr, 0) * sizeof (pr_type_t);
	void       *mem = PR_Zone_Malloc (pr, size);

	R_INT (pr) = POINTER_TO_PROG (pr, mem);
}

static void
pr_obj_valloc (progs_t *pr)
{
	int         size = P_INT (pr, 0) * sizeof (pr_type_t);
	void       *mem = PR_Zone_Malloc (pr, size);

	R_INT (pr) = POINTER_TO_PROG (pr, mem);
}

static void
pr_obj_realloc (progs_t *pr)
{
	void       *mem = (void*)P_GPOINTER (pr, 0);
	int         size = P_INT (pr, 1) * sizeof (pr_type_t);

	mem = PR_Zone_Realloc (pr, mem, size);
	R_INT (pr) = POINTER_TO_PROG (pr, mem);
}

static void
pr_obj_calloc (progs_t *pr)
{
	int         size = P_INT (pr, 0) * sizeof (pr_type_t);
	void       *mem = PR_Zone_Malloc (pr, size);

	memset (mem, 0, size);
	R_INT (pr) = POINTER_TO_PROG (pr, mem);
}

static void
pr_obj_free (progs_t *pr)
{
	void       *mem = (void*)P_GPOINTER (pr, 0);

	PR_Zone_Free (pr, mem);
}

static void
pr_obj_get_uninstalled_dtable (progs_t *pr)
{
	//XXX
	PR_RunError (pr, "%s, not implemented", __FUNCTION__);
}

static void
pr_obj_msgSend (progs_t *pr)
{
	pr_id_t    *self = &P_STRUCT (pr, pr_id_t, 0);
	pr_sel_t   *_cmd = &P_STRUCT (pr, pr_sel_t, 1);
	pr_method_t *method;

	if (!self) {
		R_INT (pr) = R_INT (pr);
		return;
	}
	if (!_cmd)
		PR_RunError (pr, "null selector");
	method = obj_msg_lookup (pr, self, _cmd);
	if (!method)
		PR_RunError (pr, "%s does not respond to %s",
					 PR_GetString (pr, object_get_class_name (pr, self)),
					 PR_GetString (pr, _cmd->sel_id));

	call_function (pr, method->method_imp);
}

static void
pr_obj_msgSend_super (progs_t *pr)
{
	pr_super_t *super = &P_STRUCT (pr, pr_super_t, 0);
	pr_sel_t   *_cmd = &P_STRUCT (pr, pr_sel_t, 1);
	pr_method_t *method;

	method = obj_msg_lookup_super (pr, super, _cmd);
	if (!method) {
		pr_id_t    *self = &G_STRUCT (pr, pr_id_t, super->self);
		PR_RunError (pr, "%s does not respond to %s",
					 PR_GetString (pr, object_get_class_name (pr, self)),
					 PR_GetString (pr, _cmd->sel_id));
	}
	P_POINTER (pr, 0) = super->self;
	call_function (pr, method->method_imp);
}

static void
pr_obj_get_class (progs_t *pr)
{
	const char *name = P_GSTRING (pr, 0);
	pr_class_t *class;

	class = Hash_Find (pr->classes, name);
	if (!class)
		PR_RunError (pr, "could not find class %s", name);
	R_INT (pr) = POINTER_TO_PROG (pr, class);
}

static void
pr_obj_lookup_class (progs_t *pr)
{
	const char *name = P_GSTRING (pr, 0);
	pr_class_t *class;

	class = Hash_Find (pr->classes, name);
	R_INT (pr) = POINTER_TO_PROG (pr, class);
}

static void
pr_obj_next_class (progs_t *pr)
{
	//XXX
	PR_RunError (pr, "%s, not implemented", __FUNCTION__);
}

//====================================================================

static void
pr_sel_get_name (progs_t *pr)
{
	pr_sel_t   *sel = &P_STRUCT (pr, pr_sel_t, 0);
	R_INT (pr) = sel->sel_id;
}

static void
pr_sel_get_type (progs_t *pr)
{
	pr_sel_t   *sel = &P_STRUCT (pr, pr_sel_t, 0);
	R_INT (pr) = sel->sel_types;
}

static void
pr_sel_get_uid (progs_t *pr)
{
	const char *name = P_GSTRING (pr, 0);
	pr_sel_t   *sel = Hash_Find (pr->selectors, name);
	RETURN_POINTER (pr, sel);
}

static void
pr_sel_register_name (progs_t *pr)
{
	//const char *name = P_GSTRING (pr, 0);
	//XXX
	PR_RunError (pr, "%s, not implemented", __FUNCTION__);
}

static void
pr_sel_is_mapped (progs_t *pr)
{
	//pr_sel_t   *sel = &P_STRUCT (pr, pr_sel_t, 0);
	//XXX
	PR_RunError (pr, "%s, not implemented", __FUNCTION__);
}

//====================================================================

static void
pr_class_get_class_method (progs_t *pr)
{
	pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0);
	pr_sel_t   *aSel = &P_STRUCT (pr, pr_sel_t, 1);
	pr_method_t *method;
	class = &G_STRUCT (pr, pr_class_t, class->class_pointer);
	method = obj_find_message (pr, class, aSel);
	RETURN_POINTER (pr, method);
}

static void
pr_class_get_instance_method (progs_t *pr)
{
	pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0);
	pr_sel_t   *aSel = &P_STRUCT (pr, pr_sel_t, 1);
	pr_method_t *method = obj_find_message (pr, class, aSel);
	RETURN_POINTER (pr, method);
}

#define CLASSOF(x) (&G_STRUCT (pr, pr_class_t, (x)->class_pointer))

static void
pr_class_pose_as (progs_t *pr)
{
	pr_class_t *impostor = &P_STRUCT (pr, pr_class_t, 0);
	pr_class_t *superclass = &P_STRUCT (pr, pr_class_t, 1);
	pointer_t  *subclass;

	subclass = &superclass->subclass_list;
	while (*subclass) {
		pr_class_t *sub = &P_STRUCT (pr, pr_class_t, *subclass);
		pointer_t   nextSub = sub->sibling_class;
		if (sub != impostor) {
			sub->sibling_class = impostor->subclass_list;
			sub->super_class = P_POINTER (pr, 0);	// impostor
			impostor->subclass_list = *subclass;	// sub
			CLASSOF (sub)->sibling_class = CLASSOF (impostor)->sibling_class;
			CLASSOF (sub)->super_class = impostor->class_pointer;
			CLASSOF (impostor)->subclass_list = sub->class_pointer;
		}
		*subclass = nextSub;
	}
	superclass->subclass_list = P_POINTER (pr, 0);	// impostor
	CLASSOF (superclass)->subclass_list = impostor->class_pointer;

	impostor->sibling_class = 0;
	CLASSOF (impostor)->sibling_class = 0;

	//XXX how much do I need to do here?!?
	//class_table_replace (super_class, impostor);
	R_INT (pr) = P_POINTER (pr, 0);	// impostor
}
#undef CLASSOF

static inline pr_id_t *
class_create_instance (progs_t *pr, pr_class_t *class)
{
	int         size = class->instance_size * sizeof (pr_type_t);
	pr_id_t    *id;

	id = PR_Zone_Malloc (pr, size);
	memset (id, 0, size);
	id->class_pointer = POINTER_TO_PROG (pr, class);
	return id;
}

static void
pr_class_create_instance (progs_t *pr)
{
	pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0);
	pr_id_t    *id = class_create_instance (pr, class);
	
	R_INT (pr) = POINTER_TO_PROG (pr, id);
}

static void
pr_class_get_class_name (progs_t *pr)
{
	pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0);
	R_INT (pr) = PR_CLS_ISCLASS (class) ? class->name
										: PR_SetString (pr, "Nil");
}

static void
pr_class_get_instance_size (progs_t *pr)
{
	pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0);
	R_INT (pr) = PR_CLS_ISCLASS (class) ? class->instance_size : 0;
}

static void
pr_class_get_meta_class (progs_t *pr)
{
	pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0);
	R_INT (pr) = PR_CLS_ISCLASS (class) ? class->class_pointer : 0;
}

static void
pr_class_get_super_class (progs_t *pr)
{
	pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0);
	R_INT (pr) = PR_CLS_ISCLASS (class) ? class->super_class : 0;
}

static void
pr_class_get_version (progs_t *pr)
{
	pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0);
	R_INT (pr) = PR_CLS_ISCLASS (class) ? class->version : -1;
}

static void
pr_class_is_class (progs_t *pr)
{
	pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0);
	R_INT (pr) = PR_CLS_ISCLASS (class);
}

static void
pr_class_is_meta_class (progs_t *pr)
{
	pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0);
	R_INT (pr) = PR_CLS_ISMETA (class);
}

static void
pr_class_set_version (progs_t *pr)
{
	pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0);
	if (PR_CLS_ISCLASS (class))
		class->version = P_INT (pr, 1);
}

static void
pr_class_get_gc_object_type (progs_t *pr)
{
	pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0);
	R_INT (pr) = PR_CLS_ISCLASS (class) ? class->gc_object_type : 0;
}

static void
pr_class_ivar_set_gcinvisible (progs_t *pr)
{
	//pr_class_t *impostor = &P_STRUCT (pr, pr_class_t, 0);
	//const char *ivarname = P_GSTRING (pr, 1);
	//int         gcInvisible = P_INT (pr, 2);
	//XXX
	PR_RunError (pr, "%s, not implemented", __FUNCTION__);
}

//====================================================================

static void
pr_method_get_imp (progs_t *pr)
{
	pr_method_t *method = &P_STRUCT (pr, pr_method_t, 0);

	R_INT (pr) = method->method_imp;
}

static void
pr_get_imp (progs_t *pr)
{
	//pr_class_t *class = &P_STRUCT (pr, pr_class_t, 0);
	//pr_sel_t   *sel = &P_STRUCT (pr, pr_sel_t, 1);
	//XXX
	PR_RunError (pr, "%s, not implemented", __FUNCTION__);
}

//====================================================================

static void
pr_object_dispose (progs_t *pr)
{
	pr_id_t    *object = &P_STRUCT (pr, pr_id_t, 0);
	PR_Zone_Free (pr, object);
}

static void
pr_object_copy (progs_t *pr)
{
	pr_id_t    *object = &P_STRUCT (pr, pr_id_t, 0);
	pr_class_t *class = &G_STRUCT (pr, pr_class_t, object->class_pointer);
	pr_id_t    *id;

	id = class_create_instance (pr, class);
	memcpy (id, object, sizeof (pr_type_t) * class->instance_size);
	R_INT (pr) = POINTER_TO_PROG (pr, id);
}

static void
pr_object_get_class (progs_t *pr)
{
	pr_id_t    *object = &P_STRUCT (pr, pr_id_t, 0);
	pr_class_t *class;

	if (object) {
		class = &G_STRUCT (pr, pr_class_t, object->class_pointer);
		if (PR_CLS_ISCLASS (class)) {
			R_INT (pr) = POINTER_TO_PROG (pr, class);
			return;
		}
		if (PR_CLS_ISMETA (class)) {
			R_INT (pr) = P_INT (pr, 0);
			return;
		}
	}
	R_INT (pr) = 0;
}

static void
pr_object_get_super_class (progs_t *pr)
{
	pr_id_t    *object = &P_STRUCT (pr, pr_id_t, 0);
	pr_class_t *class;

	if (object) {
		class = &G_STRUCT (pr, pr_class_t, object->class_pointer);
		if (PR_CLS_ISCLASS (class)) {
			R_INT (pr) = class->super_class;
			return;
		}
		if (PR_CLS_ISMETA (class)) {
			R_INT (pr) = ((pr_class_t *)object)->super_class;
			return;
		}
	}
	R_INT (pr) = 0;
}

static void
pr_object_get_meta_class (progs_t *pr)
{
	pr_id_t    *object = &P_STRUCT (pr, pr_id_t, 0);
	pr_class_t *class;

	if (object) {
		class = &G_STRUCT (pr, pr_class_t, object->class_pointer);
		if (PR_CLS_ISCLASS (class)) {
			R_INT (pr) = class->class_pointer;
			return;
		}
		if (PR_CLS_ISMETA (class)) {
			R_INT (pr) = ((pr_class_t *)object)->class_pointer;
			return;
		}
	}
	R_INT (pr) = 0;
}

static void
pr_object_get_class_name (progs_t *pr)
{
	pr_id_t    *object = &P_STRUCT (pr, pr_id_t, 0);
	pr_class_t *class;

	if (object) {
		class = &G_STRUCT (pr, pr_class_t, object->class_pointer);
		if (PR_CLS_ISCLASS (class)) {
			R_INT (pr) = class->name;
			return;
		}
		if (PR_CLS_ISMETA (class)) {
			R_INT (pr) = ((pr_class_t *)object)->name;
			return;
		}
	}
	RETURN_STRING (pr, "Nil");
}

static void
pr_object_is_class (progs_t *pr)
{
	pr_id_t    *object = &P_STRUCT (pr, pr_id_t, 0);

	if (object) {
		R_INT (pr) = PR_CLS_ISCLASS ((pr_class_t*)object);
		return;
	}
	R_INT (pr) = 0;
}

static void
pr_object_is_instance (progs_t *pr)
{
	pr_id_t    *object = &P_STRUCT (pr, pr_id_t, 0);

	R_INT (pr) = object_is_instance (pr, object);
}

static void
pr_object_is_meta_class (progs_t *pr)
{
	pr_id_t    *object = &P_STRUCT (pr, pr_id_t, 0);

	if (object) {
		R_INT (pr) = PR_CLS_ISMETA ((pr_class_t*)object);
		return;
	}
	R_INT (pr) = 0;
}

//====================================================================

static void
pr__i_Object__hash (progs_t *pr)
{
	R_INT (pr) = P_INT (pr, 0);
}

static void
pr__i_Object_error_error_ (progs_t *pr)
{
	pr_id_t    *self = &P_STRUCT (pr, pr_id_t, 0);
	const char *fmt = P_GSTRING (pr, 2);
	dstring_t  *dstr = dstring_new ();
	int         count = pr->pr_argc - 3;
	pr_type_t  *args = pr->pr_params[3];
	
	dsprintf (dstr, "error: %s (%s)\n%s",
			  PR_GetString (pr, object_get_class_name (pr, self)),
			  object_is_instance (pr, self) ? "instance" : "class", fmt);
	obj_verror (pr, self, 0, dstr->str, count, args);
}

static void
pr__c_Object__conformsToProtocol_ (progs_t *pr)
{
	//pr_id_t    *object = &P_STRUCT (pr, pr_id_t, 0);
	//pr_protocol_t *proto = &P_STRUCT (pr, pr_protocol_t, 2);
	//...
	//XXX
	PR_RunError (pr, "%s, not implemented", __FUNCTION__);
}

//====================================================================

static struct {
	const char *name;
	void      (*func)(progs_t *pr);
} obj_methods [] = {
	{"__obj_exec_class",		pr___obj_exec_class},

	{"obj_error",				pr_obj_error},
	{"obj_verror",				pr_obj_verror},
	{"obj_set_error_handler",	pr_obj_set_error_handler},
	{"obj_msg_lookup",			pr_obj_msg_lookup},
	{"obj_msg_lookup_super",	pr_obj_msg_lookup_super},
	{"obj_msg_sendv",			pr_obj_msg_sendv},
	{"obj_malloc",				pr_obj_malloc},
	{"obj_atomic_malloc",		pr_obj_atomic_malloc},
	{"obj_valloc",				pr_obj_valloc},
	{"obj_realloc",				pr_obj_realloc},
	{"obj_calloc",				pr_obj_calloc},
	{"obj_free",				pr_obj_free},
	{"obj_get_uninstalled_dtable", pr_obj_get_uninstalled_dtable},
	{"obj_msgSend",				pr_obj_msgSend},
	{"obj_msgSend_super",		pr_obj_msgSend_super},

	{"obj_get_class",			pr_obj_get_class},
	{"obj_lookup_class",		pr_obj_lookup_class},
	{"obj_next_class",			pr_obj_next_class},

	{"sel_get_name",			pr_sel_get_name},
	{"sel_get_type",			pr_sel_get_type},
	{"sel_get_uid",				pr_sel_get_uid},
	{"sel_register_name",		pr_sel_register_name},
	{"sel_is_mapped",			pr_sel_is_mapped},

	{"class_get_class_method",	pr_class_get_class_method},
	{"class_get_instance_method", pr_class_get_instance_method},
	{"class_pose_as",			pr_class_pose_as},
	{"class_create_instance",	pr_class_create_instance},
	{"class_get_class_name",	pr_class_get_class_name},
	{"class_get_instance_size",	pr_class_get_instance_size},
	{"class_get_meta_class",	pr_class_get_meta_class},
	{"class_get_super_class",	pr_class_get_super_class},
	{"class_get_version",		pr_class_get_version},
	{"class_is_class",			pr_class_is_class},
	{"class_is_meta_class",		pr_class_is_meta_class},
	{"class_set_version",		pr_class_set_version},
	{"class_get_gc_object_type", pr_class_get_gc_object_type},
	{"class_ivar_set_gcinvisible", pr_class_ivar_set_gcinvisible},

	{"method_get_imp",			pr_method_get_imp},
	{"get_imp",					pr_get_imp},

	{"object_copy",				pr_object_copy},
	{"object_dispose",			pr_object_dispose},
	{"object_get_class",		pr_object_get_class},
	{"object_get_class_name",	pr_object_get_class_name},
	{"object_get_meta_class",	pr_object_get_meta_class},
	{"object_get_super_class",	pr_object_get_super_class},
	{"object_is_class",			pr_object_is_class},
	{"object_is_instance",		pr_object_is_instance},
	{"object_is_meta_class",	pr_object_is_meta_class},

	{"_i_Object__hash",			pr__i_Object__hash},
	{"_i_Object_error_error_",		pr__i_Object_error_error_},
	{"_c_Object__conformsToProtocol_",	pr__c_Object__conformsToProtocol_},
};

void
PR_Obj_Progs_Init (progs_t *pr)
{
	size_t      i;

	for (i = 0; i < sizeof (obj_methods) / sizeof (obj_methods[0]); i++) {
		PR_AddBuiltin (pr, obj_methods[i].name, obj_methods[i].func, -1);
	}
}

int
PR_InitRuntime (progs_t *pr)
{
	int         fnum;
	pr_class_t **class_list, **class;
	pr_category_t **category_list, **category;

	if (!pr->selectors)
		pr->selectors = Hash_NewTable (1021, selector_get_key, 0, pr);
	else
		Hash_FlushTable (pr->selectors);

	if (!pr->classes)
		pr->classes = Hash_NewTable (1021, class_get_key, 0, pr);
	else
		Hash_FlushTable (pr->classes);

	if (!pr->categories) {
		pr->categories = Hash_NewTable (1021, 0, 0, pr);
		Hash_SetHashCompare (pr->categories,
							 category_get_hash, category_compare);
	} else {
		Hash_FlushTable (pr->categories);
	}

	pr->fields.this = ED_GetFieldIndex (pr, ".this");
	for (fnum = 0; fnum < pr->progs->numfunctions; fnum++) {
		if (strequal (PR_GetString (pr, pr->pr_functions[fnum].s_name),
					  ".ctor")) {
			PR_ExecuteProgram (pr, fnum);
		}
	}

	class_list = (pr_class_t **) Hash_GetList (pr->classes);
	if (*class_list) {
		pr_class_t *object_class;
		pointer_t   object_ptr;

		object_class = Hash_Find (pr->classes, "Object");
		if (object_class && !object_class->super_class)
			object_ptr = (pr_type_t *)object_class - pr->pr_globals;
		else
			PR_Error (pr, "root class Object not found");
		for (class = class_list; *class; class++)
			finish_class (pr, *class, object_ptr);
	}
	free (class_list);

	category_list = (pr_category_t **) Hash_GetList (pr->categories);
	if (*category_list) {
		for (category = category_list; *category; category++)
			finish_category (pr, *category);
	}
	free (category_list);
	return 1;
}