/*
 *  This file is part of the KDE libraries
 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
 *  Copyright (C) 2003 Apple Computer, Inc.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 *
 */

#ifndef KJS_LIST_H
#define KJS_LIST_H

#include "value.h"

namespace KJS {

    struct ListImpBase {
        int size;
        int refCount;
	int valueRefCount;
    };
    
    class ListIterator;

    /**
     * @short Native list type.
     *
     * List is a native ECMAScript type. List values are only used for
     * intermediate results of expression evaluation and cannot be stored
     * as properties of objects.
     *
     * The list is explicitly shared. Note that while copyTail() returns a
     * copy of the list the referenced objects are still shared.
     */
    class KJS_EXPORT List {
    public:
        List();
	List(bool needsMarking);
        ~List() { deref(); }

        List(const List &b) : _impBase(b._impBase), _needsMarking(false) {
	    ++_impBase->refCount; 
	    if (!_impBase->valueRefCount) refValues(); 
	    ++_impBase->valueRefCount; 
	}
        List &operator=(const List &);

        /**
         * Append an object to the end of the list.
         *
         * @param val Pointer to object.
         */
        void append(const Value& val) { append(val.imp()); }
        void append(ValueImp *val);
        /**
         * Remove all elements from the list.
         */
        void clear();

        /**
         * Make a copy of the list
         */
        List copy() const;

        /**
         * Make a copy of the list, omitting the first element.
         */
        List copyTail() const;
    
        /**
         * @return true if the list is empty. false otherwise.
         */
        bool isEmpty() const { return _impBase->size == 0; }
        /**
         * @return the current size of the list.
         */
        int size() const { return _impBase->size; }
        /**
         * @return A KJS::ListIterator pointing to the first element.
         */
        ListIterator begin() const;
        /**
         * @return A KJS::ListIterator pointing to the last element.
         */
        ListIterator end() const;
        
        /**
         * Retrieve an element at an indexed position. If you want to iterate
         * trough the whole list using KJS::ListIterator will be faster.
         *
         * @param i List index.
         * @return Return the element at position i. KJS::Undefined if the
         * index is out of range.
         */
        Value at(int i) const { return Value(impAt(i)); }
        /**
         * Equivalent to at.
         */
        Value operator[](int i) const { return Value(impAt(i)); }
        
        ValueImp *impAt(int i) const;
    
        /**
         * Returns a pointer to a static instance of an empty list. Useful if a
         * function has a KJS::List parameter.
         */
        static const List &empty();
        
	void mark() { if (_impBase->valueRefCount == 0) markValues(); }
    private:
        ListImpBase *_impBase;
	bool _needsMarking;
        
        void deref() { if (!_needsMarking && --_impBase->valueRefCount == 0) derefValues(); if (--_impBase->refCount == 0) release(); }

        void release();
        void refValues();
        void derefValues();
        void markValues();
    };
  
    /**
     * @short Iterator for KJS::List objects.
     */
    class ListIterator {
    public:
        /**
         * Construct an iterator that points to the first element of the list.
         * @param l The list the iterator will operate on.
         */
        ListIterator(const List &l) : _list(&l), _i(0) { }
        ListIterator(const List &l, int index) : _list(&l), _i(index) { }
        /**
         * Dereference the iterator.
         * @return A pointer to the element the iterator operates on.
         */
        ValueImp *operator->() const { return _list->impAt(_i); }
        Value operator*() const { return Value(_list->impAt(_i)); }
        /**
         * Prefix increment operator.
         * @return The element after the increment.
         */
        Value operator++() { return Value(_list->impAt(++_i)); }
        /**
         * Postfix increment operator.
         */
        Value operator++(int) { return Value(_list->impAt(_i++)); }
        /**
         * Prefix decrement operator.
         */
        Value operator--() { return Value(_list->impAt(--_i)); }
        /**
         * Postfix decrement operator.
         */
        Value operator--(int) { return Value(_list->impAt(_i--)); }
        /**
         * Compare the iterator with another one.
         * @return True if the two iterators operate on the same list element.
         * False otherwise.
         */
        bool operator==(const ListIterator &it) const { return _i == it._i; }
        /**
         * Check for inequality with another iterator.
         * @return True if the two iterators operate on different list elements.
         */
        bool operator!=(const ListIterator &it) const { return _i != it._i; }

    private:
        const List *_list;
        int _i;
    };

    inline ListIterator List::begin() const { return ListIterator(*this); }
    inline ListIterator List::end() const { return ListIterator(*this, size()); }
 
    inline List &List::operator=(const List &b)
    {
        ListImpBase *bImpBase = b._impBase;
        ++bImpBase->refCount;
        deref();
        _impBase = bImpBase;
	if (!_needsMarking) {
	    if (!_impBase->valueRefCount) {
		refValues();
	    }
	    _impBase->valueRefCount++;
	}

        return *this;
    }

 } // namespace KJS

#endif // KJS_LIST_H
