/*
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/

 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.

 * The Original Code is Rhino code, released
 * May 6, 1999.

 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
 * Rights Reserved.

 * Contributor(s): 

 * Alternatively, the contents of this file may be used under the
 * terms of the GNU Public License (the "GPL"), in which case the
 * provisions of the GPL are applicable instead of those above.
 * If you wish to allow use of your version of this file only
 * under the terms of the GPL and not to allow others to use your
 * version of this file under the NPL, indicate your decision by
 * deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL.  If you do not delete
 * the provisions above, a recipient may use your version of this
 * file under either the NPL or the GPL.
 */
#ifdef REGEXP
/* 
 * This class implements the RegExp constructor native object.
 */
/* Header files */
#include "js-api.h"
#include "js-scriptruntime.h"
#include "js-objectadapter.h"
#include "js-nativeregexpctor.h"

/* Procedures */
JSRegExpImpl *
JSNativeRegExpCtor::getImpl(JSContext *cx)
{
    return (JSRegExpImpl *) JSScriptRuntime::getRegExpProxy(cx);
}
jsVal_t 
JSNativeRegExpCtor::subStringToValue(JSContext *cx, JSSubString *ss)
{
    jsString_t s = ss == NULL ? (jsString_t) "" : ss->toString(cx);
    jsVal_t value;
 
    value = JS_STRING_TO_VAL(JS_NewGCString(cx, s));
    if (ss != NULL) {
        JS_DeleteZoneObj(cx, s);
    }
    return value;
}    
int
JSNativeRegExpCtor::getDollarNumber(jsString_t s)
{
    // assumes s starts with '$'
    int length = strlen(s);

    if (length != 2) {
        return 0;
    }
    jsChar_t c = s[1];

    if (c < '0' || '9' < c) {
        return 0;
    }
    return c - '0';
}

JSScriptable *
JSNativeRegExpCtor::defineClass(JSContext *cx, JSScriptable *scope)
{
    class RegExpCtorAdapter: public JSObjectAdapter {
#define	defineGetter(name, ucname, clazz)				\
	static jsBool_t							\
		name##_getter(jsContext_t context, jsObject_t object,	\
			jsString_t name, jsObject_t start, jsVal_t *value) { \
		JSContext *_cx = (JSContext *) context;			\
		JSScriptableObject *obj = (JSScriptableObject *) object; \
									\
		return clazz::get##ucname(_cx, obj, value);		\
	}
#define	defineSetter(name, ucname, clazz)				\
	static void							\
		name##_setter(jsContext_t context, jsObject_t object,	\
			jsString_t name, jsObject_t start, jsVal_t value) { \
		JSContext *_cx = (JSContext *) context;			\
		JSScriptableObject *obj = (JSScriptableObject *) object; \
									\
		clazz::set##ucname(_cx, obj, value);			\
	}
        public:
            defineGetter(multiline, Multiline, JSNativeRegExpCtor)
            defineGetter(input, Input, JSNativeRegExpCtor)
            defineGetter(lastMatch, LastMatch, JSNativeRegExpCtor)
            defineGetter(lastParen, LastParen, JSNativeRegExpCtor)
            defineGetter(leftContext, LeftContext, JSNativeRegExpCtor)
            defineGetter(rightContext, RightContext, JSNativeRegExpCtor)

            defineSetter(multiline, Multiline, JSNativeRegExpCtor)
            defineSetter(input, Input, JSNativeRegExpCtor)
    } ;

    JSNativeRegExpCtor *ctor = new (cx) JSNativeRegExpCtor();
    jsString_t *names = (jsString_t *) JS_NewGCObj(cx, sizeof (*names) * 1);

    cx->pushLocalVal(JS_OBJECT_TO_VAL(ctor));

    names[0] = JS_NewGCString(cx, "RegExp");
    ctor->names = names;
    ctor->namesLength = 1;

    ctor->setPrototype(cx,
		JSScriptableObject::getClassPrototype(cx, scope, "Function"));
    ctor->setParentScope(cx, scope);

    static jsProperty_t properties[] = {
#define Item(name, clazz, attributes)   {				\
		#name,  attributes, &clazz::name##_getter,		\
		&clazz::name##_setter					\
	}
#define ItemG(name, clazz, attributes)   {				\
		#name,  attributes, &clazz::name##_getter, NULL		\
	}
        Item(multiline, RegExpCtorAdapter, JS_PA_DontEnum),
        Item(input, RegExpCtorAdapter, JS_PA_DontEnum),
        ItemG(lastMatch, RegExpCtorAdapter, JS_PA_DontEnum),
        ItemG(lastParen, RegExpCtorAdapter, JS_PA_DontEnum),
        ItemG(leftContext, RegExpCtorAdapter, JS_PA_DontEnum),
        ItemG(rightContext, RegExpCtorAdapter, JS_PA_DontEnum),
#undef  ItemG
#undef  Item
    } ; 

    ctor->defineProperties(cx, TableSize(properties), properties);

    static jsProperty_t aliases[] = {
#define Item(alias, name, clazz, attributes)	{			\
		alias,  attributes, &clazz::name##_getter,		\
		&clazz::name##_setter					\
	}
