/*
 *	@file 	ejsProc.c
 *	@brief 	EJS support functions
 */
/********************************* Copyright **********************************/
/*
 *	@copy	default.g
 *	
 *	Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved.
 *	Portions Copyright (c) GoAhead Software, 1995-2000. All Rights Reserved.
 *	
 *	This software is distributed under commercial and open source licenses.
 *	You may use the GPL open source license described below or you may acquire 
 *	a commercial license from Mbedthis Software. You agree to be fully bound 
 *	by the terms of either license. Consult the LICENSE.TXT distributed with 
 *	this software for full details.
 *	
 *	This software is open source; you can redistribute it and/or modify it 
 *	under the terms of the GNU General Public License as published by the 
 *	Free Software Foundation; either version 2 of the License, or (at your 
 *	option) any later version. See the GNU General Public License for more 
 *	details at: http://www.mbedthis.com/downloads/gplLicense.html
 *	
 *	This program is distributed WITHOUT ANY WARRANTY; without even the 
 *	implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 *	
 *	This GPL license does NOT permit incorporating this software into 
 *	proprietary programs. If you are unable to comply with the GPL, you must
 *	acquire a commercial license to use this software. Commercial licenses 
 *	for this software and support services are available from Mbedthis 
 *	Software at http://www.mbedthis.com 
 *	
 *	@end
 */
/********************************** Includes **********************************/

#include	"ejsInternal.h"

#if BLD_FEATURE_EJS

/****************************** Forward Declarations **************************/
/*
 *	Object constructors
 */
static int 		objectConsProc(EjsHandle eid, int argc, MprVar **argv);
static int 		arrayConsProc(EjsHandle eid, int argc, MprVar **argv);
static int 		booleanConsProc(EjsHandle eid, int argc, MprVar **agv);
static int 		numberConsProc(EjsHandle eid, int argc, MprVar **argv);
static int 		stringConsProc(EjsHandle eid, int argc, MprVar **argv);

/*
 *	Core functions
 */
static int 		toStringProc(EjsHandle eid, int argc, MprVar **argv);
static int 		valueOfProc(EjsHandle eid, int argc, MprVar **argv);

/*
 *	Triggers
 */
static MprVarTriggerStatus lengthTrigger(MprVarTriggerOp op, 
	MprProperties *parentProperties, MprVar *prop, MprVar *newValue,
	bool copyRef);

/******************************************************************************/
/*
 *	Routine to create the base common to all object types
 */

MprVar ejsCreateObj(const char *name, int hashSize)
{
	MprVar	o;

	o = mprCreateObjVar(name, hashSize);
	if (o.type == MPR_TYPE_UNDEFINED) {
		mprAssert(0);
		return o;
	}

	mprCreatePropertyValue(&o, "toString", 
		mprCreateCFunctionVar(toStringProc, 0, MPR_VAR_SCRIPT_HANDLE));
	mprCreatePropertyValue(&o, "valueOf", 
		mprCreateCFunctionVar(valueOfProc, 0, MPR_VAR_SCRIPT_HANDLE));
	return o;
}

/******************************************************************************/
/*
 *	Routine to destroy a variable
 */

bool ejsDestroyVar(MprVar *obj)
{
	return mprDestroyVar(obj);
}

/******************************************************************************/
/*
 *	Routine to create the base array type
 */

MprVar ejsCreateArray(const char *name, int size)
{
	MprVar	obj, *lp, undef;
	char	idx[16];
	int		i;

	/*	Sanity limit for size of hash table */

	obj = ejsCreateObj(name, max(size, 503));
	if (obj.type == MPR_TYPE_UNDEFINED) {
		mprAssert(0);
		return obj;
	}

	undef = mprCreateUndefinedVar();
	for (i = 0; i < size; i++) {
		mprItoa(i, idx, sizeof(idx));
		mprCreateProperty(&obj, idx, &undef);
	}

	lp = mprCreatePropertyValue(&obj, "length", mprCreateIntegerVar(size));
	mprAssert(lp);

	mprSetVarReadonly(lp, 1);
	mprAddVarTrigger(lp, lengthTrigger);

	return obj;
}

