/**
 @file NamedArray.h

 Dojo Game Engine

 Copyright 2005, Morgan McGuire
 All rights reserved.
 */
#ifndef DOJO_NAMEDARRAY_H
#define DOJO_NAMEDARRAY_H

namespace dojo {

/**
 An array where objects are also accessible by a std::string name in constant time.
 */
template<class Type>
class NamedArray {
private:

    Array<Type>                 array;
    
    Table<std::string, Type>    table;

    std::string                 errorName;

public:

    NamedArray(const std::string e) : errorName(e) {}

    /** It is an error to insert an element with the same name as an existing element. */
    void append(const std::string& name, const Type& t) {
        alwaysAssertM(! table.containsKey(name),
            format("A %s named '%s' already exists.",
            errorName.c_str(), name.c_str()));

        array.append(t);
        table.set(name, t);
    }

    /** It is safe to call this while iterating through the array.
        Linear time. */
    void remove(const std::string& name) {
        const Type& t = table[name];
        for (int i = 0; i < array.size(); ++i) {
            if (array[i] == t) {
                array.fastRemove(i);
                break;
            }
        }

        table.remove(name);
    }

    void remove(int i) {
        Table<std::string, Type>::Iterator it    = table.begin();
        Table<std::string, Type>::Iterator end   = table.begin();

        const Type& v = array[i];
        
        while (it != end) {
            if (it.value == v) {
                table.remove(it.key);
                break;
            }
            ++it;
        }

        array.fastRemove(i);
    }

    /** Asserts if the object is not in the array */
    const Type& operator[](const std::string& name) const {
        alwaysAssertM(table.containsKey(name), format("No %s named '%s'.", errorName.c_str(), name.c_str()));
        return table[name];
    }

    Type& operator[](const std::string& name) {
        alwaysAssertM(table.containsKey(name), format("No %s named '%s'.", errorName.c_str(), name.c_str()));
        return table[name];
    }

    bool containsKey(const std::string& name) const {
        return table.containsKey(name);
    }

    bool get(const std::string& name, Type& v) {
        return table.get(name, v);
    }

    void clear() {
        array.clear();
        table.clear();
    }

    int size() const {
        return array.size();
    }

    const Type& operator[](int i) const {
        return array[i];
    }

    Type& operator[](int i) {
        return array[i];
    }

    void deleteAll() {
        array.deleteAll();
        table.clear();
    }
};

} // namespace dojo

#endif
