/*
 * 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_VECTOR_H_
#define _JS_VECTOR_H_

#include <string.h>
#include <assert.h>
#include "js-enumeration.h"

/* Typedefs */
template <typename _T, typename _Allocator>
class JSVector {
    protected:
        class VectorEnumeration: public JSEnumeration<_T> {
            protected:
                JSVector<_T, _Allocator>
                        *vector;
                int     count;
            public:
                VectorEnumeration(JSVector *v) {
                    vector = v;
                    count = 0;
                }

                jsBool_t
                        hasMoreElements() {
                    return count < vector->size();
                }
                _T      nextElement() {
                    return vector->elementAt(count++);
                }
        } ;

    protected:
        _T      *elementData;
        int     elementDataLength;
        int     elementCount;
        int     capacityIncrement;
        _Allocator
                *allocator;

    protected:
        void    ensureCapacity(int minCapacity) {
            int oldCapacity = elementDataLength;

            if (minCapacity > oldCapacity) {
                _T *oldData = elementData;
                int newCapacity = (capacityIncrement > 0) ?
                        (oldCapacity + capacityIncrement) : (oldCapacity * 2);

                if (newCapacity < minCapacity) {
                    newCapacity = minCapacity;
                }
                elementData = (_T *) allocator->allocate(
                                        sizeof (*elementData) * newCapacity);
                memcpy(elementData, oldData,
                                sizeof (*elementData) * oldCapacity);
                memset(elementData + oldCapacity, 0,
                        sizeof (*elementData) * (newCapacity - oldCapacity));
                elementDataLength = newCapacity;
                allocator->deallocate(oldData);
            }
        }

    public:
        void  operator delete(void *ptr) {
                assert(0);
        }
        JSVector(_Allocator *allocator, int initialCapacity = 20,
                int capacityIncrement = 10) {
            if (initialCapacity > 0) {
                elementData = (_T *) allocator->allocate(
                                sizeof (*elementData) * initialCapacity);
            } else {
                elementData = (_T *) NULL;
            }
            elementDataLength = initialCapacity;
            this->capacityIncrement = capacityIncrement;
            elementCount = 0;
            this->allocator = allocator;
        }
        ~JSVector() {
            if (elementData != NULL) {
                allocator->deallocate(elementData);
            }
        }
        /*
        void  operator delete(void *ptr) {
                assert(0);
        }
        */
        void    copyInto(_T *elements);
        int     size();
        jsBool_t
                isEmpty();
        JSEnumeration<_T>
                elements() {
            return VectorEnumeration(this);
        }
        _T      elementAt(int index);
        void    removeElementAt(int index);
        void    insertElementAt(_T obj, int index);
        void    addElement(_T obj);
#ifdef REGEXP
        void    setSize(int newSize);
        void    setElementAt(_T obj, int index);
#endif
} ;

/* Inlines */
template <typename _T, typename _Allocator>
inline void
JSVector<_T, _Allocator>::copyInto(_T *elements)
{
    for (int i = elementCount; --i >= 0; ) {
        elements[i] = elementData[i];
    }
}
template <typename _T, typename _Allocator>
inline int
JSVector<_T, _Allocator>::size()
{
    return elementCount;
}
template <typename _T, typename _Allocator>
inline jsBool_t
JSVector<_T, _Allocator>::isEmpty()
{
    return elementCount == 0;
}
template <typename _T, typename _Allocator>
inline _T
JSVector<_T, _Allocator>::elementAt(int index)
{
    return elementData[index];
}
template <typename _T, typename _Allocator>
inline void
JSVector<_T, _Allocator>::removeElementAt(int index)
{
    int j = elementCount - index - 1;
    if (j > 0) {
        memcpy(elementData + index, elementData + index + 1,
                sizeof (*elementData) * j);
    }
    elementCount--;
}
template <typename _T, typename _Allocator>
inline void
JSVector<_T, _Allocator>::insertElementAt(_T obj, int index)
{
    if (index <= elementCount) {
        ensureCapacity(elementCount + 1);
        memmove(elementData + index + 1, elementData + index,
                sizeof (*elementData) * (elementCount - index));
        elementData[index] = obj;
        elementCount++;
    }
}
template <typename _T, typename _Allocator>
inline void
JSVector<_T, _Allocator>::addElement(_T obj)
{
    ensureCapacity(elementCount + 1);
    elementData[elementCount++] = obj;
}
#ifdef REGEXP
template <typename _T, typename _Allocator>
inline void
JSVector<_T, _Allocator>::setSize(int newSize)
{
    if (newSize > elementCount) {
        ensureCapacity(newSize);
        memset(elementData + elementCount, 0,
                (newSize - elementCount) * sizeof (*elementData));
    } else {
        memset(elementData + elementCount, 0,
                (elementCount - newSize) * sizeof (*elementData));
    }
    elementCount = newSize;
}
template <typename _T, typename _Allocator>
inline void
JSVector<_T, _Allocator>::setElementAt(_T obj, int index)
{
    if (index < elementCount) {
        elementData[index] = obj;
    }
}
#endif /* REGEXP */

#endif /* _JS_VECTOR_H_ */

