//===-- Broadcaster.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_Broadcaster_h_
#define liblldb_Broadcaster_h_

#include "lldb/Utility/ConstString.h"
#include "lldb/lldb-defines.h" // for DISALLOW_COPY_AND_ASSIGN
#include "lldb/lldb-forward.h" // for ListenerSP, EventSP, Broadcast...

#include "llvm/ADT/SmallVector.h"

#include <cstdint> // for uint32_t, UINT32_MAX
#include <map>
#include <memory> // for shared_ptr, operator==, enable...
#include <mutex>
#include <set> // for set
#include <string>
#include <utility> // for pair
#include <vector>

namespace lldb_private {
class Broadcaster;
}
namespace lldb_private {
class EventData;
}
namespace lldb_private {
class Listener;
}
namespace lldb_private {
class Stream;
}

namespace lldb_private {

//----------------------------------------------------------------------
// lldb::BroadcastEventSpec
//
// This class is used to specify a kind of event to register for.  The Debugger
// maintains a list of BroadcastEventSpec's and when it is made
//----------------------------------------------------------------------
class BroadcastEventSpec {
public:
  BroadcastEventSpec(const ConstString &broadcaster_class, uint32_t event_bits)
      : m_broadcaster_class(broadcaster_class), m_event_bits(event_bits) {}

  BroadcastEventSpec(const BroadcastEventSpec &rhs);

  ~BroadcastEventSpec() = default;

  const ConstString &GetBroadcasterClass() const { return m_broadcaster_class; }

  uint32_t GetEventBits() const { return m_event_bits; }

  // Tell whether this BroadcastEventSpec is contained in in_spec.
  // That is:
  // (a) the two spec's share the same broadcaster class
  // (b) the event bits of this spec are wholly contained in those of in_spec.
  bool IsContainedIn(BroadcastEventSpec in_spec) const {
    if (m_broadcaster_class != in_spec.GetBroadcasterClass())
      return false;
    uint32_t in_bits = in_spec.GetEventBits();
    if (in_bits == m_event_bits)
      return true;
    else {
      if ((m_event_bits & in_bits) != 0 && (m_event_bits & ~in_bits) == 0)
        return true;
    }
    return false;
  }

