/*   
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 "IoState.h"

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

IOINLINE void IoObject_makeGrayIfWhite(IoObject *self)
{	
	if (self->mark.color == IOMARK_WHITE) 
	{ 
		IoState_makeGrayFromWhite_(IOSTATE, self); 
	}
}

IOINLINE IoObject *IoObject_addingRef_(IoObject *self, IoObject *ref)
{
	/*
	 int white = IOMARK_WHITE;
	 int gray = IOMARK_GRAY;
	 int black = IOMARK_BLACK;
	 int color = self->mark.color;
	 int refColor = ref->mark.color;
	 */
	//if (self->mark.color != IOMARK_WHITE)
	IoObject_makeGrayIfWhite(ref); // we should only do this if we are not white, right?
	
#ifdef IO_BLOCK_RECYCLING
	IoObject_setIsReferenced_(ref, 1);
#endif
	
	/*self->mark.isDirty = 1;*/
	return ref;
}

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

IOINLINE void IoObject_inlineSetSlot_to_(IoObject *self, 
                                         IoSymbol *slotName, 
                                         IoObject *value)
{ 
	IoObject_createSlotsIfNeeded(self);
	/*
	 if (!slotName->mark.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, 0x0, "too many slots");
	 }
	 */
	
	PHash_at_put_(self->slots, IOREF(slotName), IOREF(value));
	
	/*
	 if(PHash_at_put_(self->slots, IOREF(slotName), IOREF(value)))
	 { 
		 self->mark.isDirty = 1; 
	 }
	 */ 
}

IOINLINE IoObject *IoObject_rawGetSlot_context_(IoObject *self, 
                                                IoSymbol *slotName, 
                                                IoObject **context)
{
	register IoObject *v = (IoObject *)0x0;
	
	if (self->mark.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 *)0x0;
	
	if (self->mark.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_debugMakeGrayIfWhite(IoObject *self)
{
	//printf("IoObject_makeGrayIfWhite %s_%p", self->tag->name, (void *)self);
	//if (ISSEQ(self)) printf(" '%s'", CSTRING(self));
	if (strcmp(self->tag->name, "CFunction") == 0) 
	{
		//printf("IoObject_makeGrayIfWhite ");
		IoCFunction_print(self);
		//IoCFunction_printIfSpecial(self, "IoObject_makeGrayIfWhite");
	}

	IoObject_makeGrayIfWhite(self);
}
*/

IOINLINE void IoObject_mark(IoObject *self)
{
	/*
	IoObject *proto = IoState_protoWithName_(IOSTATE, "Object");
	void *p = IOSTATE->objectProto;
	
	if ((void *)self == (void *)p)
	{
		printf("marking Object proto --------------------------------- \n");
	}
	*/
	
	if (self->mark.ownsSlots)
	{ 
		//if (self == proto)
		//{
		//PHash_doOnKeyAndValue_(self->slots, (PHashDoCallback *)IoObject_debugMakeGrayIfWhite); 
		//} else {
		PHash_doOnKeyAndValue_(self->slots, (PHashDoCallback *)IoObject_makeGrayIfWhite); 
		//}
	}
	
    // mark protos 
	
	{ 
		IoObject **proto = self->protos;
		
		while (*proto != 0x0)
		{
			IoObject_makeGrayIfWhite(*proto);
			proto ++;
		}
	}
	
	
	if (self->mark.color == IOMARK_GRAY)
	{
		TagFreeFunc *func = self->tag->markFunc;
		
		if (func) 
		{
			(func)(self);
		}
		
		IoState_makeBlackFromGray_(IOSTATE, self);
	}
	/*
	else if (self->mark.color == IOMARK_BLACK)
	{
		printf("attempt to mark a black object\n");
		IoObject_print(self);
		printf("1\n");
		printf("1\n");
		printf("1\n");
	}
	 */
	//IoObjectGroup_findLoops(IOSTATE->grayGroup);
}

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

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;
}

IOINLINE IoObject *IoObject_forward(IoObject *self, IoObject *locals, IoMessage *m)
{
	IoState *state = IOSTATE;
	IoObject *context;
	IoObject *forwardSlot = IoObject_rawGetSlot_context_(self, state->forwardString, &context);
	
	if (Stack_totalSize(state->currentIoStack) > (CSTACK_STACKSIZE - (sizeof(void *) * 100))) 
	{ 
		IoState_error_(state, m, "stack overflow in '%s' responding to message '%s'", IoObject_name(self), CSTRING(IoMessage_name(m))); 
	}
	
	/*printf("%p forwarding '%s'\n", self, CSTRING(IoMessage_name(m)));*/
	
	if (forwardSlot) 
	{
		return IoObject_activate(forwardSlot, self, locals, m, context);
	}
	
	//printf("%s does not respond to message '%s'\n", 
	//	   IoObject_name(self), CSTRING(IoMessage_name(m)));
	
	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);
	//printf("[%p %s]\n", (void *)self, IoSeq_asCString(IoMessage_name(m)));
	
	if (5000 < Stack_totalSize(IOSTATE->currentIoStack)) 
	{ 
		IoState_error_(IOSTATE, m, "stack overflow in '%s' responding to message '%s'", 
					IoObject_name(self), CSTRING(IoMessage_name(m))); 
	}

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

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

	return IoObject_forward(self, locals, m);
}

#undef IO_IN_C_FILE
#endif
