//===-- Communication.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_Communication_h_
#define liblldb_Communication_h_

#include "lldb/Core/Broadcaster.h"
#include "lldb/Host/HostThread.h"
#include "lldb/Utility/Timeout.h"
#include "lldb/lldb-defines.h"      // for DISALLOW_COPY_AND_ASSIGN
#include "lldb/lldb-enumerations.h" // for ConnectionStatus, FLAGS_ANONYMOU...
#include "lldb/lldb-forward.h"      // for ConnectionSP
#include "lldb/lldb-types.h"        // for thread_arg_t, thread_result_t

#include <atomic>
#include <mutex>
#include <ratio> // for micro
#include <string>

#include <stddef.h> // for size_t
#include <stdint.h> // for uint8_t

namespace lldb_private {
class Connection;
}
namespace lldb_private {
class ConstString;
}
namespace lldb_private {
class Status;
}

namespace lldb_private {

//----------------------------------------------------------------------
/// @class Communication Communication.h "lldb/Core/Communication.h"
/// @brief An abstract communications class.
///
/// Communication is an class that handles data communication
/// between two data sources. It uses a Connection class to do the
/// real communication. This approach has a couple of advantages: it
/// allows a single instance of this class to be used even though its
/// connection can change. Connections could negotiate for different
/// connections based on abilities like starting with Bluetooth and
/// negotiating up to WiFi if available. It also allows this class to be
/// subclassed by any interfaces that don't want to give bytes but want
/// to validate and give out packets. This can be done by overriding:
///
/// AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast);
///
/// Communication inherits from Broadcaster which means it can be
/// used in conjunction with Listener to wait for multiple broadcaster
/// objects and multiple events from each of those objects.
/// Communication defines a set of pre-defined event bits (see
/// enumerations definitions that start with "eBroadcastBit" below).
///
/// There are two modes in which communications can occur:
///     @li single-threaded
///     @li multi-threaded
///
/// In single-threaded mode, all reads and writes happen synchronously
/// on the calling thread.
///
/// In multi-threaded mode, a read thread is spawned that continually
/// reads data and caches any received bytes. To start the read thread
/// clients call:
///
///     bool Communication::StartReadThread (Status *);
///
/// If true is returned a read thread has been spawned that will
/// continually execute a call to the pure virtual DoRead function:
///
///     size_t Communication::ReadFromConnection (void *, size_t, uint32_t);
///
/// When bytes are received the data gets cached in \a m_bytes and this
/// class will broadcast a \b eBroadcastBitReadThreadGotBytes event.
/// Clients that want packet based communication should override
/// AppendBytesToCache. The subclasses can choose to call the
/// built in AppendBytesToCache with the \a broadcast parameter set to
/// false. This will cause the \b eBroadcastBitReadThreadGotBytes event
/// not get broadcast, and then the subclass can post a \b
/// eBroadcastBitPacketAvailable event when a full packet of data has
/// been received.
///
/// If the connection is disconnected a \b eBroadcastBitDisconnected
/// event gets broadcast. If the read thread exits a \b
/// eBroadcastBitReadThreadDidExit event will be broadcast. Clients
/// can also post a \b eBroadcastBitReadThreadShouldExit event to this
/// object which will cause the read thread to exit.
//----------------------------------------------------------------------
class Communication : public Broadcaster {
public:
  FLAGS_ANONYMOUS_ENUM(){
      eBroadcastBitDisconnected =
          (1u << 0), ///< Sent when the communications connection is lost.
      eBroadcastBitReadThreadGotBytes =
          (1u << 1), ///< Sent by the read thread when bytes become available.
      eBroadcastBitReadThreadDidExit =
          (1u
           << 2), ///< Sent by the read thread when it exits to inform clients.
      eBroadcastBitReadThreadShouldExit =
          (1u << 3), ///< Sent by clients that need to cancel the read thread.
      eBroadcastBitPacketAvailable =
          (1u << 4), ///< Sent when data received makes a complete packet.
      eBroadcastBitNoMorePendingInput = (1u << 5), ///< Sent by the read thread
                                                   ///to indicate all pending
                                                   ///input has been processed.
      kLoUserBroadcastBit =
          (1u << 16), ///< Subclasses can used bits 31:16 for any needed events.
      kHiUserBroadcastBit = (1u << 31),
      eAllEventBits = 0xffffffff};

  typedef void (*ReadThreadBytesReceived)(void *baton, const void *src,
                                          size_t src_len);

  //------------------------------------------------------------------
  /// Construct the Communication object with the specified name for
  /// the Broadcaster that this object inherits from.
  ///
  /// @param[in] broadcaster_name
  ///     The name of the broadcaster object.  This name should be as
  ///     complete as possible to uniquely identify this object. The
  ///     broadcaster name can be updated after the connect function
  ///     is called.
  //------------------------------------------------------------------
  Communication(const char *broadcaster_name);

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