#define ItemG(alias, name, clazz, attributes)				\
			{ alias,  attributes, &clazz::name##_getter, NULL }
        Item("$*", multiline, RegExpCtorAdapter, JS_PA_DontEnum),
        Item("$_", input, RegExpCtorAdapter, JS_PA_DontEnum),
        ItemG("$&", lastMatch, RegExpCtorAdapter, JS_PA_DontEnum),
        ItemG("$+", lastParen, RegExpCtorAdapter, JS_PA_DontEnum),
        ItemG("$`", leftContext, RegExpCtorAdapter, JS_PA_DontEnum),
        ItemG("$'", rightContext, RegExpCtorAdapter, JS_PA_DontEnum),
#undef  ItemG
#undef  Item
    } ; 

    ctor->defineProperties(cx, TableSize(aliases), aliases);

    // We know that scope is a Scriptable object since we
    // constrained the type on initStandardObjects.
    JSScriptableObject *global = (JSScriptableObject *) scope;

    global->defineProperty(cx, "RegExp", JS_OBJECT_TO_VAL(ctor),
			JS_PA_DontEnum);

    cx->popLocalVals(1);
    return ctor;
}
jsBool_t
JSNativeRegExpCtor::getMultiline(JSContext *cx, JSScriptableObject *obj,
				jsVal_t *value)
{
    *value = getImpl(cx)->multiline  ? JS_VAL_TRUE : JS_VAL_FALSE;
    return TRUE;
}
void
JSNativeRegExpCtor::setMultiline(JSContext *cx, JSScriptableObject *obj,
				jsVal_t value)
{
    getImpl(cx)->multiline = JSScriptRuntime::toBoolean(cx, value);
}
jsBool_t  
JSNativeRegExpCtor::getInput(JSContext *cx, JSScriptableObject *obj,
			jsVal_t *value)
{
    jsString_t s = getImpl(cx)->input;

    *value = JS_STRING_TO_VAL(s == NULL ? JS_NewGCString(cx, "") : s);
    return TRUE;
}
void
JSNativeRegExpCtor::setInput(JSContext *cx, JSScriptableObject *obj,
			jsVal_t value)
{
    jsString_t s = JSScriptRuntime::toString(cx, value);

    getImpl(cx)->input = JS_NewGCString(cx, s);
    JS_DeleteZoneObj(cx, s);
}
jsBool_t
JSNativeRegExpCtor::getLastMatch(JSContext *cx, JSScriptableObject *obj,
			jsVal_t *value)
{
    JSSubString *ss = getImpl(cx)->lastMatch;

    *value = subStringToValue(cx, ss);
    return TRUE; 
}
jsBool_t
JSNativeRegExpCtor::getLastParen(JSContext *cx, JSScriptableObject *obj,
			jsVal_t *value)
{
    JSSubString *ss = getImpl(cx)->lastParen;

    *value = subStringToValue(cx, ss);
    return TRUE; 
}
jsBool_t
JSNativeRegExpCtor::getLeftContext(JSContext *cx, JSScriptableObject *obj,
				jsVal_t *value)
{
    JSSubString *ss = getImpl(cx)->leftContext;

    *value = subStringToValue(cx, ss);
    return TRUE; 
}
jsBool_t
JSNativeRegExpCtor::getRightContext(JSContext *cx, JSScriptableObject *obj,
				jsVal_t *value)
{
    JSSubString *ss = getImpl(cx)->rightContext;

    *value = subStringToValue(cx, ss);
    return TRUE; 
}

jsString_t
JSNativeRegExpCtor::getClassName(JSContext *cx)
{
    return JSNativeFunction::getClassName(cx);
}
jsBool_t
JSNativeRegExpCtor::has(JSContext *cx, jsString_t id, JSScriptable *start)
{
    if (id != NULL) {
	int length = strlen(id);

	if (length > 1 && id[0] == '$') {
            if (getDollarNumber(id) != 0) {
                return TRUE;
	    }
	}
    }
    return JSNativeFunction::has(cx, id, start);
}
jsBool_t
JSNativeRegExpCtor::get(JSContext *cx, jsString_t id, JSScriptable *start,
			jsVal_t *value)
{
    int length = strlen(id);

    if (length > 1 && id[0] == '$') {
        int dollar = getDollarNumber(id);

        if (dollar != 0) {
            dollar--;
            JSRegExpImpl *res = getImpl(cx);
	    jsString_t s = res->getParenSubString(dollar)->toString(cx);

            *value = JS_STRING_TO_VAL(JS_NewGCString(cx, s));

	    JS_DeleteZoneObj(cx, s);
	    return TRUE;
        }
    }
    return JSNativeFunction::get(cx, id, start, value);
}
jsVal_t
JSNativeRegExpCtor::call(JSContext *cx, JSScriptable *scope,
			JSScriptable *thisObj, int numArgs, jsVal_t *args)
{
    return JS_OBJECT_TO_VAL(construct(cx, parent, numArgs, args));
}

JSScriptable *
JSNativeRegExpCtor::construct(JSContext *cx, JSScriptable *scope,
			int numArgs, jsVal_t *args)
{
    JSNativeRegExp *re = new (cx) JSNativeRegExp(cx);
    cx->pushLocalVal(JS_OBJECT_TO_VAL(re)); // gc safe 20061124

    JSNativeRegExp::compile(cx, re, numArgs, args, this);
    re->setPrototype(cx,
		JSScriptableObject::getClassPrototype(cx, scope, "RegExp"));
    re->setParentScope(cx, getParentScope(cx));

    cx->popLocalVals(1); // gc safe 20061124
    return re;
}
#endif /* REGEXP */

