//===-- ThreadSafeSTLMap.h --------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef liblldb_ThreadSafeSTLMap_h_
#define liblldb_ThreadSafeSTLMap_h_

// C Includes
// C++ Includes
#include <map>
#include <mutex>

// Other libraries and framework includes
// Project includes
#include "lldb/lldb-defines.h"

namespace lldb_private {

template <typename _Key, typename _Tp>
class ThreadSafeSTLMap
{
public:
    typedef std::map<_Key,_Tp> collection;
    typedef typename collection::iterator iterator;
    typedef typename collection::const_iterator const_iterator;
    //------------------------------------------------------------------
    // Constructors and Destructors
    //------------------------------------------------------------------
    ThreadSafeSTLMap() : m_collection(), m_mutex() {}

    ~ThreadSafeSTLMap()
    {
    }

    bool
    IsEmpty() const
    {
        std::lock_guard<std::recursive_mutex> guard(m_mutex);
        return m_collection.empty();
    }

    void
    Clear()
    {
        std::lock_guard<std::recursive_mutex> guard(m_mutex);
        return m_collection.clear();
    }

    size_t
    Erase(const _Key &key)
    {
        std::lock_guard<std::recursive_mutex> guard(m_mutex);
        return EraseNoLock(key);
    }

    size_t
    EraseNoLock (const _Key& key)
    {
        return m_collection.erase (key);
    }

    bool
    GetValueForKey(const _Key &key, _Tp &value) const
    {
        std::lock_guard<std::recursive_mutex> guard(m_mutex);
        return GetValueForKeyNoLock(key, value);
    }

    // Call this if you have already manually locked the mutex using the
    // GetMutex() accessor
    bool
    GetValueForKeyNoLock (const _Key& key, _Tp &value) const
    {
        const_iterator pos = m_collection.find(key);
        if (pos != m_collection.end())
        {
            value = pos->second;
            return true;
        }
        return false;
    }

    bool
    GetFirstKeyForValue(const _Tp &value, _Key &key) const
    {
        std::lock_guard<std::recursive_mutex> guard(m_mutex);
        return GetFirstKeyForValueNoLock(value, key);
    }

    bool
    GetFirstKeyForValueNoLock (const _Tp &value, _Key& key) const
    {
        const_iterator pos, end = m_collection.end();
        for (pos = m_collection.begin(); pos != end; ++pos)
        {
            if (pos->second == value)
            {
                key = pos->first;
                return true;
            }
        }
        return false;
    }

    bool
    LowerBound(const _Key &key, _Key &match_key, _Tp &match_value, bool decrement_if_not_equal) const
    {
        std::lock_guard<std::recursive_mutex> guard(m_mutex);
        return LowerBoundNoLock(key, match_key, match_value, decrement_if_not_equal);
    }

    bool
    LowerBoundNoLock (const _Key& key,
                      _Key& match_key,
                      _Tp &match_value,
                      bool decrement_if_not_equal) const
    {
        const_iterator pos = m_collection.lower_bound (key);
        if (pos != m_collection.end())
        {
            match_key = pos->first;
            if (decrement_if_not_equal && key != match_key && pos != m_collection.begin())
            {
                --pos;
                match_key = pos->first;
            }
            match_value = pos->second;
            return true;
        }
        return false;
    }

    iterator
    lower_bound_unsafe (const _Key& key)
    {
        return m_collection.lower_bound (key);
    }

    void
    SetValueForKey(const _Key &key, const _Tp &value)
    {
        std::lock_guard<std::recursive_mutex> guard(m_mutex);
        SetValueForKeyNoLock(key, value);
    }

    // Call this if you have already manually locked the mutex using the
    // GetMutex() accessor
    void
    SetValueForKeyNoLock (const _Key& key, const _Tp &value)
    {
        m_collection[key] = value;
    }

    std::recursive_mutex &
    GetMutex()
    {
        return m_mutex;
    }

private:
    collection m_collection;
    mutable std::recursive_mutex m_mutex;

    //------------------------------------------------------------------
    // For ThreadSafeSTLMap only
    //------------------------------------------------------------------
    DISALLOW_COPY_AND_ASSIGN (ThreadSafeSTLMap);
};


} // namespace lldb_private

#endif  // liblldb_ThreadSafeSTLMap_h_
