
#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);
	
	{
		IoMessage *condition = IoMessage_rawArgAt_(m, 0);
		IoMessage *doMessage = IoMessage_rawArgAt_(m, 1);
		IoObject *result = IONIL(self);
		IoState *state = IOSTATE;
		unsigned char c;
		
		IoMessage_resetStopStatus(m); 
		IoState_pushRetainPool(IOSTATE);
		
		for (;;)
		{ 
			IoState_clearTopPool(IOSTATE);
			c = !ISNIL(IoMessage_locals_performOn_(condition, locals, self));
			
			if (!c) 
			{
				break;
			}
			
			result = IoMessage_locals_performOn_(doMessage, locals, self);
			
			if (IoMessage_handleStatus(doMessage)) 
			{
				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);
	{
		IoMessage *doMessage = IoMessage_rawArgAt_(m, 0);
		IoState *state = IOSTATE;
		IoObject *result;
		
		IoMessage_resetStopStatus(m); 
		IoState_pushRetainPool(IOSTATE);
		
		for (;;)
		{ 
			IoState_clearTopPool(IOSTATE);
			
			result = IoMessage_locals_performOn_(doMessage, locals, self);
			
			if (IoMessage_handleStatus(doMessage)) 
			{
				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 = 0x0;
		
		if (IoMessage_argCount(m) > 4)
		{ 
			increment = IoMessage_locals_doubleArgAt_(m, locals, 3); 
			doMessage = IoMessage_rawArgAt_(m, 4);
		}
		else
		{ 
			doMessage = IoMessage_rawArgAt_(m, 3); 
		}
		
		IoMessage_resetStopStatus(m); 
		
		if (startValue > endValue) 
		{
			increment = -1;
		}
		
		if (startValue == endValue) return IONIL(self);
		
		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);
			
#ifdef IO_BLOCK_RECYCLING
			/* a little trick to reuse the counter Number object if it hasn't been referenced */
			if (num && IoObject_isReferenced(num) == 0) 
			{
				IoNumber_rawSet(num, i);
			}
			else
#endif
			{
				/* the IoState_clearTopPool() call above ensures we don't accumulate these new numbers on the Io stack */
				num = IONUMBER(i);
				IoObject_addingRef_(locals, num);
				PHash_at_put_(locals->slots, slotName, num);
#ifdef IO_BLOCK_RECYCLING
				IoObject_setIsReferenced_(num, 0);
#endif
			} 
			
			/*IoObject_setSlot_to_(locals, slotName, IONUMBER(i));*/
			result = IoMessage_locals_performOn_(doMessage, locals, self);
			
			if (IoMessage_handleStatus(doMessage)) 
			{
				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);
	IoMessage_return_(m, v);
	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);
	}
	
	IoMessage_break(m, 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. ")
	*/
	
	IoMessage_continue(m); 
	return self; 
}


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.")
	*/
	
	int argCount = IoMessage_argCount(m);
	
	IOASSERT(argCount > 0, "Error: 'if' needs at least 1 argument\n");
	
	if (argCount == 1)
	{ 
		return IoMessage_locals_valueArgAt_(m, locals, 0); 
	}
	
	if (!ISNIL(IoMessage_locals_valueArgAt_(m, locals, 0)))
	{ 
		return IoMessage_locals_valueArgAt_(m, locals, 1); 
	}
	else if (argCount == 3)
	{ 
		return IoMessage_locals_valueArgAt_(m, locals, 2); 
	}
	
	return IONIL(self);
}

IoObject *IoObject_then(IoObject *self, IoObject *locals, IoMessage *m)
{
	/*#io
	docSlot("then(arg)", "Evaluates arg and returns Nop.")
	*/
	
	IoMessage_locals_valueArgAt_(m, locals, 0); 
	
	return IONOP(self);
}

IoObject *IoObject_ifTrue(IoObject *self, IoObject *locals, IoMessage *m)
{ 
	/*#io
	docSlot("ifTrue(expression)", "Evaluates argument and returns result.")
	*/
	
	return IoMessage_locals_valueArgAt_(m, locals, 0); 
}

IoObject *IoObject_ifFalse(IoObject *self, IoObject *locals, IoMessage *m)
{ 
	/*#io
	docSlot("ifFalse(expression)", "Returns Nil. (Does not evaluate the argument)")
	*/
	
	return IONIL(self); 
}

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;
	IoMessage_return_(m, IONIL(self));
	return self;
}