/******************************************************************************/
/******************************** Constructors ********************************/
/******************************************************************************/
/*
 *	Object constructor. Nothing really done here. For future expansion.
 */

static int objectConsProc(EjsHandle eid, int argc, MprVar **argv)
{
#if XX_UNUSED_XX
	MprVar	*obj;
	Ejs		*ep;

	if((ep = ejsPtr(eid)) == NULL) {
		return -1;
	}

	obj = mprGetProperty(ep->local, "this", 0);
	mprAssert(obj);
#endif
	return 0;
}

/******************************************************************************/
/*
 *	Array constructor
 */

static int arrayConsProc(EjsHandle eid, int argc, MprVar **argv)
{
	MprVar	*obj, *lp, undef;
	Ejs		*ep;
	char	idx[16];
	int		i, max;

	objectConsProc(eid, argc, argv);

	if((ep = ejsPtr(eid)) == NULL) {
		return -1;
	}
	obj = mprGetProperty(ep->local, "this", 0);
	mprAssert(obj);


	if (argc == 1 && mprVarIsNumber(argv[0]->type)) {
		/*
		 *	x = new Array(size);
		 */
		undef = mprCreateUndefinedVar();
		max = (int) mprVarToInteger(argv[0]);
		for (i = 0; i < max; i++) {
			mprItoa(i, idx, sizeof(idx));
			mprCreateProperty(obj, idx, &undef);
		}
	} else {
		/*
		 *	x = new Array(element0, element1, ..., elementN):
		 */
		max = argc;
		for (i = 0; i < max; i++) {
			mprItoa(i, idx, sizeof(idx));
			mprCreateProperty(obj, idx, argv[i]);
		}
	}

	lp = mprCreatePropertyValue(obj, "length", mprCreateIntegerVar(max));
	mprAssert(lp);

	mprSetVarReadonly(lp, 1);
	mprAddVarTrigger(lp, lengthTrigger);

	return 0;
}

/******************************************************************************/
/*
 *	Boolean constructor
 */

static int booleanConsProc(EjsHandle eid, int argc, MprVar **argv)
{
	objectConsProc(eid, argc, argv);
	return 0;
}

/******************************************************************************/
#if FUTURE
/*
 *	Date constructor
 */

static int dateConsProc(EjsHandle eid, int argc, MprVar **argv)
{
	objectConsProc(eid, argc, argv);
	return 0;
}

#endif
/******************************************************************************/
/*
 *	Number constructor
 */

static int numberConsProc(EjsHandle eid, int argc, MprVar **argv)
{
	objectConsProc(eid, argc, argv);
	return 0;
}

/******************************************************************************/
/*
 *	String constructor
 */

static int stringConsProc(EjsHandle eid, int argc, MprVar **argv)
{
	objectConsProc(eid, argc, argv);
	return 0;
}

/******************************************************************************/
/********************************** Functions *********************************/
/******************************************************************************/

static int toStringProc(EjsHandle eid, int argc, MprVar **argv)
{
	MprVar	*obj;
	Ejs		*ep;
	char	*buf;
	int		radix;

	if (argc == 0) {
		radix = 10;

	} else if (argc == 1) {
		radix = (int) mprVarToInteger(argv[0]);

	} else {
		mprAssert(0);
		return -1;
	}

	if((ep = ejsPtr(eid)) == NULL) {
		return -1;
	}

	obj = mprGetProperty(ep->local, "this", 0);
	mprAssert(obj);

	mprVarToString(&buf, MPR_MAX_STRING, 0, obj);
	mprCopyVarValue(&ep->result, mprCreateStringVar(buf, 0), MPR_SHALLOW_COPY);
	mprFree(buf);

	return 0;
}

/******************************************************************************/

