// Copyright 2015 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_ASSOCIATED_INTERFACE_PTR_H_
#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_

#include <stdint.h>

#include <string>
#include <utility>

#include "base/callback.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
#include "mojo/public/cpp/bindings/associated_interface_request.h"
#include "mojo/public/cpp/bindings/connection_error_callback.h"
#include "mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h"
#include "mojo/public/cpp/bindings/lib/multiplex_router.h"
#include "mojo/public/cpp/system/message_pipe.h"

namespace mojo {

// Represents the client side of an associated interface. It is similar to
// InterfacePtr, except that it doesn't own a message pipe handle.
template <typename Interface>
class AssociatedInterfacePtr {
 public:
  using InterfaceType = Interface;
  using PtrInfoType = AssociatedInterfacePtrInfo<Interface>;

  // Constructs an unbound AssociatedInterfacePtr.
  AssociatedInterfacePtr() {}
  AssociatedInterfacePtr(decltype(nullptr)) {}

  AssociatedInterfacePtr(AssociatedInterfacePtr&& other) {
    internal_state_.Swap(&other.internal_state_);
  }

  AssociatedInterfacePtr& operator=(AssociatedInterfacePtr&& other) {
    reset();
    internal_state_.Swap(&other.internal_state_);
    return *this;
  }

  // Assigning nullptr to this class causes it to closes the associated
  // interface (if any) and returns the pointer to the unbound state.
  AssociatedInterfacePtr& operator=(decltype(nullptr)) {
    reset();
    return *this;
  }

  ~AssociatedInterfacePtr() {}

  // Sets up this object as the client side of an associated interface.
  // Calling with an invalid |info| has the same effect as reset(). In this
  // case, the AssociatedInterfacePtr is not considered as bound.
  //
  // |runner| must belong to the same thread. It will be used to dispatch all
  // callbacks and connection error notification. It is useful when you attach
  // multiple task runners to a single thread for the purposes of task
  // scheduling.
  //
  // NOTE: The corresponding AssociatedInterfaceRequest must be sent over
  // another interface before using this object to make calls. Please see the
  // comments of MakeRequest(AssociatedInterfacePtr<Interface>*) for more
  // details.
  void Bind(AssociatedInterfacePtrInfo<Interface> info,
            scoped_refptr<base::SingleThreadTaskRunner> runner =
                base::ThreadTaskRunnerHandle::Get()) {
    reset();

    if (info.is_valid())
      internal_state_.Bind(std::move(info), std::move(runner));
  }

  bool is_bound() const { return internal_state_.is_bound(); }

  Interface* get() const { return internal_state_.instance(); }

  // Functions like a pointer to Interface. Must already be bound.
  Interface* operator->() const { return get(); }
  Interface& operator*() const { return *get(); }

  // Returns the version number of the interface that the remote side supports.
  uint32_t version() const { return internal_state_.version(); }

  // Queries the max version that the remote side supports. On completion, the
  // result will be returned as the input of |callback|. The version number of
  // this object will also be updated.
  void QueryVersion(const base::Callback<void(uint32_t)>& callback) {
    internal_state_.QueryVersion(callback);
  }

  // If the remote side doesn't support the specified version, it will close the
  // associated interface asynchronously. This does nothing if it's already
  // known that the remote side supports the specified version, i.e., if
  // |version <= this->version()|.
  //
  // After calling RequireVersion() with a version not supported by the remote
  // side, all subsequent calls to interface methods will be ignored.
  void RequireVersion(uint32_t version) {
    internal_state_.RequireVersion(version);
  }

  // Sends a message on the underlying message pipe and runs the current
  // message loop until its response is received. This can be used in tests to
  // verify that no message was sent on a message pipe in response to some
  // stimulus.
  void FlushForTesting() { internal_state_.FlushForTesting(); }

  // Closes the associated interface (if any) and returns the pointer to the
  // unbound state.
  void reset() {
    State doomed;
    internal_state_.Swap(&doomed);
  }

  // Similar to the method above, but also specifies a disconnect reason.
  void ResetWithReason(uint32_t custom_reason, const std::string& description) {
    if (internal_state_.is_bound())
      internal_state_.CloseWithReason(custom_reason, description);
    reset();
  }

  // Indicates whether an error has been encountered. If true, method calls made
  // on this interface will be dropped (and may already have been dropped).
  bool encountered_error() const { return internal_state_.encountered_error(); }

  // Registers a handler to receive error notifications.
  //
  // This method may only be called after the AssociatedInterfacePtr has been
  // bound.
  void set_connection_error_handler(const base::Closure& error_handler) {
    internal_state_.set_connection_error_handler(error_handler);
  }

  void set_connection_error_with_reason_handler(
      const ConnectionErrorWithReasonCallback& error_handler) {
    internal_state_.set_connection_error_with_reason_handler(error_handler);
  }

  // Unbinds and returns the associated interface pointer information which
  // could be used to setup an AssociatedInterfacePtr again. This method may be
  // used to move the proxy to a different thread.
  //
  // It is an error to call PassInterface() while there are pending responses.
  // TODO: fix this restriction, it's not always obvious when there is a
  // pending response.
  AssociatedInterfacePtrInfo<Interface> PassInterface() {
    DCHECK(!internal_state_.has_pending_callbacks());
    State state;
    internal_state_.Swap(&state);

    return state.PassInterface();
  }

