/*
 * 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 ~GlobData() {
                }

	  	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);

#ifdef PERSIST
        /* persist methods */
        void    presave( JSContext *cx, JSScriptable *scope, jsVal_t regExp,
                        JSPersist::StringConstants& stringConstants,
                        JSPersist::NumberConstants& numberConstants );
        void    save( JSContext *cx, JSScriptable *scope, jsVal_t regExp,
                        JSRandomWriter* out,
                        JSPersist::StringConstants& stringConstants,
                        JSPersist::NumberConstants& numberConstants );
        void    load( JSContext *cx, JSScriptable *scope, jsVal_t regExp,
                        JSRandomReader* in, jsString_t* strings,
                        double* numbers );
#endif
} ;

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