  bool operator<(const BroadcastEventSpec &rhs) const;
  BroadcastEventSpec &operator=(const BroadcastEventSpec &rhs);

private:
  ConstString m_broadcaster_class;
  uint32_t m_event_bits;
};

class BroadcasterManager
    : public std::enable_shared_from_this<BroadcasterManager> {
public:
  friend class Listener;

protected:
  BroadcasterManager();

public:
  // Listeners hold onto weak pointers to their broadcaster managers.  So they
  // must be made into shared pointers, which you do with
  // MakeBroadcasterManager.

  static lldb::BroadcasterManagerSP MakeBroadcasterManager();

  ~BroadcasterManager() = default;

  uint32_t RegisterListenerForEvents(const lldb::ListenerSP &listener_sp,
                                     BroadcastEventSpec event_spec);

  bool UnregisterListenerForEvents(const lldb::ListenerSP &listener_sp,
                                   BroadcastEventSpec event_spec);

  lldb::ListenerSP GetListenerForEventSpec(BroadcastEventSpec event_spec) const;

  void SignUpListenersForBroadcaster(Broadcaster &broadcaster);

  void RemoveListener(const lldb::ListenerSP &listener_sp);

  void RemoveListener(Listener *listener);

  void Clear();

private:
  typedef std::pair<BroadcastEventSpec, lldb::ListenerSP> event_listener_key;
  typedef std::map<BroadcastEventSpec, lldb::ListenerSP> collection;
  typedef std::set<lldb::ListenerSP> listener_collection;
  collection m_event_map;
  listener_collection m_listeners;

  mutable std::recursive_mutex m_manager_mutex;

  // A couple of comparator classes for find_if:

  class BroadcasterClassMatches {
  public:
    BroadcasterClassMatches(const ConstString &broadcaster_class)
        : m_broadcaster_class(broadcaster_class) {}

    ~BroadcasterClassMatches() = default;

    bool operator()(const event_listener_key input) const {
      return (input.first.GetBroadcasterClass() == m_broadcaster_class);
    }

  private:
    ConstString m_broadcaster_class;
  };

  class BroadcastEventSpecMatches {
  public:
    BroadcastEventSpecMatches(BroadcastEventSpec broadcaster_spec)
        : m_broadcaster_spec(broadcaster_spec) {}

    ~BroadcastEventSpecMatches() = default;

    bool operator()(const event_listener_key input) const {
      return (input.first.IsContainedIn(m_broadcaster_spec));
    }

  private:
    BroadcastEventSpec m_broadcaster_spec;
  };

  class ListenerMatchesAndSharedBits {
  public:
    explicit ListenerMatchesAndSharedBits(BroadcastEventSpec broadcaster_spec,
                                          const lldb::ListenerSP listener_sp)
        : m_broadcaster_spec(broadcaster_spec), m_listener_sp(listener_sp) {}

    ~ListenerMatchesAndSharedBits() = default;

    bool operator()(const event_listener_key input) const {
      return (input.first.GetBroadcasterClass() ==
                  m_broadcaster_spec.GetBroadcasterClass() &&
              (input.first.GetEventBits() &
               m_broadcaster_spec.GetEventBits()) != 0 &&
              input.second == m_listener_sp);
    }

  private:
    BroadcastEventSpec m_broadcaster_spec;
    const lldb::ListenerSP m_listener_sp;
  };

  class ListenerMatches {
  public:
    explicit ListenerMatches(const lldb::ListenerSP in_listener_sp)
        : m_listener_sp(in_listener_sp) {}

    ~ListenerMatches() = default;

    bool operator()(const event_listener_key input) const {
      if (input.second == m_listener_sp)
        return true;
      else
        return false;
    }

  private:
    const lldb::ListenerSP m_listener_sp;
  };

  class ListenerMatchesPointer {
  public:
    ListenerMatchesPointer(const Listener *in_listener)
        : m_listener(in_listener) {}

    ~ListenerMatchesPointer() = default;

    bool operator()(const event_listener_key input) const {
      if (input.second.get() == m_listener)
        return true;
      else
        return false;
    }

    bool operator()(const lldb::ListenerSP input) const {
      if (input.get() == m_listener)
        return true;
      else
        return false;
    }

  private:
    const Listener *m_listener;
  };
};

//----------------------------------------------------------------------
/// @class Broadcaster Broadcaster.h "lldb/Core/Broadcaster.h"
/// @brief An event broadcasting class.
///
/// The Broadcaster class is designed to be subclassed by objects that
/// wish to vend events in a multi-threaded environment. Broadcaster
/// objects can each vend 32 events. Each event is represented by a bit
/// in a 32 bit value and these bits can be set:
///     @see Broadcaster::SetEventBits(uint32_t)
/// or cleared:
///     @see Broadcaster::ResetEventBits(uint32_t)
/// When an event gets set the Broadcaster object will notify the
/// Listener object that is listening for the event (if there is one).
///
/// Subclasses should provide broadcast bit definitions for any events
/// they vend, typically using an enumeration:
///     \code
///         class Foo : public Broadcaster
///         {
///         public:
///         //----------------------------------------------------------
///         // Broadcaster event bits definitions.
///         //----------------------------------------------------------
///         enum
///         {
///             eBroadcastBitOne   = (1 << 0),
///             eBroadcastBitTwo   = (1 << 1),
///             eBroadcastBitThree = (1 << 2),
///             ...
///         };
///     \endcode
//----------------------------------------------------------------------
class Broadcaster {
  friend class Listener;
  friend class Event;

public:
  //------------------------------------------------------------------
  /// Construct with a broadcaster with a name.
  ///
  /// @param[in] name
  ///     A NULL terminated C string that contains the name of the
  ///     broadcaster object.
  //------------------------------------------------------------------
  Broadcaster(lldb::BroadcasterManagerSP manager_sp, const char *name);

