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


// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Breakpoint/BreakpointLocationCollection.h"
#include "lldb/Core/ModuleList.h"
#include "lldb/Breakpoint/Breakpoint.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadSpec.h"

using namespace lldb;
using namespace lldb_private;

//----------------------------------------------------------------------
// BreakpointLocationCollection constructor
//----------------------------------------------------------------------
BreakpointLocationCollection::BreakpointLocationCollection() :
    m_break_loc_collection(),
    m_collection_mutex()
{
}

//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
BreakpointLocationCollection::~BreakpointLocationCollection()
{
}

void
BreakpointLocationCollection::Add(const BreakpointLocationSP &bp_loc)
{
    std::lock_guard<std::mutex> guard(m_collection_mutex);
    BreakpointLocationSP old_bp_loc = FindByIDPair (bp_loc->GetBreakpoint().GetID(), bp_loc->GetID());
    if (!old_bp_loc.get())
        m_break_loc_collection.push_back(bp_loc);
}

bool
BreakpointLocationCollection::Remove (lldb::break_id_t bp_id, lldb::break_id_t bp_loc_id)
{
    std::lock_guard<std::mutex> guard(m_collection_mutex);
    collection::iterator pos = GetIDPairIterator(bp_id, bp_loc_id);    // Predicate
    if (pos != m_break_loc_collection.end())
    {
        m_break_loc_collection.erase(pos);
        return true;
    }
    return false;

}

class BreakpointIDPairMatches
{
public:
    BreakpointIDPairMatches (lldb::break_id_t break_id, lldb::break_id_t break_loc_id) :
        m_break_id(break_id),
        m_break_loc_id (break_loc_id)
    {
    }

    bool operator() (const BreakpointLocationSP &bp_loc) const
    {
        return m_break_id == bp_loc->GetBreakpoint().GetID()
            && m_break_loc_id == bp_loc->GetID();
    }

private:
   const lldb::break_id_t m_break_id;
   const lldb::break_id_t m_break_loc_id;
};

BreakpointLocationCollection::collection::iterator
BreakpointLocationCollection::GetIDPairIterator (lldb::break_id_t break_id, lldb::break_id_t break_loc_id)
{
    return std::find_if(m_break_loc_collection.begin(), m_break_loc_collection.end(),   // Search full range
                        BreakpointIDPairMatches(break_id, break_loc_id));               // Predicate
}

BreakpointLocationCollection::collection::const_iterator
BreakpointLocationCollection::GetIDPairConstIterator (lldb::break_id_t break_id, lldb::break_id_t break_loc_id) const
{
    return std::find_if(m_break_loc_collection.begin(), m_break_loc_collection.end(),   // Search full range
                        BreakpointIDPairMatches(break_id, break_loc_id));               // Predicate
}

BreakpointLocationSP
BreakpointLocationCollection::FindByIDPair (lldb::break_id_t break_id, lldb::break_id_t break_loc_id)
{
    BreakpointLocationSP stop_sp;
    collection::iterator pos = GetIDPairIterator(break_id, break_loc_id);
    if (pos != m_break_loc_collection.end())
        stop_sp = *pos;

    return stop_sp;
}

const BreakpointLocationSP
BreakpointLocationCollection::FindByIDPair (lldb::break_id_t break_id, lldb::break_id_t break_loc_id) const
{
    BreakpointLocationSP stop_sp;
    collection::const_iterator pos = GetIDPairConstIterator(break_id, break_loc_id);
    if (pos != m_break_loc_collection.end())
        stop_sp = *pos;

    return stop_sp;
}

BreakpointLocationSP
BreakpointLocationCollection::GetByIndex (size_t i)
{
    std::lock_guard<std::mutex> guard(m_collection_mutex);
    BreakpointLocationSP stop_sp;
    if (i < m_break_loc_collection.size())
        stop_sp = m_break_loc_collection[i];

    return stop_sp;
}

const BreakpointLocationSP
BreakpointLocationCollection::GetByIndex (size_t i) const
{
    std::lock_guard<std::mutex> guard(m_collection_mutex);
    BreakpointLocationSP stop_sp;
    if (i < m_break_loc_collection.size())
        stop_sp = m_break_loc_collection[i];

    return stop_sp;
}

bool
BreakpointLocationCollection::ShouldStop (StoppointCallbackContext *context)
{
    bool shouldStop = false;
    size_t i = 0;
    size_t prev_size = GetSize();
    while (i < prev_size)
    {
        // ShouldStop can remove the breakpoint from the list
        if (GetByIndex(i)->ShouldStop(context))
            shouldStop = true;

        if (prev_size == GetSize())
            i++;
        prev_size = GetSize();
    }
    return shouldStop;
}

bool 
BreakpointLocationCollection::ValidForThisThread (Thread *thread)
{
    std::lock_guard<std::mutex> guard(m_collection_mutex);
    collection::iterator pos,
        begin = m_break_loc_collection.begin(),
        end = m_break_loc_collection.end();

    for (pos = begin; pos != end; ++pos)
    {
        if ((*pos)->ValidForThisThread (thread))
            return true;
    }
    return false;
}

bool 
BreakpointLocationCollection::IsInternal () const
{
    std::lock_guard<std::mutex> guard(m_collection_mutex);
    collection::const_iterator pos,
        begin = m_break_loc_collection.begin(),
        end = m_break_loc_collection.end();

    bool is_internal = true;
    
    for (pos = begin; pos != end; ++pos)
    {
        if (!(*pos)->GetBreakpoint().IsInternal ())
        {
            is_internal = false;
            break;
        }
    }
    return is_internal;
}

void
BreakpointLocationCollection::GetDescription (Stream *s, lldb::DescriptionLevel level)
{
    std::lock_guard<std::mutex> guard(m_collection_mutex);
    collection::iterator pos,
        begin = m_break_loc_collection.begin(),
        end = m_break_loc_collection.end();

    for (pos = begin; pos != end; ++pos)
    {
        if (pos != begin)
            s->PutChar(' ');
        (*pos)->GetDescription(s, level);
    }
}

