//===-- ThreadPlanShouldStopHere.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_ThreadPlanShouldStopHere_h_
#define liblldb_ThreadPlanShouldStopHere_h_

// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Target/ThreadPlan.h"

namespace lldb_private {

// This is an interface that ThreadPlans can adopt to allow flexible modifications of the behavior
// when a thread plan comes to a place where it would ordinarily stop.  If such modification makes
// sense for your plan, inherit from this class, and when you would be about to stop (in your ShouldStop
// method), call InvokeShouldStopHereCallback, passing in the frame comparison between where the step operation
// started and where you arrived.  If it returns true, then QueueStepOutFromHere will queue the plan
// to execute instead of stopping.
//
// The classic example of the use of this is ThreadPlanStepInRange not stopping in frames that have
// no debug information.
//
// This class also defines a set of flags to control general aspects of this "ShouldStop" behavior.
// A class implementing this protocol needs to define a default set of flags, and can provide access to
// changing that default flag set if it wishes.

class ThreadPlanShouldStopHere
{
public:
    struct ThreadPlanShouldStopHereCallbacks
    {
        ThreadPlanShouldStopHereCallbacks()
        {
            should_stop_here_callback = nullptr;
            step_from_here_callback = nullptr;
        }
        
        ThreadPlanShouldStopHereCallbacks(ThreadPlanShouldStopHereCallback should_stop,
                                          ThreadPlanStepFromHereCallback step_from_here)
        {
            should_stop_here_callback = should_stop;
            step_from_here_callback = step_from_here;
        }
        
        void
        Clear()
        {
            should_stop_here_callback = nullptr;
            step_from_here_callback = nullptr;
        }
        
        ThreadPlanShouldStopHereCallback should_stop_here_callback;
        ThreadPlanStepFromHereCallback   step_from_here_callback;
    };
    
    enum
    {
        eNone = 0,
        eAvoidInlines            = (1 << 0),
        eStepInAvoidNoDebug      = (1 << 1),
        eStepOutAvoidNoDebug     = (1 << 2)
    };

    //------------------------------------------------------------------
    // Constructors and Destructors
    //------------------------------------------------------------------
    ThreadPlanShouldStopHere (ThreadPlan *owner);
    
    ThreadPlanShouldStopHere(ThreadPlan *owner,
                             const ThreadPlanShouldStopHereCallbacks *callbacks,
                             void *baton = nullptr);
    virtual
    ~ThreadPlanShouldStopHere();
    
    // Set the ShouldStopHere callbacks.  Pass in null to clear them and have no special behavior (though you
    // can also call ClearShouldStopHereCallbacks for that purpose.  If you pass in a valid pointer, it will
    // adopt the non-null fields, and any null fields will be set to the default values.
    
    void
    SetShouldStopHereCallbacks (const ThreadPlanShouldStopHereCallbacks *callbacks, void *baton)
    {
        if (callbacks)
        {
            m_callbacks = *callbacks;
            if (!m_callbacks.should_stop_here_callback)
                m_callbacks.should_stop_here_callback = ThreadPlanShouldStopHere::DefaultShouldStopHereCallback;
            if (!m_callbacks.step_from_here_callback)
                m_callbacks.step_from_here_callback = ThreadPlanShouldStopHere::DefaultStepFromHereCallback;
        }
        else
        {
            ClearShouldStopHereCallbacks ();
        }
        m_baton = baton;
    }
    
    void
    ClearShouldStopHereCallbacks()
    {
        m_callbacks.Clear();
    }

    bool
    InvokeShouldStopHereCallback (lldb::FrameComparison operation);
    
    lldb::ThreadPlanSP
    CheckShouldStopHereAndQueueStepOut (lldb::FrameComparison operation);

    lldb_private::Flags &
    GetFlags ()
    {
        return m_flags;
    }

    const lldb_private::Flags &
    GetFlags () const
    {
        return m_flags;
    }

protected:
    static bool
    DefaultShouldStopHereCallback (ThreadPlan *current_plan, Flags &flags, lldb::FrameComparison operation, void *baton);
    
    static lldb::ThreadPlanSP
    DefaultStepFromHereCallback (ThreadPlan *current_plan, Flags &flags, lldb::FrameComparison operation, void *baton);
    
    virtual lldb::ThreadPlanSP
    QueueStepOutFromHerePlan (Flags &flags, lldb::FrameComparison operation);

    // Implement this, and call it in the plan's constructor to set the default flags.
    virtual void SetFlagsToDefault () = 0;

    ThreadPlanShouldStopHereCallbacks m_callbacks;
    void * m_baton;
    ThreadPlan *m_owner;
    lldb_private::Flags m_flags;

private:
    DISALLOW_COPY_AND_ASSIGN (ThreadPlanShouldStopHere);
};

} // namespace lldb_private

#endif // liblldb_ThreadPlanShouldStopHere_h_