  //------------------------------------------------------------------
  /// Destructor.
  ///
  /// The destructor is virtual since this class gets subclassed.
  //------------------------------------------------------------------
  virtual ~Broadcaster();

  void CheckInWithManager();

  //------------------------------------------------------------------
  /// Broadcast an event which has no associated data.
  ///
  /// @param[in] event_type
  ///     The element from the enum defining this broadcaster's events
  ///     that is being broadcast.
  ///
  /// @param[in] event_data
  ///     User event data that will be owned by the lldb::Event that
  ///     is created internally.
  ///
  /// @param[in] unique
  ///     If true, then only add an event of this type if there isn't
  ///     one already in the queue.
  ///
  //------------------------------------------------------------------
  void BroadcastEvent(lldb::EventSP &event_sp) {
    m_broadcaster_sp->BroadcastEvent(event_sp);
  }

  void BroadcastEventIfUnique(lldb::EventSP &event_sp) {
    m_broadcaster_sp->BroadcastEventIfUnique(event_sp);
  }

  void BroadcastEvent(uint32_t event_type,
                      const lldb::EventDataSP &event_data_sp) {
    m_broadcaster_sp->BroadcastEvent(event_type, event_data_sp);
  }

  void BroadcastEvent(uint32_t event_type, EventData *event_data = nullptr) {
    m_broadcaster_sp->BroadcastEvent(event_type, event_data);
  }

  void BroadcastEventIfUnique(uint32_t event_type,
                              EventData *event_data = nullptr) {
    m_broadcaster_sp->BroadcastEventIfUnique(event_type, event_data);
  }

  void Clear() { m_broadcaster_sp->Clear(); }

  virtual void AddInitialEventsToListener(const lldb::ListenerSP &listener_sp,
                                          uint32_t requested_events);

  //------------------------------------------------------------------
  /// Listen for any events specified by \a event_mask.
  ///
  /// Only one listener can listen to each event bit in a given
  /// Broadcaster. Once a listener has acquired an event bit, no
  /// other broadcaster will have access to it until it is
  /// relinquished by the first listener that gets it. The actual
  /// event bits that get acquired by \a listener may be different
  /// from what is requested in \a event_mask, and to track this the
  /// actual event bits that are acquired get returned.
  ///
  /// @param[in] listener
  ///     The Listener object that wants to monitor the events that
  ///     get broadcast by this object.
  ///
  /// @param[in] event_mask
  ///     A bit mask that indicates which events the listener is
  ///     asking to monitor.
  ///
  /// @return
  ///     The actual event bits that were acquired by \a listener.
  //------------------------------------------------------------------
  uint32_t AddListener(const lldb::ListenerSP &listener_sp,
                       uint32_t event_mask) {
    return m_broadcaster_sp->AddListener(listener_sp, event_mask);
  }

  //------------------------------------------------------------------
  /// Get the NULL terminated C string name of this Broadcaster
  /// object.
  ///
  /// @return
  ///     The NULL terminated C string name of this Broadcaster.
  //------------------------------------------------------------------
  const ConstString &GetBroadcasterName() { return m_broadcaster_name; }

  //------------------------------------------------------------------
  /// Get the event name(s) for one or more event bits.
  ///
  /// @param[in] event_mask
  ///     A bit mask that indicates which events to get names for.
  ///
  /// @return
  ///     The NULL terminated C string name of this Broadcaster.
  //------------------------------------------------------------------
  bool GetEventNames(Stream &s, const uint32_t event_mask,
                     bool prefix_with_broadcaster_name) const {
    return m_broadcaster_sp->GetEventNames(s, event_mask,
                                           prefix_with_broadcaster_name);
  }

  //------------------------------------------------------------------
  /// Set the name for an event bit.
  ///
  /// @param[in] event_mask
  ///     A bit mask that indicates which events the listener is
  ///     asking to monitor.
  ///
  /// @return
  ///     The NULL terminated C string name of this Broadcaster.
  //------------------------------------------------------------------
  void SetEventName(uint32_t event_mask, const char *name) {
    m_broadcaster_sp->SetEventName(event_mask, name);
  }

