/*#io
Object ioDoc(
		   */
#include "IoObject.h"
#include "IoNumber.h"

// loops ---------------------------------------

IoObject *IoObject_while(IoObject *self, IoObject *locals, IoMessage *m)
{
	/*#io
	docSlot("while(<condition>, expression)", 
		   "Keeps evaluating message until condition return Nil. 
Returns the result of the last message evaluated or Nil if none were evaluated.")
	*/
	
	IoMessage_assertArgCount_receiver_(m, 2, self);
	
	{
		IoObject *result = IONIL(self);
		IoState *state = IOSTATE;
		unsigned char c;
		
		IoState_resetStopStatus(IOSTATE);
		IoState_pushRetainPool(IOSTATE);
		
		for (;;)
		{ 
			IoState_clearTopPool(IOSTATE);
			c = ISTRUE(IoMessage_locals_valueArgAt_(m, locals, 0));
			
			if (!c) 
			{
				break;
			}
			
			result = (IoObject *)IoMessage_locals_valueArgAt_(m, locals, 1);
			
			if (IoState_handleStatus(IOSTATE))
			{
				goto done;
			}
		}
done:
			IoState_popRetainPoolExceptFor_(state, result);
		return result;
	}
}

IoObject *IoObject_loop(IoObject *self, IoObject *locals, IoMessage *m)
{
	/*#io
	docSlot("loop(expression)", 
		   "Keeps evaluating message until a break.")
	*/
	
	IoMessage_assertArgCount_receiver_(m, 1, self);
	{
		IoState *state = IOSTATE;
		IoObject *result;
		
		IoState_resetStopStatus(IOSTATE);
		IoState_pushRetainPool(state);
		
		for (;;)
		{ 
			IoState_clearTopPool(state);
			
			result = IoMessage_locals_valueArgAt_(m, locals, 0);
			
			if (IoState_handleStatus(IOSTATE))
			{
				goto done;
			}
		}
done:
			IoState_popRetainPoolExceptFor_(state, result);
		return result;
	}
}


IoObject *IoObject_for(IoObject *self, IoObject *locals, IoMessage *m)
{
	/*#io
	docSlot("for(<counter>, <start>, <end>, <do message>)", 
		   "See manual.html. ")
	*/
	
	IoMessage_assertArgCount_receiver_(m, 4, self);
	
	{
		IoState *state = IOSTATE;
		IoMessage *indexMessage = IoMessage_rawArgAt_(m, 0);
		IoMessage *doMessage;
		IoObject *result = IONIL(self);
		double i;
		IoSymbol *slotName = IoMessage_name(indexMessage);
		double startValue = IoMessage_locals_doubleArgAt_(m, locals, 1);
		double endValue   = IoMessage_locals_doubleArgAt_(m, locals, 2);
		double increment  = 1;
		IoNumber *num = NULL;
		
		if (IoMessage_argCount(m) > 4)
		{ 
			increment = IoMessage_locals_doubleArgAt_(m, locals, 3); 
			doMessage = IoMessage_rawArgAt_(m, 4);
		}
		else
		{ 
			doMessage = IoMessage_rawArgAt_(m, 3); 
		}
		
		/*
		if (startValue > endValue) 
		{
			increment = -1;
		}
		*/
		
		IoState_resetStopStatus(state); 
		IoState_pushRetainPool(state);
		
		for (i = startValue; ; i += increment)
		{
			if (increment > 0) 
			{ 
				if (i > endValue) break; 
			}
			else
			{ 
				if (i < endValue) break; 
			}
			
			/*if (result != locals && result != self) IoState_immediatelyFreeIfUnreferenced_(state, result);*/
			IoState_clearTopPool(state);
			
			{
				num = IONUMBER(i);
				IoObject_addingRef_(locals, num);
				PHash_at_put_(locals->slots, slotName, num);
				
				//IoObject_setSlot_to_(self, slotName, num);
			} 
			
			/*IoObject_setSlot_to_(locals, slotName, IONUMBER(i));*/
			result = IoMessage_locals_performOn_(doMessage, locals, self);
			
			if (IoState_handleStatus(IOSTATE)) 
			{
				goto done;
			}
		}
		
done:
			IoState_popRetainPoolExceptFor_(state, result);
		return result;
	}
}

IoObject *IoObject_return(IoObject *self, IoObject *locals, IoMessage *m)
{ 
	/*#io
	docSlot("return(anObject)", "Return anObject from the current execution block.")
	*/
	
	IoObject *v = IoMessage_locals_valueArgAt_(m, locals, 0);
	IoState_return(IOSTATE, v);
	return self;
}

IoObject *IoObject_returnIfNonNil(IoObject *self, IoObject *locals, IoMessage *m)
{ 
	/*#io
	docSlot("returnIfNonNil", "Returns the receiver from the current execution block if it is non nil. Otherwise returns the receiver locally.")
	*/
	
	if(!ISNIL(self)) 
	{
		IoState_return(IOSTATE, self);
	}
	
	return self;
}

IoObject *IoObject_break(IoObject *self, IoObject *locals, IoMessage *m)
{
	/*#io
	docSlot("break(optionalReturnValue)", "Break the current loop, if any. ")
	*/
	
	IoObject *v = IONIL(self);
	
	if (IoMessage_argCount(m) > 0)
	{
		v = IoMessage_locals_valueArgAt_(m, locals, 0);
	}
	
	IoState_break(IOSTATE, v);
	return self;
}

IoObject *IoObject_continue(IoObject *self, IoObject *locals, IoMessage *m)
{ 
	/*#io
	docSlot("continue", 
		   "Skip the rest of the current loop iteration and start on 
the next, if any. ")
	*/
	
	IoState_continue(IOSTATE);
	return self; 
}

IoObject *IoObject_eol(IoObject *self, IoObject *locals, IoMessage *m)
{
	/*#io
	docSlot(";", "Reset the eval context to the locals. ")
	*/
	IOSTATE->stopStatus = MESSAGE_STOP_STATUS_EOL;
}

IoObject *IoObject_if(IoObject *self, IoObject *locals, IoMessage *m)
{
	/*#io
	docSlot("if(<condition>, <trueMessage>, <optionalFalseMessage>)", 
		   "Evaluates trueMessage if condition evaluates to a non-Nil. 
Otherwise evaluates optionalFalseMessage if it is present. 
Returns the result of the evaluated message or Nil if none was evaluated.")
	*/
	
	const IoObject *const r = IoMessage_locals_valueArgAt_(m, locals, 0);
	const int condition = ISTRUE((IoObject *)r);
	const int index = condition ? 1 : 2;
	
	if (index < IoMessage_argCount(m))
		return IoMessage_locals_valueArgAt_(m, locals, index);
	
	return IOBOOL(self, condition);
}

IoObject *IoObject_tailCall(IoObject *self, IoObject *locals, IoMessage *m)
{ 
	/*#io
	docSlot("tailCall(expression)", 
		   """Does a tailCall on the currently executing Block. Example:

<pre>
Io> a := method(x, x = x + 1; if(x > 10, return x); tailCall(x))
==> method(x, updateSlot("x", x +(1));
		 if(x >(10), return(x));
		 tailCall(x))
Io> a(1)
==> 11
</pre>
""")
	*/
	
	IOSTATE->tailCallMessage = m;
	IoState_return(IOSTATE, IONIL(self));
	return self;
}