static int valueOfProc(EjsHandle eid, int argc, MprVar **argv)
{
	MprVar	*obj;
	Ejs		*ep;

	if (argc != 0) {
		mprAssert(0);
		return -1;
	}

	if((ep = ejsPtr(eid)) == NULL) {
		return -1;
	}

	obj = mprGetProperty(ep->local, "this", 0);
	mprAssert(obj);

	switch (obj->type) {
	default:
	case MPR_TYPE_UNDEFINED:
	case MPR_TYPE_NULL:
	case MPR_TYPE_CFUNCTION:
	case MPR_TYPE_OBJECT:
	case MPR_TYPE_FUNCTION:
	case MPR_TYPE_STRING_CFUNCTION:
	case MPR_TYPE_PTR:
		mprCopyVar(&ep->result, obj, MPR_SHALLOW_COPY);
		break;

	case MPR_TYPE_STRING:
		mprCopyVarValue(&ep->result, mprCreateIntegerVar(atoi(obj->string)), 0);
		break;

	case MPR_TYPE_BOOL:
	case MPR_TYPE_INT:
#if BLD_FEATURE_INT64
	case MPR_TYPE_INT64:
#endif
#if BLD_FEATURE_FLOATING_POINT
	case MPR_TYPE_FLOAT:
#endif
		mprCopyVar(&ep->result, obj, 0);
		break;
	} 
	return 0;
}

/******************************************************************************/
/*
 *	Var access trigger on the Array.length property. Return the count of
 *	enumerable properties (don't count functions).
 */

static MprVarTriggerStatus lengthTrigger(MprVarTriggerOp op, 
	MprProperties *parentProperties, MprVar *prop, MprVar *newValue, 
	bool copyRef)
{
	switch (op) {
	case MPR_VAR_READ:
		/*
		 *	Subtract one for the length property
	 	 *	FUTURE -- need an API to access parentProperties
		 *	FUTURE -- contradiction to be read-only yet allow USE_NEW_VALUE.
		 *		API needs finer control.
		 */
		*newValue = mprCreateIntegerVar(parentProperties->numDataItems - 1);
		return MPR_TRIGGER_USE_NEW_VALUE;

	case MPR_VAR_WRITE:
		return MPR_TRIGGER_ABORT;

	case MPR_VAR_CREATE_PROPERTY:
	case MPR_VAR_DELETE_PROPERTY:
	case MPR_VAR_DELETE:
	default:
		break;
	}
	return MPR_TRIGGER_PROCEED;
}

/******************************************************************************/
/**************************** Extension Functions *****************************/
/******************************************************************************/
/*
 *	Assert 
 */

static int assertProc(EjsHandle eid, int argc, MprVar **argv)
{
	bool	b;

	if (argc < 1) {
		ejsSetErrorMsg(eid, "usage: assert(condition)\n");
		return -1;
	}
	b = mprVarToBool(argv[0]);
	if (b == 0) {
		ejsSetErrorMsg(eid, "Assertion failure\n");
		return -1;
	}
	ejsSetReturnValue(eid, mprCreateBoolVar(b));
	return 0;
}

/******************************************************************************/
/*
 *	Exit 
 */

static int exitProc(EjsHandle eid, int argc, MprVar **argv)
{
	int			status;

	if (argc < 1) {
		ejsSetErrorMsg(eid, "usage: exit(status)\n");
		return -1;
	}
	status = (int) mprVarToInteger(argv[0]);
	ejsSetExitStatus(eid, status);

	ejsSetReturnValue(eid, mprCreateStringVar("", 0));
	return 0;
}

/******************************************************************************/

static void printVar(MprVar *vp, int recurseCount, int indent)
{
	MprVar	*np;
	char	*buf;
	int		i;

	if (recurseCount > 5) {
		write(1, "Skipping - recursion too deep\n", 29);
		return;
	}

	for (i = 0; i < indent; i++) {
		write(1, "  ", 2);
	}

	if (vp->type == MPR_TYPE_OBJECT) {
		if (vp->name) {
			write(1, vp->name, strlen(vp->name));
		} else {
			write(1, "unknown", 7);
		}
		write(1, ": {\n", 4);
		np = mprGetFirstProperty(vp, MPR_ENUM_DATA);
		while (np) {
			if (strcmp(np->name, "local") == 0 ||
					strcmp(np->name, "global") == 0 ||
					strcmp(np->name, "this") == 0) {
				np = mprGetNextProperty(vp, np, MPR_ENUM_DATA);
				continue;
			}
			printVar(np, recurseCount + 1, indent + 1);
			np = mprGetNextProperty(vp, np, MPR_ENUM_DATA);
			if (np) {
				write(1, ",\n", 2);
			}
		}
		write(1, "\n", 1);
		for (i = 0; i < indent; i++) {
			write(1, "  ", 2);
		}
		write(1, "}", 1);

	} else {
		if (vp->name) {
			write(1, vp->name, strlen(vp->name));
		} else {
			write(1, "unknown", 7);
		}
		write(1, ": ", 2);

		/*	FUTURE -- other types ? */
		mprVarToString(&buf, MPR_MAX_STRING, 0, vp);
		if (vp->type == MPR_TYPE_STRING) {
			write(1, "\"", 1);
		}
		write(1, buf, strlen(buf));
		if (vp->type == MPR_TYPE_STRING) {
			write(1, "\"", 1);
		}
		mprFree(buf);
	}
}

