//===-- ThreadList.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_ThreadList_h_
#define liblldb_ThreadList_h_

#include <mutex>
#include <vector>

#include "lldb/lldb-private.h"
#include "lldb/Core/UserID.h"
#include "lldb/Utility/Iterable.h"
#include "lldb/Target/ThreadCollection.h"
#include "lldb/Target/Thread.h"

namespace lldb_private {

// This is a thread list with lots of functionality for use only by the process
// for which this is the thread list.  A generic container class with iterator
// functionality is ThreadCollection.
class ThreadList : public ThreadCollection
{
friend class Process;

public:

    ThreadList (Process *process);

    ThreadList (const ThreadList &rhs);

    ~ThreadList() override;

    const ThreadList&
    operator = (const ThreadList& rhs);

    uint32_t
    GetSize(bool can_update = true);

    // Return the selected thread if there is one.  Otherwise, return the thread
    // selected at index 0.
    lldb::ThreadSP
    GetSelectedThread ();
    
    // Manage the thread to use for running expressions.  This is usually the Selected thread,
    // but sometimes (e.g. when evaluating breakpoint conditions & stop hooks) it isn't.
    class ExpressionExecutionThreadPusher
    {
        public:
        ExpressionExecutionThreadPusher(ThreadList &thread_list, lldb::tid_t tid) :
            m_thread_list(&thread_list),
            m_tid(tid)
        {
            m_thread_list->PushExpressionExecutionThread(m_tid);
        }
        
        ExpressionExecutionThreadPusher(lldb::ThreadSP thread_sp);
        
        ~ExpressionExecutionThreadPusher()
        {
            if (m_thread_list && m_tid != LLDB_INVALID_THREAD_ID)
                m_thread_list->PopExpressionExecutionThread(m_tid);
        }
        
        private:
            ThreadList *m_thread_list;
            lldb::tid_t m_tid;
    };

    lldb::ThreadSP
    GetExpressionExecutionThread();
    
protected:
    void
    PushExpressionExecutionThread(lldb::tid_t tid);
    
    void
    PopExpressionExecutionThread(lldb::tid_t tid);

public:
    bool
    SetSelectedThreadByID (lldb::tid_t tid, bool notify = false);

    bool
    SetSelectedThreadByIndexID (uint32_t index_id, bool notify = false);

    void
    Clear();

    void
    Flush();

    void
    Destroy();

    // Note that "idx" is not the same as the "thread_index". It is a zero
    // based index to accessing the current threads, whereas "thread_index"
    // is a unique index assigned
    lldb::ThreadSP
    GetThreadAtIndex (uint32_t idx, bool can_update = true);

    lldb::ThreadSP
    FindThreadByID (lldb::tid_t tid, bool can_update = true);
    
    lldb::ThreadSP
    FindThreadByProtocolID (lldb::tid_t tid, bool can_update = true);

    lldb::ThreadSP
    RemoveThreadByID (lldb::tid_t tid, bool can_update = true);
    
    lldb::ThreadSP
    RemoveThreadByProtocolID (lldb::tid_t tid, bool can_update = true);

    lldb::ThreadSP
    FindThreadByIndexID (uint32_t index_id, bool can_update = true);

    lldb::ThreadSP
    GetThreadSPForThreadPtr (Thread *thread_ptr);

    bool
    ShouldStop (Event *event_ptr);

    Vote
    ShouldReportStop (Event *event_ptr);

    Vote
    ShouldReportRun (Event *event_ptr);

    void
    RefreshStateAfterStop ();

    //------------------------------------------------------------------
    /// The thread list asks tells all the threads it is about to resume.
    /// If a thread can "resume" without having to resume the target, it
    /// will return false for WillResume, and then the process will not be
    /// restarted.
    ///
    /// @return
    ///    \b true instructs the process to resume normally,
    ///    \b false means start & stopped events will be generated, but
    ///    the process will not actually run.  The thread must then return
    ///    the correct StopInfo when asked.
    ///
    //------------------------------------------------------------------
    bool
    WillResume ();

    void
    DidResume ();

    void
    DidStop ();

    void
    DiscardThreadPlans();

    uint32_t
    GetStopID () const;

    void
    SetStopID (uint32_t stop_id);

    std::recursive_mutex &
    GetMutex() override;

    void
    Update (ThreadList &rhs);
    
protected:

    void
    SetShouldReportStop (Vote vote);

    void
    NotifySelectedThreadChanged (lldb::tid_t tid);

    //------------------------------------------------------------------
    // Classes that inherit from Process can see and modify these
    //------------------------------------------------------------------
    Process *m_process; ///< The process that manages this thread list.
    uint32_t m_stop_id; ///< The process stop ID that this thread list is valid for.
    lldb::tid_t m_selected_tid;  ///< For targets that need the notion of a current thread.
    std::vector<lldb::tid_t> m_expression_tid_stack;

private:

    ThreadList ();
};

} // namespace lldb_private

#endif // liblldb_ThreadList_h_
