/* Copyright (c) 2012 Massachusetts Institute of Technology
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef __MAP_H__
#define __MAP_H__

#include <iostream>
#include <map>

#include "String.h"
#include "Assert.h"

namespace LibUtil
{
    using std::map;

    template<class T> class Map
    {
        public:
            typedef typename map<String, T>::iterator       Iterator;
            typedef typename map<String, T>::const_iterator ConstIterator;
            typedef typename map<String, T>::size_type      SizeType;

        public:
            Map();
            virtual ~Map();

        public:
            // Return a new copy of this Map instance
            Map* clone() const;
            // Copy map_ to this instance
            void copyFrom(const Map<T>* map_);
            // Return the size of the map
            SizeType size() const;
            // Check if the map is empty
            bool isEmpty() const;
            // Check if the key exists
            bool keyExist(const String& key_) const;
            // Get the value_ corresponding to the key_
            const T& get(const String& key_) const;
            // Get the value_ corresponding to the key_ if the key_ exist, otherwise, the default_value_is returned
            const T& getIfKeyExist(const String& key_, const T& default_value_ = T()) const;
            // Add/Update a <key_, value_> entry
            void set(const String& key_, const T& value_);
            // Get iterator to the element
            Iterator find(const String& key_);
            ConstIterator find(const String& key_) const;
            // Remove an entry corresponding to key_
            void remove(const String& key_);
            // Remove an entry at 'it' 
            void remove(Iterator it);
            // Remove all keys
            void clear();
            // Merge a map. Values with same key will be overwritten.
            void merge(const Map<T>* map_);
            // Returns a MapIterator referring to the first element in the map
            Iterator begin();
            ConstIterator begin() const;
            // Returns a MapIterator referring to the past-the-end element in the map
            Iterator end();
            ConstIterator end() const;

        protected:
            Map(const Map& map_);

        protected:
            map<String, T> mMap;
    };

    template<class T> Map<T>::Map()
    {}

    template<class T> Map<T>::~Map()
    {}

    template<class T> Map<T>* Map<T>::clone() const
    {
        return new Map<T>(*this);
    }

    template<class T> void Map<T>::copyFrom(const Map<T>* map_)
    {
        // Remove all keys (it won't free the content if T is a pointer)
        mMap.clear();

        // Copy the contents
        mMap = map_->mMap;
    }

    template<class T> typename Map<T>::SizeType Map<T>::size() const
    {
        return mMap.size();
    }

    template<class T> bool Map<T>::isEmpty() const
    {
        return (mMap.empty());
    }

    template<class T> bool Map<T>::keyExist(const String& key_) const
    {
        ConstIterator it = mMap.find(key_);
        return (it != mMap.end());
    }

    template<class T> const T& Map<T>::get(const String& key_) const
    {
        ConstIterator it;

        it = mMap.find(key_);
        ASSERT((it != mMap.end()), "Key not found: " + key_);
        return (it->second);
    }

    template<class T> const T& Map<T>::getIfKeyExist(const String& key_, const T& default_value_) const
    {
        if(keyExist(key_))
        {
            return get(key_);
        }
        else
        {
            return default_value_;
        }
    }

    template<class T> void Map<T>::set(const String& key_, const T& value_)
    {
        mMap[key_] = value_;
        return;
    }

    template<class T> typename Map<T>::Iterator Map<T>::find(const String& key_)
    {
        return mMap.find(key_);
    }

    template<class T> typename Map<T>::ConstIterator Map<T>::find(const String& key_) const
    {
        return mMap.find(key_);
    }

    template<class T> void Map<T>::remove(const String& key_)
    {
        mMap.erase(key_);
        return;
    }

    template<class T> void Map<T>::remove(Iterator it)
    {
        mMap.erase(it);
        return;
    }

    template<class T> void Map<T>::clear()
    {
        mMap.clear();
        return;
    }

    template<class T> void Map<T>::merge(const Map<T>* map_)
    {
        ConstIterator it;
        for(it = map_->begin(); it != map_->end(); it++)
        {
            const String& key = it->first;
            const T& value = it->second;
            set(key, value);
        }
        return;
    }

    template<class T> typename Map<T>::Iterator Map<T>::begin()
    {
        return mMap.begin();
    }

    template<class T> typename Map<T>::ConstIterator Map<T>::begin() const
    {
        return mMap.begin();
    }

    template<class T> typename Map<T>::Iterator Map<T>::end()
    {
        return mMap.end();
    }

    template<class T> typename Map<T>::ConstIterator Map<T>::end() const
    {
        return mMap.end();
    }

    inline std::ostream& operator<<(std::ostream& ost_, const Map<String>& map_)
    {
        Map<String>::ConstIterator it;
        for(it = map_.begin(); it != map_.end(); it++)
        {
            ost_ << it->first << " = " << it->second << std::endl;
        }
        return ost_;
    }

    template<class T> Map<T>::Map(const Map<T>& map_)
        : mMap(map_.mMap)
    {}

    typedef Map<String> StringMap;


    // Handy function to delete all pointers in a map
    template<class T> void clearPtrMap(Map<T*>* map_)
    {
        for(typename Map<T*>::Iterator it = map_->begin(); it != map_->end(); ++it)
        {
            T* temp_T = it->second;
            delete temp_T;
        }
        map_->clear();
        return;
    }

    // Handy function to delete all pointers in a map and the map itself
    template<class T> void deletePtrMap(Map<T*>* map_)
    {
        clearPtrMap<T>(map_);
        delete map_;
        return;
    }

    // Handy function to clone all pointers in a map
    template<class T> Map<T*>* clonePtrMap(const Map<T*>* map_)
    {
        Map<T*>* new_T_map = new Map<T*>;
        for(typename Map<T*>::ConstIterator it = map_->begin(); it != map_->end(); ++it)
        {
            const String& temp_name = it->first;
            const T* temp_T = it->second;
            new_T_map->set(temp_name, temp_T->clone());
        }
        return new_T_map;
    }
}

#endif // __MAP_H__