/*#io
Sandbox ioDoc(
		    docCopyright("Steve Dekorte", 2002)
		    docLicense("BSD revised")
		    docObject("Sandbox")
		    docDescription("Sandbox can be used to run separate instances of Io within the same process.")
		    docCategory("Core")
		    */

#include "IoSandbox.h"
#include "IoSeq.h"
#include "IoState.h"
#include "IoCFunction.h"
#include "IoObject.h"
#include "IoList.h"
#include "IoSeq.h"
#include "ByteArray.h"
#include "PortableTruncate.h"
#include <errno.h>
#include <stdio.h>

#define DATA(self) ((IoSandboxData *)IoObject_dataPointer(self))

IoTag *IoSandbox_tag(void *state)
{
	IoTag *tag = IoTag_newWithName_("Sandbox");
	tag->state = state;
	tag->cloneFunc = (TagCloneFunc *)IoSandbox_rawClone;
	tag->freeFunc  = (TagFreeFunc *)IoSandbox_free;
	tag->writeToStoreOnStreamFunc  = (TagWriteToStoreOnStreamFunc *)IoSandbox_writeToStore_stream_;
	tag->readFromStoreOnStreamFunc = (TagReadFromStoreOnStreamFunc *)IoSandbox_readFromStore_stream_;
	return tag;
}

IoSandbox *IoSandbox_proto(void *state)
{
	IoMethodTable methodTable[] = {
	{"messageCount", IoSandbox_messageCount},
	{"setMessageCount", IoSandbox_setMessageCount},
	{"timeLimit", IoSandbox_timeLimit},
	{"setTimeLimit", IoSandbox_setTimeLimit},
	{"doSandboxString", IoSandbox_doSandboxString},
	{NULL, NULL},
	};
	
	IoObject *self = IoObject_new(state);
	self->tag = IoSandbox_tag(state);
	
	IoState_registerProtoWithFunc_((IoState *)state, self, IoSandbox_proto);
	
	IoObject_addMethodTable_(self, methodTable);
	
	return self;
}

#define BOXSTATE(self) ((IoState *)(IoObject_dataPointer(self)))

IoState *IoSandbox_boxState(IoSandbox *self)
{
	if (!IoObject_dataPointer(self))
	{
		IoObject_setDataPointer_(self, IoState_new());
		IoSandbox_addPrintCallback(self);
	}
	
	return (IoState *)(IoObject_dataPointer(self));
}

void IoSandbox_addMakeSecureMethod(IoSandbox *self)
{
	/*#io
	docSlot("makeSecure", 
		   "Removes Collector DynLib File Directory Debugger Store Sandbox objects.")
	*/
	
	char *s = "makeSecure := method("
	"Object removeSlots := method(\n"
	"    stackLoop := block(\n"
	"        stackName := call argAt(0) name\n"
	"        indexName := call argAt(1) name\n"
	"        stack := list(call evalArgAt(2))\n"
	"        body := call argAt(3)\n"
	"\n"
	"        call sender setSlot(stackName, stack)\n"
	"        while(stack size > 0,\n"
	"            call sender setSlot(indexName, stack pop)\n"
	"            call sender doMessage(body)\n"
	"        )\n"
	"    ) setIsActivatable(true)\n"
	"\n"
	"    stackLoop(stack, m, call message arguments first,\n"
	"        self removeSlot(m name)\n"
	"        if (m next isNil not, stack append(m next))\n"
	"        if (m attached isNil not, stack append(m attached))\n"
	"    )\n"
	")\n"
	"\n"
	"# Remove some of the Objects from IoVM\n"
	"IoVM removeSlots(Collector DynLib File Directory Debugger Store Sandbox)\n"
	"\n"
	"# Remove the dangerous CFunctions\n"
	"System removeSlots(getenv system exit)\n"
	"Object removeSlots(doFile launchFile)\n"
	"\n"
//    "# Remove the printing CFunctions\n"
//    "(?keepPrintingCFunctions) ifNil(\n"
//    "    Number removeSlots(print linePrint)\n"
//    "    Duration removeSlots(print)\n"
//    "    String removeSlots(print linePrint)\n"
//    "    Nil removeSlots(print)\n"
//    "    Date removeSlots(print)\n"
//    "    Buffer removeSlots(print)\n"
//    "    Locals removeSlots(print write writeln)\n"
//    "    Block removeSlots(print)\n"
//    "    Object removeSlots(print write writeln)\n"
//    ")\n"
//    "\n"
	"# Remove the Protos we don't need\n"
	"#Protos removeSlots(IoServer IoDesktop)\n"
	"\n"
	"# Clean up\n"
	"Object removeSlot(\"removeSlots\")\n"
	")"
	;
	IoObject_rawDoString_label_(self, IOSYMBOL(s), IOSYMBOL("[Sandbox]"));
}