/******************************************************************************/
/*
 *	Print the args to stdout
 */

static int printVarsProc(EjsHandle eid, int argc, MprVar **argv)
{
	MprVar	*vp;
	char	*buf;
	int		i;

	for (i = 0; i < argc; i++) {
		vp = argv[i];
		switch (vp->type) {
		case MPR_TYPE_OBJECT:
			printVar(vp, 0, 0);
			break;
		default:
			mprVarToString(&buf, MPR_MAX_STRING, 0, vp);
			write(1, buf, strlen(buf));
			mprFree(buf);
			break;
		}
	}
	write(1, "\n", 1);

	ejsSetReturnValue(eid, mprCreateStringVar("", 0));
	return 0;
}

/******************************************************************************/
/*
 *	Print the args to stdout
 */

static int printProc(EjsHandle eid, int argc, MprVar **argv)
{
	char	*buf;
	int		i;

	for (i = 0; i < argc; i++) {
		mprVarToString(&buf, MPR_MAX_STRING, 0, argv[i]);
		write(1, buf, strlen(buf));
		mprFree(buf);
	}
	return 0;
}

/******************************************************************************/
/*
 *	println
 */

static int printlnProc(EjsHandle eid, int argc, MprVar **argv)
{
	printProc(eid, argc, argv);
	write(1, "\n", 1);
	return 0;
}

/******************************************************************************/
/*
 *	Trace 
 */

static int traceProc(EjsHandle eid, int argc, char **argv)
{
	if (argc == 1) {
		mprLog(0, "%s", argv[0]);

	} else if (argc == 2) {
		mprLog(atoi(argv[0]), "%s", argv[1]);

	} else {
		ejsSetErrorMsg(eid, "Usage: trace([level], message)");
		return -1;
	}
	ejsSetReturnString(eid, "");
	return 0;
}

/******************************************************************************/
/*
 *	Return the object reference count
 */

static int refCountProc(EjsHandle eid, int argc, MprVar **argv)
{
	MprVar		*vp;
	int			count;

	vp = argv[0];
	if (vp->type == MPR_TYPE_OBJECT) {
		count = mprGetVarRefCount(vp);
		ejsSetReturnValue(eid, mprCreateIntegerVar(count));
	} else {
		ejsSetReturnValue(eid, mprCreateIntegerVar(0));
	}

	return 0;
}

/******************************************************************************/
/*
 *	Evaluate a sub-script. It is evaluated in the same variable scope as
 *	the calling script / function.
 */

static int evalScriptProc(EjsHandle eid, int argc, MprVar **argv)
{
	MprVar		*arg;
	char		*emsg;
	int			i;

	ejsSetReturnValue(eid, mprCreateUndefinedVar());

	for (i = 0; i < argc; i++) {
		arg = argv[i];
		if (arg->type != MPR_TYPE_STRING) {
			continue;
		}
		if (ejsEvalScript(eid, arg->string, 0, &emsg) < 0) {
			ejsSetErrorMsg(eid, "%s", emsg);
			mprFree(emsg);
			return -1;
		}
	}
	/*
	 *	Return with the value of the last expression
 	 */
	return 0;
}

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
/*
 *	Define the standard properties and functions inherited by all script engines.
 */

