// 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_GROUP_H_
#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_H_

#include <utility>

#include "base/memory/ref_counted.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/bindings_export.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"

namespace mojo {

class AssociatedGroupController;

// AssociatedGroup refers to all the interface endpoints running at one end of a
// message pipe. It is used to create associated interfaces for that message
// pipe.
// It is thread safe and cheap to make copies.
class MOJO_CPP_BINDINGS_EXPORT AssociatedGroup {
 public:
  // Configuration used by CreateAssociatedInterface(). Please see the comments
  // of that method for more details.
  enum AssociatedInterfaceConfig { WILL_PASS_PTR, WILL_PASS_REQUEST };

  AssociatedGroup();
  AssociatedGroup(const AssociatedGroup& other);

  ~AssociatedGroup();

  AssociatedGroup& operator=(const AssociatedGroup& other);

  // |config| indicates whether |ptr_info| or |request| will be sent to the
  // remote side of the message pipe.
  //
  // NOTE: If |config| is |WILL_PASS_REQUEST|, you will want to bind |ptr_info|
  // to a local AssociatedInterfacePtr to make calls. However, there is one
  // restriction: the pointer should NOT be used to make calls before |request|
  // is sent. Violating that will cause the message pipe to be closed. On the
  // other hand, as soon as |request| is sent, the pointer is usable. There is
  // no need to wait until |request| is bound to an implementation at the remote
  // side.
  template <typename T>
  void CreateAssociatedInterface(
      AssociatedInterfaceConfig config,
      AssociatedInterfacePtrInfo<T>* ptr_info,
      AssociatedInterfaceRequest<T>* request) {
    ScopedInterfaceEndpointHandle local;
    ScopedInterfaceEndpointHandle remote;
    CreateEndpointHandlePair(&local, &remote);

    if (!local.is_valid() || !remote.is_valid()) {
      *ptr_info = AssociatedInterfacePtrInfo<T>();
      *request = AssociatedInterfaceRequest<T>();
      return;
    }

    if (config == WILL_PASS_PTR) {
      ptr_info->set_handle(std::move(remote));

      // The implementation is local, therefore set the version according to
      // the interface definition that this code is built against.
      ptr_info->set_version(T::Version_);
      request->Bind(std::move(local));
    } else {
      ptr_info->set_handle(std::move(local));

      // The implementation is remote, we don't know about its actual version
      // yet.
      ptr_info->set_version(0u);
      request->Bind(std::move(remote));
    }
  }

 private:
  friend class AssociatedGroupController;

  void CreateEndpointHandlePair(
      ScopedInterfaceEndpointHandle* local_endpoint,
      ScopedInterfaceEndpointHandle* remote_endpoint);

  scoped_refptr<AssociatedGroupController> controller_;
};

}  // namespace mojo

#endif  // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_H_
