/*
 * 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
#ifndef _JS_REG_EXP_IMPL_H_
#define _JS_REG_EXP_IMPL_H_

#include "js-types.h"
#include "js-gcnew.h"
#include "js-nativeregexp.h"
#include "js-substring.h"
#include "js-vector.h"
#include "js-runtime.h"
#include "js-regexpproxy.h"

/* Classes */
class JSRegExpImpl: public JSGCNew, public JSRegExpProxy { /* GC collectable */
    friend class JSNativeRegExp;
    friend class JSNativeRegExpCtor;

    protected:
        class GlobData {
            public:
                static  const int GLOB_MATCH =      1;
                static  const int GLOB_REPLACE =    2;
                static  const int GLOB_SEARCH =     3;

                int8_t  mode;   /* input: return index, match object,
                                  or void */
                int     optarg; /* input: index of optional flags argument */
                jsBool_t
                        global; /* output: whether regexp was global */
                jsString_t
                        str;    /* output: 'this' parameter object as string */
                JSNativeRegExp
                        *regexp; /* output: regexp parameter object
                                  private data */
                JSScriptable
                        *parent;

            public:
                virtual void doGlobal(JSContext *cx, JSFunction *funObj,
                                int count) = 0;
        } ;
        class MatchData: public  GlobData {
            public:
                JSScriptable    *arrayobj;

            public:
                MatchData();

                void    doGlobal(JSContext *cx, JSFunction *funObj, int count);
        } ;
        class ReplaceData: public GlobData {
            friend class JSRegExpImpl;
            public:
                JSFunction *
                        lambda; /* replacement function object or null */
                jsString_t
                        repstr; /* replacement string */
                int     dollar; /* -1 or index of first $ in repstr */
                jsString_t
                        charArray; /* result characters, null initially */
                int     length; /* result length, 0 initially */
                int     index;  /* index in result of next replacement */
                int     leftIndex; /* leftContext index, always 0 for JS1.2 */

            protected:
                static  JSSubString *
                        interpretDollar(JSContext *cx, JSRegExpImpl *res,
                                jsString_t da, int dp, int bp, int *skip);

                int     findReplen(JSContext *cx, JSRegExpImpl *reImpl);
                void    doReplace(JSContext *cx, JSRegExpImpl *regExpImpl,
                                jsString_t charArray, int arrayIndex);

            public:
                ReplaceData();

                void    doGlobal(JSContext *cx, JSFunction *funObj, int count);

        } ;
        friend class ReplaceData;
        friend class MatchData;

        typedef JSVector<JSSubString *, JSRuntime::GCAllocator> _REImplVector;
        class REImplVector: public _REImplVector {
            public:
                REImplVector(JSContext *cx, int initialCapacity = 20,
                        int capacityIncrement = 10)
                        : _REImplVector(&cx->runtime->allocator,
                                initialCapacity, capacityIncrement) {
                }
                void    mark(JSContext *cx) {
                    JSGCHeap *gcHeap = &cx->runtime->gcHeap;

                    gcHeap->markObj(elementData);
                    for (int i = 0; i < elementCount; ++i) {
                        if (elementData[i] != NULL) {
                            elementData[i]->mark(cx);
                        }
                    }
                }
        } ;

    protected:
        jsBool_t        multiline;      /* whether input contains newlines
                                          (perl $*) */
        jsString_t      input;          /* input string to match (perl $_, GC
                                          root) */
        REImplVector    parens;         /* Vector of SubString; last set of
                                          parens */
        JSSubString     *lastMatch;     /* last string matched (perl $&) */
        JSSubString     *lastParen;     /* last paren matched (perl $+) */
        JSSubString     *leftContext;   /* input to left of last match
                                          (perl $`) */
        JSSubString     *rightContext;  /* input to right of last match
                                          (perl $') */

        JSSubString     *dollarStr;
        JSSubString     *emptySubString;

        MatchData       *mdata;

    protected:
        static  jsVal_t matchOrReplace(JSContext *cx, JSScriptable *thisObj,
                                int numArgs, jsVal_t *args, JSFunction *funObj,
                                GlobData *data, jsBool_t forceFlat);
        JSSubString *
                getParenSubString(int i);

    public:
        static  JSRegExpImpl *
                        getRegExpImpl(JSContext *cx);

        JSRegExpImpl(JSContext *cx);
        virtual ~JSRegExpImpl() {
        }

        /* operators */
        void *operator new(size_t size, void *placement);

        /* RegExpProxy methods */
        jsBool_t
                isRegExp(jsVal_t obj);
        jsVal_t newRegExp(JSContext *cx, JSScriptable *scope, jsString_t source,
                        jsString_t global, jsBool_t flat);
        jsVal_t executeRegExp(JSContext *cx, jsVal_t regExp,
                        JSScriptable *scopeObj, jsString_t str, int *indexp,
                        jsBool_t test);
        jsVal_t match(JSContext *cx, JSScriptable *thisObj, int numArgs,
                        jsVal_t *args, JSFunction *funObj);
        jsVal_t search(JSContext *cx, JSScriptable *thisObj, int numArgs,
                        jsVal_t *args, JSFunction *funObj);
        jsVal_t replace(JSContext *cx, JSScriptable *thisObj, int numArgs,
                        jsVal_t *args, JSFunction *funObj);
        int     find_split(JSContext *cx, JSFunction *funObj, jsString_t target,
                        jsString_t separator, jsVal_t reObj, int *ip,
                        int *matchlen, jsBool_t *matched,
                        jsString_t **parensp, int *parensLegth);

        /* gc methods */
        void    mark(JSContext *cx);
} ;

#endif /* _JS_REG_EXP_IMPL_H_ */
#endif /* REGEXP */

