/*   
docCopyright("Steve Dekorte", 2002)
docLicense("BSD revised")
*/

#ifdef IOOBJECT_C 
#define IO_IN_C_FILE
#endif
#include "Common_inline.h"
#ifdef IO_DECLARE_INLINES

#include "IoVMApi.h"

#include "IoState.h"

IOINLINE int IoObject_isWhite(IoObject *self)
{
	return Collector_markerIsWhite_(IOCOLLECTOR, &(self->marker));
}

IOINLINE int IoObject_isGray(IoObject *self)
{
	return Collector_markerIsGray_(IOCOLLECTOR, &(self->marker));
}

IOINLINE int IoObject_isBlack(IoObject *self)
{
	return Collector_markerIsBlack_(IOCOLLECTOR, &(self->marker));
}

IOINLINE void IoObject_createSlotsIfNeeded(IoObject *self)
{
	if (!self->ownsSlots)
	{ 
		/*printf("creating slots for %s %p\n", self->tag->name, (void *)self);*/
		IoObject_createSlots(self); 
	}
}

IOINLINE void IoObject_rawRemoveAllProtos(IoObject *self)
{
	IoObject_createSlotsIfNeeded(self);
	{
	int count = IoObject_rawProtosCount(self);
	memset(self->protos, 0, count * sizeof(IoObject *));
	}
}

IOINLINE void IoObject_shouldMark(IoObject *self)
{	
	Collector_shouldMark_(IOCOLLECTOR, self);
}

IOINLINE void IoObject_shouldMarkIfNonNull(IoObject *self)
{	
	if (self) 
	{
		IoObject_shouldMark(self);
	}
}

IOINLINE IoObject *IoObject_addingRef_(IoObject *self, IoObject *ref)
{	 
	Collector_value_addingRefTo_(IOCOLLECTOR, self, ref);
	//self->isDirty = 1;
	return ref;
}

IOINLINE void IoObject_inlineSetSlot_to_(IoObject *self, 
                                         IoSymbol *slotName, 
                                         IoObject *value)
{ 
	IoObject_createSlotsIfNeeded(self);
	/*
	 if (!slotName->isSymbol) 
	 { 
		 printf("Io System Error: setSlot slotName not symbol\n"); 
		 exit(1); 
	 }
	 */
	
	/*
	 if (PHash_count(self->slots) > 200)
	 { 
		 printf("PHash %p slots %i\n", (void *)(self->slots), PHash_count(self->slots));
		 IoState_error_(IOSTATE, NULL, "too many slots");
	 }
	 */
	
	PHash_at_put_(self->slots, IOREF(slotName), IOREF(value));
	
	/*
	 if(PHash_at_put_(self->slots, IOREF(slotName), IOREF(value)))
	 { 
		 self->isDirty = 1; 
	 }
	 */ 
}

IOINLINE IoObject *IoObject_rawGetSlot_context_(IoObject *self, 
                                                IoSymbol *slotName, 
                                                IoObject **context)
{
	register IoObject *v = (IoObject *)NULL;
	
	if (self->ownsSlots)
	{
		v = (IoObject *)PHash_at_(self->slots, slotName);
		
		if (v) 
		{
			*context = self;
			return v; 
		}
	}
	
	IoObject_setHasDoneLookup_(self, 1);
	
	{
		register IoObject **protos = self->protos;
		
		for (; *protos; protos ++)
		{
			if (IoObject_hasDoneLookup((*protos))) 
			{
				continue;
			}
			
			v = IoObject_rawGetSlot_context_(*protos, slotName, context);
			
			if (v) 
			{
				break;
			}
		}
	}
	
	IoObject_setHasDoneLookup_(self, 0);
	
	return v;
}