  void Clear();

  //------------------------------------------------------------------
  /// Connect using the current connection by passing \a url to its
  /// connect function.
  /// string.
  ///
  /// @param[in] url
  ///     A string that contains all information needed by the
  ///     subclass to connect to another client.
  ///
  /// @return
  ///     \b True if the connect succeeded, \b false otherwise. The
  ///     internal error object should be filled in with an
  ///     appropriate value based on the result of this function.
  ///
  /// @see Status& Communication::GetError ();
  /// @see bool Connection::Connect (const char *url);
  //------------------------------------------------------------------
  lldb::ConnectionStatus Connect(const char *url, Status *error_ptr);

  //------------------------------------------------------------------
  /// Disconnect the communications connection if one is currently
  /// connected.
  ///
  /// @return
  ///     \b True if the disconnect succeeded, \b false otherwise. The
  ///     internal error object should be filled in with an
  ///     appropriate value based on the result of this function.
  ///
  /// @see Status& Communication::GetError ();
  /// @see bool Connection::Disconnect ();
  //------------------------------------------------------------------
  lldb::ConnectionStatus Disconnect(Status *error_ptr = nullptr);

  //------------------------------------------------------------------
  /// Check if the connection is valid.
  ///
  /// @return
  ///     \b True if this object is currently connected, \b false
  ///     otherwise.
  //------------------------------------------------------------------
  bool IsConnected() const;

  bool HasConnection() const;

  lldb_private::Connection *GetConnection() { return m_connection_sp.get(); }

  //------------------------------------------------------------------
  /// Read bytes from the current connection.
  ///
  /// If no read thread is running, this function call the
  /// connection's Connection::Read(...) function to get any available.
  ///
  /// If a read thread has been started, this function will check for
  /// any cached bytes that have already been read and return any
  /// currently available bytes. If no bytes are cached, it will wait
  /// for the bytes to become available by listening for the \a
  /// eBroadcastBitReadThreadGotBytes event. If this function consumes
  /// all of the bytes in the cache, it will reset the
  /// \a eBroadcastBitReadThreadGotBytes event bit.
  ///
  /// @param[in] dst
  ///     A destination buffer that must be at least \a dst_len bytes
  ///     long.
  ///
  /// @param[in] dst_len
  ///     The number of bytes to attempt to read, and also the max
  ///     number of bytes that can be placed into \a dst.
  ///
  /// @param[in] timeout
  ///     A timeout value or llvm::None for no timeout.
  ///
  /// @return
  ///     The number of bytes actually read.
  ///
  /// @see size_t Connection::Read (void *, size_t);
  //------------------------------------------------------------------
  size_t Read(void *dst, size_t dst_len, const Timeout<std::micro> &timeout,
              lldb::ConnectionStatus &status, Status *error_ptr);

  //------------------------------------------------------------------
  /// The actual write function that attempts to write to the
  /// communications protocol.
  ///
  /// Subclasses must override this function.
  ///
  /// @param[in] src
  ///     A source buffer that must be at least \a src_len bytes
  ///     long.
  ///
  /// @param[in] src_len
  ///     The number of bytes to attempt to write, and also the
  ///     number of bytes are currently available in \a src.
  ///
  /// @return
  ///     The number of bytes actually Written.
  //------------------------------------------------------------------
  size_t Write(const void *src, size_t src_len, lldb::ConnectionStatus &status,
               Status *error_ptr);

  //------------------------------------------------------------------
  /// Sets the connection that it to be used by this class.
  ///
  /// By making a communication class that uses different connections
  /// it allows a single communication interface to negotiate and
  /// change its connection without any interruption to the client.
  /// It also allows the Communication class to be subclassed for
  /// packet based communication.
  ///
  /// @param[in] connection
  ///     A connection that this class will own and destroy.
  ///
  /// @see
  ///     class Connection
  //------------------------------------------------------------------
  void SetConnection(Connection *connection);

  //------------------------------------------------------------------
  /// Starts a read thread whose sole purpose it to read bytes from
  /// the current connection. This function will call connection's
  /// read function:
  ///
  /// size_t Connection::Read (void *, size_t);
  ///
  /// When bytes are read and cached, this function will call:
  ///
  /// Communication::AppendBytesToCache (const uint8_t * bytes, size_t len, bool
  /// broadcast);
  ///
  /// Subclasses should override this function if they wish to override
  /// the default action of caching the bytes and broadcasting a \b
  /// eBroadcastBitReadThreadGotBytes event.
  ///
  /// @return
  ///     \b True if the read thread was successfully started, \b
  ///     false otherwise.
  ///
  /// @see size_t Connection::Read (void *, size_t);
  /// @see void Communication::AppendBytesToCache (const uint8_t * bytes, size_t
  /// len, bool broadcast);
  //------------------------------------------------------------------
  virtual bool StartReadThread(Status *error_ptr = nullptr);