  const char *GetEventName(uint32_t event_mask) const {
    return m_broadcaster_sp->GetEventName(event_mask);
  }

  bool EventTypeHasListeners(uint32_t event_type) {
    return m_broadcaster_sp->EventTypeHasListeners(event_type);
  }

  //------------------------------------------------------------------
  /// Removes a Listener from this broadcasters list and frees the
  /// event bits specified by \a event_mask that were previously
  /// acquired by \a listener (assuming \a listener was listening to
  /// this object) for other listener objects to use.
  ///
  /// @param[in] listener
  ///     A Listener object that previously called AddListener.
  ///
  /// @param[in] event_mask
  ///     The event bits \a listener wishes to relinquish.
  ///
  /// @return
  ///     \b True if the listener was listening to this broadcaster
  ///     and was removed, \b false otherwise.
  ///
  /// @see uint32_t Broadcaster::AddListener (Listener*, uint32_t)
  //------------------------------------------------------------------
  bool RemoveListener(const lldb::ListenerSP &listener_sp,
                      uint32_t event_mask = UINT32_MAX) {
    return m_broadcaster_sp->RemoveListener(listener_sp, event_mask);
  }

  //------------------------------------------------------------------
  /// Provides a simple mechanism to temporarily redirect events from
  /// broadcaster.  When you call this function passing in a listener and
  /// event type mask, all events from the broadcaster matching the mask
  /// will now go to the hijacking listener.
  /// Only one hijack can occur at a time.  If we need more than this we
  /// will have to implement a Listener stack.
  ///
  /// @param[in] listener
  ///     A Listener object.  You do not need to call StartListeningForEvents
  ///     for this broadcaster (that would fail anyway since the event bits
  ///     would most likely be taken by the listener(s) you are usurping.
  ///
  /// @param[in] event_mask
  ///     The event bits \a listener wishes to hijack.
  ///
  /// @return
  ///     \b True if the event mask could be hijacked, \b false otherwise.
  ///
  /// @see uint32_t Broadcaster::AddListener (Listener*, uint32_t)
  //------------------------------------------------------------------
  bool HijackBroadcaster(const lldb::ListenerSP &listener_sp,
                         uint32_t event_mask = UINT32_MAX) {
    return m_broadcaster_sp->HijackBroadcaster(listener_sp, event_mask);
  }

  bool IsHijackedForEvent(uint32_t event_mask) {
    return m_broadcaster_sp->IsHijackedForEvent(event_mask);
  }

  //------------------------------------------------------------------
  /// Restore the state of the Broadcaster from a previous hijack attempt.
  ///
  //------------------------------------------------------------------
  void RestoreBroadcaster() { m_broadcaster_sp->RestoreBroadcaster(); }

  // This needs to be filled in if you are going to register the broadcaster
  // with the broadcaster
  // manager and do broadcaster class matching.
  // FIXME: Probably should make a ManagedBroadcaster subclass with all the bits
  // needed to work
  // with the BroadcasterManager, so that it is clearer how to add one.
  virtual ConstString &GetBroadcasterClass() const;

  lldb::BroadcasterManagerSP GetManager();

protected:
  // BroadcasterImpl contains the actual Broadcaster implementation.  The
  // Broadcaster makes a BroadcasterImpl
  // which lives as long as it does.  The Listeners & the Events hold a weak
  // pointer to the BroadcasterImpl,
  // so that they can survive if a Broadcaster they were listening to is
  // destroyed w/o their being able to
  // unregister from it (which can happen if the Broadcasters & Listeners are
  // being destroyed on separate threads
  // simultaneously.
  // The Broadcaster itself can't be shared out as a weak pointer, because some
  // things that are broadcasters
  // (e.g. the Target and the Process) are shared in their own right.
  //
  // For the most part, the Broadcaster functions dispatch to the
  // BroadcasterImpl, and are documented in the
  // public Broadcaster API above.

  class BroadcasterImpl {
    friend class Listener;
    friend class Broadcaster;

  public:
    BroadcasterImpl(Broadcaster &broadcaster);

    ~BroadcasterImpl() = default;