IoSandbox *IoSandbox_rawClone(IoSandbox *proto)
{
	IoObject *self = IoObject_rawClonePrimitive(proto);
	return self;
}

void IoSandbox_addPrintCallback(IoSandbox *self)
{
	IoState *boxState = IoSandbox_boxState(self);
	IoState_callbackContext_(boxState, self);
	IoState_printCallback_(boxState, (IoStatePrintCallback *)IoSandbox_printCallback);  
}

void IoSandbox_printCallback(IoSandbox *self, const char *s)
{
	IoState *state = IOSTATE;
	IoSeq *buf = IOSEQ((const unsigned char *)s, (unsigned int)strlen(s));
	IoMessage *m = IoMessage_newWithName_(state, IOSYMBOL("printCallback"));
	IoMessage *arg = IoMessage_newWithName_returnsValue_(state, IOSYMBOL("buffer"), buf);
	IoMessage_addArg_(m, arg);
	IoMessage_locals_performOn_(m, state->lobby, self);
}

IoSandbox *IoSandbox_new(void *state)
{
	IoObject *proto = IoState_protoWithInitFunction_((IoState *)state, IoSandbox_proto);
	return IOCLONE(proto);
}

void IoSandbox_free(IoSandbox *self)
{
	if (IoObject_dataPointer(self)) 
	{
		IoState_free(IoSandbox_boxState(self));
	}
}

void IoSandbox_writeToStore_stream_(IoSandbox *self, IoStore *store, BStream *stream)
{
}

void *IoSandbox_readFromStore_stream_(IoSandbox *self, IoStore *store, BStream *stream)
{
	return self;
}

/* ----------------------------------------------------------- */

IoNumber *IoSandbox_messageCount(IoSandbox *self, IoObject *locals, IoMessage *m)
{
	/*#io
	docSlot("messageCount", 
		   "Returns a number containing the messageCount limit of the Sandbox. ")
	*/
	
	IoState *boxState = IoSandbox_boxState(self);
	return IONUMBER(boxState->messageCountLimit);
}

IoObject *IoSandbox_setMessageCount(IoSandbox *self, IoObject *locals, IoMessage *m)
{ 
	/*#io
	docSlot("setMessageCount(anInteger)", 
		   "Sets the messageCount limit of the receiver. ")
	*/
	
	IoState *boxState = IoSandbox_boxState(self);
	boxState->messageCountLimit = IoMessage_locals_intArgAt_(m, locals, 0);
	return self; 
}

IoNumber *IoSandbox_timeLimit(IoSandbox *self, IoObject *locals, IoMessage *m)
{
	/*#io
	docSlot("timeLimit", 
		   "Returns a number containing the time limit of calls made to the Sandbox. ")
	*/
	
	IoState *boxState = IoSandbox_boxState(self);
	return IONUMBER(boxState->timeLimit);
}

IoObject *IoSandbox_setTimeLimit(IoSandbox *self, IoObject *locals, IoMessage *m)
{ 
	/*#io
	docSlot("setTimeLimit(aDouble)", 
		   "Sets the time limit of the Sandbox. ")
	*/
	
	IoState *boxState = IoSandbox_boxState(self);
	boxState->timeLimit = IoMessage_locals_doubleArgAt_(m, locals, 0);
	return self; 
}

IoObject *IoSandbox_doSandboxString(IoSandbox *self, IoObject *locals, IoMessage *m)
{
	/*#io
	docSlot("doSandboxString(aString)", 
		   "Evaluate aString instead the Sandbox. ")
	*/
	
	IoState *boxState = IoSandbox_boxState(self);
	char *s = IoMessage_locals_cStringArgAt_(m, locals, 0);
	
	IoObject *result = IoState_doSandboxCString_(boxState, s);
	
	if (ISSYMBOL(result))
	{
		return IOSYMBOL(CSTRING(result));
	}
	
	if (ISNUMBER(result))
	{
		return IONUMBER(CNUMBER(result));
	}    
	
	return IONIL(self);
}