int ejsDefineStandardProperties(MprVar *obj)
{
#if BLD_FEATURE_FLOATING_POINT
	double	d = 0.0;

	/*	FUTURE - this generates warnings on some systems. This is okay. */

	mprCreatePropertyValue(obj, "NaN", mprCreateFloatVar(0.0 / d));
	d = MAX_FLOAT;
	mprCreatePropertyValue(obj, "Infinity", mprCreateFloatVar(d * d));
#endif
	mprCreatePropertyValue(obj, "null", mprCreateNullVar());
	mprCreatePropertyValue(obj, "undefined", mprCreateUndefinedVar());
	mprCreatePropertyValue(obj, "true", mprCreateBoolVar(1));
	mprCreatePropertyValue(obj, "false", mprCreateBoolVar(0));
	mprCreatePropertyValue(obj, "NULL", mprCreatePtrVar(NULL));

#if BLD_FEATURE_LEGACY_API
	/*
 	 *	DEPRECATED: 2.0.
 	 *	So that ESP/ASP can ignore "language=javascript" statements
	 */
	mprCreatePropertyValue(obj, "javascript", mprCreateIntegerVar(0));
#endif

	/*
 	 *	Extension functions
 	 */
	mprCreatePropertyValue(obj, "assert", 
		mprCreateCFunctionVar(assertProc, 0, MPR_VAR_SCRIPT_HANDLE));
	mprCreatePropertyValue(obj, "eval", 
		mprCreateCFunctionVar(evalScriptProc, 0, MPR_VAR_SCRIPT_HANDLE));
	mprCreatePropertyValue(obj, "exit", 
		mprCreateCFunctionVar(exitProc, 0, MPR_VAR_SCRIPT_HANDLE));
	mprCreatePropertyValue(obj, "refCount", 
		mprCreateCFunctionVar(refCountProc, 0, MPR_VAR_SCRIPT_HANDLE));
	mprCreatePropertyValue(obj, "print", 
		mprCreateCFunctionVar(printProc, 0, MPR_VAR_SCRIPT_HANDLE));
	mprCreatePropertyValue(obj, "println", 
		mprCreateCFunctionVar(printlnProc, 0, MPR_VAR_SCRIPT_HANDLE));
	mprCreatePropertyValue(obj, "printVars", 
		mprCreateCFunctionVar(printVarsProc,0, MPR_VAR_SCRIPT_HANDLE));
	mprCreatePropertyValue(obj, "trace", 
		mprCreateStringCFunctionVar(traceProc, 0, MPR_VAR_SCRIPT_HANDLE));

	/*
	 *	Constructors
	 */
	mprCreatePropertyValue(obj, "Array", 
		mprCreateCFunctionVar(arrayConsProc, 0, MPR_VAR_SCRIPT_HANDLE));
	mprCreatePropertyValue(obj, "Boolean",
		mprCreateCFunctionVar(booleanConsProc, 0, MPR_VAR_SCRIPT_HANDLE));
	mprCreatePropertyValue(obj, "Object", 
		mprCreateCFunctionVar(objectConsProc, 0, MPR_VAR_SCRIPT_HANDLE));
	mprCreatePropertyValue(obj, "Number", 
		mprCreateCFunctionVar(numberConsProc, 0, MPR_VAR_SCRIPT_HANDLE));
	mprCreatePropertyValue(obj, "String", 
		mprCreateCFunctionVar(stringConsProc, 0, MPR_VAR_SCRIPT_HANDLE));

	/*	mprCreatePropertyValue(obj, "Date", 
	 *		mprCreateCFunctionVar(dateConsProc, 0, MPR_VAR_SCRIPT_HANDLE));
	 *	mprCreatePropertyValue(obj, "Regexp", 
	 *		mprCreateCFunctionVar(regexpConsProc, 0, MPR_VAR_SCRIPT_HANDLE));
	 */

	/*
	 *	Can we use on var x = "string text";
	 */
	return 0;
}

/******************************************************************************/

#else
void ejsProcsDummy() {}

/******************************************************************************/
#endif /* BLD_FEATURE_EJS */

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim:tw=78
 * vim600: sw=4 ts=4 fdm=marker
 * vim<600: sw=4 ts=4
 */