    void BroadcastEvent(lldb::EventSP &event_sp);

    void BroadcastEventIfUnique(lldb::EventSP &event_sp);

    void BroadcastEvent(uint32_t event_type, EventData *event_data = nullptr);

    void BroadcastEvent(uint32_t event_type,
                        const lldb::EventDataSP &event_data_sp);

    void BroadcastEventIfUnique(uint32_t event_type,
                                EventData *event_data = nullptr);

    void Clear();

    uint32_t AddListener(const lldb::ListenerSP &listener_sp,
                         uint32_t event_mask);

    const char *GetBroadcasterName() const {
      return m_broadcaster.GetBroadcasterName().AsCString();
    }

    Broadcaster *GetBroadcaster();

    bool GetEventNames(Stream &s, const uint32_t event_mask,
                       bool prefix_with_broadcaster_name) const;

    void SetEventName(uint32_t event_mask, const char *name) {
      m_event_names[event_mask] = name;
    }

    const char *GetEventName(uint32_t event_mask) const {
      const auto pos = m_event_names.find(event_mask);
      if (pos != m_event_names.end())
        return pos->second.c_str();
      return nullptr;
    }

    bool EventTypeHasListeners(uint32_t event_type);

    bool RemoveListener(lldb_private::Listener *listener,
                        uint32_t event_mask = UINT32_MAX);

    bool RemoveListener(const lldb::ListenerSP &listener_sp,
                        uint32_t event_mask = UINT32_MAX);

    bool HijackBroadcaster(const lldb::ListenerSP &listener_sp,
                           uint32_t event_mask = UINT32_MAX);

    bool IsHijackedForEvent(uint32_t event_mask);

    void RestoreBroadcaster();

  protected:
    void PrivateBroadcastEvent(lldb::EventSP &event_sp, bool unique);

    const char *GetHijackingListenerName();

    //------------------------------------------------------------------
    //
    //------------------------------------------------------------------
    typedef llvm::SmallVector<std::pair<lldb::ListenerWP, uint32_t>, 4>
        collection;
    typedef std::map<uint32_t, std::string> event_names_map;

    llvm::SmallVector<std::pair<lldb::ListenerSP, uint32_t &>, 4>
    GetListeners();

    Broadcaster &m_broadcaster;    ///< The broadcsater that this implements
    event_names_map m_event_names; ///< Optionally define event names for
                                   ///readability and logging for each event bit
    collection m_listeners; ///< A list of Listener / event_mask pairs that are
                            ///listening to this broadcaster.
    std::recursive_mutex
        m_listeners_mutex; ///< A mutex that protects \a m_listeners.
    std::vector<lldb::ListenerSP> m_hijacking_listeners; // A simple mechanism
                                                         // to intercept events
                                                         // from a broadcaster
    std::vector<uint32_t> m_hijacking_masks; // At some point we may want to
                                             // have a stack or Listener
    // collections, but for now this is just for private hijacking.

  private:
    //------------------------------------------------------------------
    // For Broadcaster only
    //------------------------------------------------------------------
    DISALLOW_COPY_AND_ASSIGN(BroadcasterImpl);
  };

  typedef std::shared_ptr<BroadcasterImpl> BroadcasterImplSP;
  typedef std::weak_ptr<BroadcasterImpl> BroadcasterImplWP;

  BroadcasterImplSP GetBroadcasterImpl() { return m_broadcaster_sp; }

  const char *GetHijackingListenerName() {
    return m_broadcaster_sp->GetHijackingListenerName();
  }
  //------------------------------------------------------------------
  // Classes that inherit from Broadcaster can see and modify these
  //------------------------------------------------------------------

private:
  //------------------------------------------------------------------
  // For Broadcaster only
  //------------------------------------------------------------------
  BroadcasterImplSP m_broadcaster_sp;
  lldb::BroadcasterManagerSP m_manager_sp;
  const ConstString
      m_broadcaster_name; ///< The name of this broadcaster object.

  DISALLOW_COPY_AND_ASSIGN(Broadcaster);
};

} // namespace lldb_private

#endif // liblldb_Broadcaster_h_
