/*
 * 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) {
	memmove(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_ */

