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

