/*
 * 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.
 */

#ifndef _JS_NODE_P_H_
#define _JS_NODE_P_H_

#include <stdio.h>
#include <assert.h>
#include "js-node.h"
#include "js-hashtable.h"

/* Classes */
class JSNode::Datum: public JSZoneNew {
    public:
	static  const int	int_datum	= 1;
	static  const int	double_datum	= 2;
	static  const int	string_datum	= 3;
	static  const int	node_datum	= 4;
	static  const int	vector_datum	= 5;
	static  const int	variabletable_datum	= 6;
	static  const int	source_datum	= 7;

	int	type;
	int	refCount;

    public:
	Datum() {
	    refCount = 1;
	}
	void	addRef() {
	    refCount += 1;
	}
	void	release(JSContext *cx) {
	    refCount -= 1;
	    if (refCount <= 0) {
		destroy(cx);
	    }
	}
        int	getType() {
	    return type;
	}
	virtual jsString_t
			toString(JSContext *cx) {
	    jsChar_t buf[16];

	    sprintf(buf, "datum = %d", getType());
	    return JS_NewZoneString(cx, buf);
	}
} ;
class JSIntDatum: public JSNode::Datum {
    public:
	int	datum;

    public:
	JSIntDatum(int datum) {
	    type = int_datum;
	    this->datum = datum;
	}
} ;
class JSDoubleDatum: public JSNode::Datum {
    public:
	double	datum;

    public:
	JSDoubleDatum(double datum) {
	    type = double_datum;
	    this->datum = datum;
	}
} ;
class JSStringDatum: public JSNode::Datum {
    public:
	jsString_t	datum;

    public:
	JSStringDatum(JSContext *cx, jsString_t datum) {
	    type = string_datum;
	    this->datum = datum != NULL ? JS_NewZoneString(cx, datum) : NULL;
	}
	void	destroy(JSContext *cx) {
	    if (datum != NULL) JS_DeleteZoneObj(cx, datum);
	    JSZoneNew::destroy(cx);
	}
} ;
class JSNodeDatum: public JSNode::Datum {
    public:
	JSNode		*datum;

    public:
	JSNodeDatum(JSNode *datum) {
	    type = node_datum;
	    this->datum = datum;
	}
	void	destroy(JSContext *cx) {
	    datum->release(cx);
	    JSZoneNew::destroy(cx);
	}
} ;
class JSVectorDatum: public JSNode::Datum {
    public:
	JSNode::Vector	*datum;

    public:
	JSVectorDatum(JSNode::Vector *datum) {
	    type = vector_datum;
	    this->datum = datum;
	}
	void	destroy(JSContext *cx) {
	    datum->destroy(cx);
	    JSZoneNew::destroy(cx);
	}
} ;
class JSVariableTableDatum: public JSNode::Datum {
    public:
	JSVariableTable	*datum;

    public:
	JSVariableTableDatum(JSVariableTable *datum) {
	    type = variabletable_datum;
	    this->datum = datum;
	}
} ;
class JSSourceDatum: public JSNode::Datum {
    public:
	jsString_t	datum;
	int		datumLength;

    public:
	JSSourceDatum(jsString_t datum, int datumLength) {
	    type = source_datum;
	    this->datum = datum;
	    this->datumLength = datumLength;
	}
	void	destroy(JSContext *cx) {
	    if (datum != NULL) JS_DeleteZoneObj(cx, datum);
	    JSZoneNew::destroy(cx);
	}
} ;

typedef JSHashtable<JSNode::Datum *, int, JSContext::ZoneAllocator> _JSProps;
class JSNode::Props: public JSZoneNew, public _JSProps {
     protected:
	JSContext	*cx;

     protected:
        static size_t hashCode(int key) {
            return (size_t) key;
        }
        static int cmpKey(int key1, int key2) {
            return key1 - key2;
        }
        static	void dupEntry(_JSProps::HashtableEntry *d,
			_JSProps::HashtableEntry *s,
			JSContext::ZoneAllocator *allocator) {
	    *d = *s;
	} 
	static void destroyEntry(_JSProps::HashtableEntry *tabEntry,
			JSContext::ZoneAllocator *allocator) {
	    allocator->deallocate(tabEntry);
        }

     public:
	Props(JSContext *cx, int initialCapacity = 10)
		: _JSProps(hashCode, cmpKey, dupEntry, destroyEntry,
			&cx->allocator, initialCapacity) {
	}
	virtual ~Props() {
	}

	void destroy(JSContext *cx) {
	    this->~Props();
	    JSZoneNew::destroy(cx);
	}
       	void  operator delete(void *ptr) {
       		assert(0);
       	}

	Props	*clone(JSContext *cx) {
	    Props *cl = new (cx) Props(*this);

	    if (table != NULL) {
		cl->table = (HashtableEntry **) allocator->allocate(
				sizeof (*cl->table) * tableLength);
		memset(cl->table, 0, sizeof (*cl->table) * tableLength);
		for (int i = tableLength ; i-- > 0 ;) {
		    if (table[i] != NULL) {
			cl->table[i] = table[i]->clone(dupEntry, allocator);
			for (HashtableEntry *e = table[i] ;
			    e != NULL ; e = e->next) {
			    e->value->addRef();
			}
		    }
                }
            }
	    return cl;
	}
} ;

#endif /* _JS_NODE_P_H_ */