  // DO NOT USE. Exposed only for internal use and for testing.
  internal::AssociatedInterfacePtrState<Interface>* internal_state() {
    return &internal_state_;
  }

  // Allow AssociatedInterfacePtr<> to be used in boolean expressions, but not
  // implicitly convertible to a real bool (which is dangerous).
 private:
  // TODO(dcheng): Use an explicit conversion operator.
  typedef internal::AssociatedInterfacePtrState<Interface>
      AssociatedInterfacePtr::*Testable;

 public:
  operator Testable() const {
    return internal_state_.is_bound() ? &AssociatedInterfacePtr::internal_state_
                                      : nullptr;
  }

 private:
  // Forbid the == and != operators explicitly, otherwise AssociatedInterfacePtr
  // will be converted to Testable to do == or != comparison.
  template <typename T>
  bool operator==(const AssociatedInterfacePtr<T>& other) const = delete;
  template <typename T>
  bool operator!=(const AssociatedInterfacePtr<T>& other) const = delete;

  typedef internal::AssociatedInterfacePtrState<Interface> State;
  mutable State internal_state_;

  DISALLOW_COPY_AND_ASSIGN(AssociatedInterfacePtr);
};

// Creates an associated interface. The returned request is supposed to be sent
// over another interface (either associated or non-associated).
//
// NOTE: |ptr| must NOT be used to make calls before the request is sent.
// Violating that will lead to crash. On the other hand, as soon as the request
// is sent, |ptr| is usable. There is no need to wait until the request is bound
// to an implementation at the remote side.
template <typename Interface>
AssociatedInterfaceRequest<Interface> MakeRequest(
    AssociatedInterfacePtr<Interface>* ptr,
    scoped_refptr<base::SingleThreadTaskRunner> runner =
        base::ThreadTaskRunnerHandle::Get()) {
  AssociatedInterfacePtrInfo<Interface> ptr_info;
  auto request = MakeRequest(&ptr_info);
  ptr->Bind(std::move(ptr_info), std::move(runner));
  return request;
}

// Creates an associated interface. One of the two endpoints is supposed to be
// sent over another interface (either associated or non-associated); while the
// other is used locally.
//
// NOTE: If |ptr_info| is used locally and bound to an AssociatedInterfacePtr,
// the interface pointer must NOT be used to make calls before the request is
// sent. Please see NOTE of the previous function for more details.
template <typename Interface>
AssociatedInterfaceRequest<Interface> MakeRequest(
    AssociatedInterfacePtrInfo<Interface>* ptr_info) {
  ScopedInterfaceEndpointHandle handle0;
  ScopedInterfaceEndpointHandle handle1;
  ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(&handle0,
                                                              &handle1);

  ptr_info->set_handle(std::move(handle0));
  ptr_info->set_version(0);

  AssociatedInterfaceRequest<Interface> request;
  request.Bind(std::move(handle1));
  return request;
}

// Like |GetProxy|, but the interface is never associated with any other
// interface. The returned request can be bound directly to the corresponding
// associated interface implementation, without first passing it through a
// message pipe endpoint.
//
// This function has two main uses:
//
//  * In testing, where the returned request is bound to e.g. a mock and there
//    are no other interfaces involved.
//
//  * When discarding messages sent on an interface, which can be done by
//    discarding the returned request.
template <typename Interface>
AssociatedInterfaceRequest<Interface> GetIsolatedProxy(
    AssociatedInterfacePtr<Interface>* ptr) {
  MessagePipe pipe;
  scoped_refptr<internal::MultiplexRouter> router0 =
      new internal::MultiplexRouter(std::move(pipe.handle0),
                                    internal::MultiplexRouter::MULTI_INTERFACE,
                                    false, base::ThreadTaskRunnerHandle::Get());
  scoped_refptr<internal::MultiplexRouter> router1 =
      new internal::MultiplexRouter(std::move(pipe.handle1),
                                    internal::MultiplexRouter::MULTI_INTERFACE,
                                    true, base::ThreadTaskRunnerHandle::Get());

  ScopedInterfaceEndpointHandle endpoint0, endpoint1;
  ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(&endpoint0,
                                                              &endpoint1);
  InterfaceId id = router1->AssociateInterface(std::move(endpoint0));
  endpoint0 = router0->CreateLocalEndpointHandle(id);

  ptr->Bind(AssociatedInterfacePtrInfo<Interface>(std::move(endpoint0),
                                                  Interface::Version_));

  AssociatedInterfaceRequest<Interface> request;
  request.Bind(std::move(endpoint1));
  return request;
}

// Creates an associated interface proxy in its own AssociatedGroup.
// TODO(yzshen): Rename GetIsolatedProxy() to MakeIsolatedRequest(), and change
// all callsites of this function to directly use that.
template <typename Interface>
AssociatedInterfaceRequest<Interface> MakeRequestForTesting(
    AssociatedInterfacePtr<Interface>* ptr) {
  return GetIsolatedProxy(ptr);
}

}  // namespace mojo

#endif  // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_