  //------------------------------------------------------------------
  /// Stops the read thread by cancelling it.
  ///
  /// @return
  ///     \b True if the read thread was successfully canceled, \b
  ///     false otherwise.
  //------------------------------------------------------------------
  virtual bool StopReadThread(Status *error_ptr = nullptr);

  virtual bool JoinReadThread(Status *error_ptr = nullptr);
  //------------------------------------------------------------------
  /// Checks if there is a currently running read thread.
  ///
  /// @return
  ///     \b True if the read thread is running, \b false otherwise.
  //------------------------------------------------------------------
  bool ReadThreadIsRunning();

  //------------------------------------------------------------------
  /// The static read thread function. This function will call
  /// the "DoRead" function continuously and wait for data to become
  /// available. When data is received it will append the available
  /// data to the internal cache and broadcast a
  /// \b eBroadcastBitReadThreadGotBytes event.
  ///
  /// @param[in] comm_ptr
  ///     A pointer to an instance of this class.
  ///
  /// @return
  ///     \b NULL.
  ///
  /// @see void Communication::ReadThreadGotBytes (const uint8_t *, size_t);
  //------------------------------------------------------------------
  static lldb::thread_result_t ReadThread(lldb::thread_arg_t comm_ptr);

  void SetReadThreadBytesReceivedCallback(ReadThreadBytesReceived callback,
                                          void *callback_baton);

  //------------------------------------------------------------------
  /// Wait for the read thread to process all outstanding data.
  ///
  /// After this function returns, the read thread has processed all data that
  /// has been waiting in the Connection queue.
  ///
  //------------------------------------------------------------------
  void SynchronizeWithReadThread();

  static const char *ConnectionStatusAsCString(lldb::ConnectionStatus status);

  bool GetCloseOnEOF() const { return m_close_on_eof; }

  void SetCloseOnEOF(bool b) { m_close_on_eof = b; }

  static ConstString &GetStaticBroadcasterClass();

  ConstString &GetBroadcasterClass() const override {
    return GetStaticBroadcasterClass();
  }

protected:
  lldb::ConnectionSP m_connection_sp; ///< The connection that is current in use
                                      ///by this communications class.
  HostThread m_read_thread; ///< The read thread handle in case we need to
                            ///cancel the thread.
  std::atomic<bool> m_read_thread_enabled;
  std::atomic<bool> m_read_thread_did_exit;
  std::string
      m_bytes; ///< A buffer to cache bytes read in the ReadThread function.
  std::recursive_mutex m_bytes_mutex; ///< A mutex to protect multi-threaded
                                      ///access to the cached bytes.
  std::mutex
      m_write_mutex; ///< Don't let multiple threads write at the same time...
  std::mutex m_synchronize_mutex;
  ReadThreadBytesReceived m_callback;
  void *m_callback_baton;
  bool m_close_on_eof;

  size_t ReadFromConnection(void *dst, size_t dst_len,
                            const Timeout<std::micro> &timeout,
                            lldb::ConnectionStatus &status, Status *error_ptr);

  //------------------------------------------------------------------
  /// Append new bytes that get read from the read thread into the
  /// internal object byte cache. This will cause a \b
  /// eBroadcastBitReadThreadGotBytes event to be broadcast if \a
  /// broadcast is true.
  ///
  /// Subclasses can override this function in order to inspect the
  /// received data and check if a packet is available.
  ///
  /// Subclasses can also still call this function from the
  /// overridden method to allow the caching to correctly happen and
  /// suppress the broadcasting of the \a eBroadcastBitReadThreadGotBytes
  /// event by setting \a broadcast to false.
  ///
  /// @param[in] src
  ///     A source buffer that must be at least \a src_len bytes
  ///     long.
  ///
  /// @param[in] src_len
  ///     The number of bytes to append to the cache.
  //------------------------------------------------------------------
  virtual void AppendBytesToCache(const uint8_t *src, size_t src_len,
                                  bool broadcast,
                                  lldb::ConnectionStatus status);

  //------------------------------------------------------------------
  /// Get any available bytes from our data cache. If this call
  /// empties the data cache, the \b eBroadcastBitReadThreadGotBytes event
  /// will be reset to signify no more bytes are available.
  ///
  /// @param[in] dst
  ///     A destination buffer that must be at least \a dst_len bytes
  ///     long.
  ///
  /// @param[in] dst_len
  ///     The number of bytes to attempt to read from the cache,
  ///     and also the max number of bytes that can be placed into
  ///     \a dst.
  ///
  /// @return
  ///     The number of bytes extracted from the data cache.
  //------------------------------------------------------------------
  size_t GetCachedBytes(void *dst, size_t dst_len);

private:
  DISALLOW_COPY_AND_ASSIGN(Communication);
};

} // namespace lldb_private

#endif // liblldb_Communication_h_