IOINLINE IoObject *IoObject_rawGetSlot_(IoObject *self, IoSymbol *slotName)
{
	register IoObject *v = (IoObject *)NULL;
	
	if (self->ownsSlots)
	{
		v = (IoObject *)PHash_at_(self->slots, slotName);
		
		if (v) return v; 
	}
	
	IoObject_setHasDoneLookup_(self, 1);
	
	{
		register IoObject **protos = self->protos;
		
		for (; *protos; protos ++)
		{
			if (IoObject_hasDoneLookup((*protos))) 
			{
				continue;
			}
			
			v = IoObject_rawGetSlot_(*protos, slotName);
			
			if (v) break;
		}
	}
	
	IoObject_setHasDoneLookup_(self, 0);
	
	return v;
}

IOINLINE IoObject *IoObject_firstProto(IoObject *self)
{
	return self->protos[0];
}

IOINLINE IoObject *IoObject_rawProtoAt_(IoObject *self, int i)
{
	return self->protos[i];
}

IOINLINE void IoObject_mark(IoObject *self)
{	
	/*
	if (self->isLocals)
	{
		printf("mark %p locals\n", (void *)self);
	}
	else
	{
		printf("mark %p %s\n", (void *)self, IoObject_name(self));
	}
	*/
	
	if (self->ownsSlots)
	{ 
		PHash_doOnKeyAndValue_(self->slots, (PHashDoCallback *)IoObject_shouldMark); 
	}
	
	// mark protos 
	
	{ 
		IoObject **proto = self->protos;
		
		while (*proto != NULL)
		{
			IoObject_shouldMark(*proto);
			proto ++;
		}
	}
	
	{
		TagMarkFunc *func = self->tag->markFunc;
		
		if (func) 
		{
			(func)(self);
		}
	}
}

IoObject *IoObject_addingRef_(IoObject *self, IoObject *ref);
IOVM_API int IoObject_hasCloneFunc_(IoObject *self, TagCloneFunc *func);

IOINLINE IoObject *IoObject_activate(IoObject *self, 
                                     IoObject *target, 
                                     IoObject *locals, 
                                     IoMessage *m,
                                     IoObject *slotContext)
{
	//TagActivateFunc *act = self->tag->activateFunc;
	//return act ? (IoObject *)((*act)(self, target, locals, m, slotContext)) : self;
	//printf("activate %s %i\n", self->tag->name, self->isActivatable);

	return self->isActivatable ? (IoObject *)((self->tag->activateFunc)(self, target, locals, m, slotContext)) : self;
	//return self->tag->activateFunc ? (IoObject *)((self->tag->activateFunc)(self, target, locals, m, slotContext)) : self;

}

IOINLINE IoObject *IoObject_forward(IoObject *self, IoObject *locals, IoMessage *m)
{
	IoState *state = IOSTATE;
	IoObject *context;
	IoObject *forwardSlot = IoObject_rawGetSlot_context_(self, state->forwardSymbol, &context);
	
	if (Coro_stackSpaceAlmostGone((Coro*)IoCoroutine_cid(state->currentCoroutine))) 
	{ 

		IoState_error_(IOSTATE, m, "stack overflow in forward while sending '%s' message to a '%s' object", 
					CSTRING(IoMessage_name(m)), IoObject_name(self)); 
	}
		
	if (forwardSlot) 
	{
		return IoObject_activate(forwardSlot, self, locals, m, context);
	}
	
	IoState_error_(state, m, "'%s' does not respond to message '%s'", 
				IoObject_name(self), CSTRING(IoMessage_name(m)));
	return self;
}

IOINLINE IoObject *IoObject_perform(IoObject *self, IoObject *locals, IoMessage *m)
{
	IoObject *context;
	IoObject *slotValue = IoObject_rawGetSlot_context_(self, IoMessage_name(m), &context);

	// note: coro chaining was moved to IoBlock.c

	if (slotValue) 
	{
		return IoObject_activate(slotValue, self, locals, m, context);
	}

	if (self->isLocals)
	{
		return IoObject_localsForward(self, locals, m);
	}

	return IoObject_forward(self, locals, m);
}

#undef IO_IN_C_FILE
#endif
