/*
    Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann@kde.org>
                  2004, 2005 Rob Buis <buis@kde.org>

    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 SVGList_h
#define SVGList_h

#if ENABLE(SVG)
#include "ExceptionCode.h"
#include "SVGListTraits.h"

#include <wtf/RefCounted.h>
#include <wtf/PassRefPtr.h>
#include <wtf/Vector.h>

namespace WebCore {

    class QualifiedName;

    template<typename Item>
    struct SVGListTypeOperations {
        static Item nullItem()
        {
            return SVGListTraits<UsesDefaultInitializer<Item>::value, Item>::nullItem();
        }
        static bool isNull(const Item& it)
        {
            return SVGListTraits<UsesDefaultInitializer<Item>::value, Item>::isNull(it);
        }
    };

    template<typename Item>
    class SVGList : public RefCounted<SVGList<Item> > {
    private:
        typedef SVGListTypeOperations<Item> TypeOperations;

    public:
        virtual ~SVGList() { }

        const QualifiedName& associatedAttributeName() const { return m_associatedAttributeName; }

        unsigned int numberOfItems() const { return m_vector.size(); }
        void clear(ExceptionCode &) { m_vector.clear(); }

        Item initialize(Item newItem, ExceptionCode& ec)
        {
            if (TypeOperations::isNull(newItem)) {
                ec = TYPE_MISMATCH_ERR;
                return TypeOperations::nullItem();
            }
            clear(ec);
            return appendItem(newItem, ec);
        }

        Item getFirst() const
        {
            ExceptionCode ec = 0;
            return getItem(0, ec);
        }

        Item getLast() const
        {
            ExceptionCode ec = 0;
            return getItem(m_vector.size() - 1, ec);
        }

        Item getItem(unsigned int index, ExceptionCode& ec)
        {
            if (index >= m_vector.size()) {
                ec = INDEX_SIZE_ERR;
                return TypeOperations::nullItem();
            }

            return m_vector.at(index);
        }

        const Item getItem(unsigned int index, ExceptionCode& ec) const
        {
            if (index >= m_vector.size()) {
                ec = INDEX_SIZE_ERR;
                return TypeOperations::nullItem();
            }

            return m_vector[index];
        }

        Item insertItemBefore(Item newItem, unsigned int index, ExceptionCode& ec)
        {
            if (TypeOperations::isNull(newItem)) {
                ec = TYPE_MISMATCH_ERR;
                return TypeOperations::nullItem();
            }

            if (index < m_vector.size()) {
                m_vector.insert(index, newItem);
            } else {
                m_vector.append(newItem);
            }
            return newItem;
        }

        Item replaceItem(Item newItem, unsigned int index, ExceptionCode& ec)
        {
            if (index >= m_vector.size()) {
                ec = INDEX_SIZE_ERR;
                return TypeOperations::nullItem();
            }
    
            if (TypeOperations::isNull(newItem)) {
                ec = TYPE_MISMATCH_ERR;
                return TypeOperations::nullItem();
            }

            m_vector[index] = newItem;
            return newItem;
        }

        Item removeItem(unsigned int index, ExceptionCode& ec)
        {
            if (index >= m_vector.size()) {
                ec = INDEX_SIZE_ERR;
                return TypeOperations::nullItem();
            }

            Item item = m_vector[index];
            m_vector.remove(index);
            return item;
        }

        Item appendItem(Item newItem, ExceptionCode& ec)
        {
            if (TypeOperations::isNull(newItem)) {
                ec = TYPE_MISMATCH_ERR;
                return TypeOperations::nullItem();
            }

            m_vector.append(newItem);
            return newItem;
        }

    protected:
        SVGList(const QualifiedName& attributeName) 
            : m_associatedAttributeName(attributeName)
        {
        }

    private:
        Vector<Item> m_vector;
        const QualifiedName& m_associatedAttributeName;
    };

    template<typename Item>
    class SVGPODListItem : public RefCounted<SVGPODListItem<Item> > {
    public:
        static PassRefPtr<SVGPODListItem> create() { return adoptRef(new SVGPODListItem); }
        static PassRefPtr<SVGPODListItem> copy(const Item& item) { return adoptRef(new SVGPODListItem(item)); }

        operator Item&() { return m_item; }
        operator const Item&() const { return m_item; }

        // Updating facilities, used by JSSVGPODTypeWrapperCreatorForList
        Item value() const { return m_item; }
        void setValue(const Item& newItem) { m_item = newItem; }

    private:
        SVGPODListItem() : m_item() { }
        SVGPODListItem(const Item& item) : RefCounted<SVGPODListItem<Item> >(), m_item(item) { }
        
        Item m_item;
    };

    template<typename Item>
    class SVGPODList : public SVGList<RefPtr<SVGPODListItem<Item> > > {
    public:
        Item initialize(Item newItem, ExceptionCode& ec)
        {
            SVGPODListItem<Item>* ptr(SVGList<RefPtr<SVGPODListItem<Item> > >::initialize(SVGPODListItem<Item>::copy(newItem), ec).get());
            if (!ptr)
                return Item();

            return static_cast<const Item&>(*ptr); 
        }

        Item getFirst() const
        {
            SVGPODListItem<Item>* ptr(SVGList<RefPtr<SVGPODListItem<Item> > >::getFirst().get());
            if (!ptr)
                return Item();

            return static_cast<const Item&>(*ptr);
        }

        Item getLast() const
        {
            SVGPODListItem<Item>* ptr(SVGList<RefPtr<SVGPODListItem<Item> > >::getLast().get());
            if (!ptr)
                return Item();

            return static_cast<const Item&>(*ptr); 
        }

        Item getItem(unsigned int index, ExceptionCode& ec)
        {
            SVGPODListItem<Item>* ptr(SVGList<RefPtr<SVGPODListItem<Item> > >::getItem(index, ec).get());
            if (!ptr)
                return Item();

            return static_cast<const Item&>(*ptr);
        }

        const Item getItem(unsigned int index, ExceptionCode& ec) const
        {
            SVGPODListItem<Item>* ptr(SVGList<RefPtr<SVGPODListItem<Item> > >::getItem(index, ec).get());
             if (!ptr)
                return Item();

            return static_cast<const Item&>(*ptr);
        }

        Item insertItemBefore(Item newItem, unsigned int index, ExceptionCode& ec)
        {
            SVGPODListItem<Item>* ptr(SVGList<RefPtr<SVGPODListItem<Item> > >::insertItemBefore(SVGPODListItem<Item>::copy(newItem), index, ec).get());
            if (!ptr)
                return Item();

            return static_cast<const Item&>(*ptr); 
        }

        Item replaceItem(Item newItem, unsigned int index, ExceptionCode& ec)
        {
            SVGPODListItem<Item>* ptr(SVGList<RefPtr<SVGPODListItem<Item> > >::replaceItem(SVGPODListItem<Item>::copy(newItem), index, ec).get());
            if (!ptr)
                return Item();

            return static_cast<const Item&>(*ptr);
        }

        Item removeItem(unsigned int index, ExceptionCode& ec)
        {
            SVGPODListItem<Item>* ptr(SVGList<RefPtr<SVGPODListItem<Item> > >::removeItem(index, ec).get());
            if (!ptr)
                return Item();

            return static_cast<const Item&>(*ptr); 
        }

        Item appendItem(Item newItem, ExceptionCode& ec)
        {
            SVGPODListItem<Item>* ptr(SVGList<RefPtr<SVGPODListItem<Item> > >::appendItem(SVGPODListItem<Item>::copy(newItem), ec).get());
            if (!ptr)
                return Item();

            return static_cast<const Item&>(*ptr); 
        }
        
    protected:
        SVGPODList(const QualifiedName& attributeName) 
            : SVGList<RefPtr<SVGPODListItem<Item> > >(attributeName) { }
    };

} // namespace WebCore

#endif // ENABLE(SVG)
#endif // SVGList_h
