// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MOJO_PUBLIC_CPP_BINDINGS_PENDING_REMOTE_H_
#define MOJO_PUBLIC_CPP_BINDINGS_PENDING_REMOTE_H_

#include <cstdint>
#include <type_traits>
#include <utility>

#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "build/build_config.h"
#include "mojo/public/cpp/bindings/interface_ptr_info.h"
#include "mojo/public/cpp/bindings/lib/pending_remote_state.h"
#include "mojo/public/cpp/system/message_pipe.h"

namespace mojo {

template <typename T>
class PendingReceiver;

template <typename T>
struct PendingRemoteConverter;

// A valid PendingRemote is entangled with exactly one Receiver or
// PendingReceiver, and can be consumed to bind a Remote in order to begin
// issuing method calls to that receiver. See Remote documentation for more
// details.
//
// PendingRemote instances may be freely moved to another thread/sequence, or
// even transferred to another process via a Mojo interface call (see
// pending_remote<T> syntax in mojom IDL).
//
// NOTE: This object is essentially semantic sugar wrapping a raw message pipe
// handle that is expected to send Interface messages of a specified version
// (typically 0) to a Receiver. As such, consumers who know what they're doing
// (i.e. who are confident about what lies on the other side of a pipe) may
// freely convert between a PendingRemote and a 2-tuple of
// [raw message pipe handle, expected interface version number].
template <typename Interface>
class PendingRemote {
 public:
  // Constructs an invalid PendingRemote. This object is not entangled with any
  // receiver and cannot be used to bind a Remote.
  //
  // A valid PendingRemote is typically obtained by calling
  // |Receiver::BindNewPipeAndPassRemote()| on an existing unbound Receiver
  // instance.
  //
  // To simultaneously create a valid PendingRemote and an entangled
  // PendingReceiver for rarer cases where both objects need to be passed
  // elsewhere, use the |InitWithNewPipeAndPassReceiver()| method defined below.
  PendingRemote() = default;
  PendingRemote(PendingRemote&&) noexcept = default;

  // Temporary helper for transitioning away from old types. Intentionally an
  // implicit constructor.
  PendingRemote(InterfacePtrInfo<Interface>&& ptr_info)
      : PendingRemote(ptr_info.PassHandle(), ptr_info.version()) {}

  // Constructs a valid PendingRemote over a valid raw message pipe handle and
  // expected interface version number.
  PendingRemote(ScopedMessagePipeHandle pipe, uint32_t version)
      : state_(std::move(pipe), version) {}

  // Disabled on NaCl since it crashes old version of clang.
#if !defined(OS_NACL)
  // Move conversion operator for custom remote types. Only participates in
  // overload resolution if a typesafe conversion is supported.
  template <
      typename T,
      std::enable_if_t<std::is_same<
          PendingRemote<Interface>,
          std::result_of_t<decltype (&PendingRemoteConverter<T>::template To<
                                     Interface>)(T&&)>>::value>* = nullptr>
  PendingRemote(T&& other)
      : PendingRemote(PendingRemoteConverter<T>::template To<Interface>(
            std::move(other))) {}
#endif  // !defined(OS_NACL)

  PendingRemote(const PendingRemote&) = delete;
  PendingRemote& operator=(const PendingRemote&) = delete;

  ~PendingRemote() = default;

  PendingRemote& operator=(PendingRemote&&) noexcept = default;

  // Indicates whether the PendingRemote is valid, meaning it can be used to
  // bind a Remote that wants to begin issuing method calls to be dispatched by
  // the entangled Receiver.
  bool is_valid() const { return state_.pipe.is_valid(); }
  explicit operator bool() const { return is_valid(); }

  // Temporary helper for transitioning away from old bindings types. This is
  // intentionally an implicit conversion.
  operator InterfacePtrInfo<Interface>() && {
    // |PassPipe()| invalidates all state, so capture |version()| first.
    uint32_t version = this->version();
    return InterfacePtrInfo<Interface>(PassPipe(), version);
  }

  // Resets this PendingRemote to an invalid state. If it was entangled with a
  // Receiver or PendingReceiver, that object remains in a valid state and will
  // eventually detect that its remote caller is gone.
  void reset() { state_.reset(); }

  // Takes ownership of this PendingRemote's message pipe handle. After this
  // call, the PendingRemote is no longer in a valid state and can no longer be
  // used to bind a Remote.
  ScopedMessagePipeHandle PassPipe() WARN_UNUSED_RESULT {
    state_.version = 0;
    return std::move(state_.pipe);
  }
  const ScopedMessagePipeHandle& Pipe() const { return state_.pipe; }

  // The version of the interface this Remote is assuming when making method
  // calls. For the most common case of unversioned mojom interfaces, this is
  // always zero.
  uint32_t version() const { return state_.version; }

  // Creates a new message pipe, retaining one end in the PendingRemote (making
  // it valid) and returning the other end as its entangled PendingReceiver. May
  // only be called on an invalid PendingRemote.
  REINITIALIZES_AFTER_MOVE PendingReceiver<Interface>
  InitWithNewPipeAndPassReceiver() WARN_UNUSED_RESULT;

  // For internal Mojo use only.
  internal::PendingRemoteState* internal_state() { return &state_; }

 private:
  internal::PendingRemoteState state_;
};

class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) NullRemote {
 public:
  template <typename Interface>
  operator PendingRemote<Interface>() const {
    return PendingRemote<Interface>();
  }
};

// Fuses a PendingReceiver<T> endpoint with a PendingRemote<T> endpoint. The
// endpoints must belong to two different message pipes, and this effectively
// fuses two pipes into a single pipe. Returns |true| on success or |false| on
// failure.
template <typename Interface>
bool FusePipes(PendingReceiver<Interface> receiver,
               PendingRemote<Interface> remote) {
  MojoResult result = FuseMessagePipes(receiver.PassPipe(), remote.PassPipe());
  return result == MOJO_RESULT_OK;
}

}  // namespace mojo

#include "mojo/public/cpp/bindings/pending_receiver.h"

namespace mojo {

template <typename Interface>
PendingReceiver<Interface>
PendingRemote<Interface>::InitWithNewPipeAndPassReceiver() {
  DCHECK(!is_valid()) << "PendingReceiver already has a remote";
  MessagePipe pipe;
  state_.pipe = std::move(pipe.handle0);
  return PendingReceiver<Interface>(std::move(pipe.handle1));
}

}  // namespace mojo

#endif  // MOJO_PUBLIC_CPP_BINDINGS_PENDING_REMOTE_H_
