// 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_SELF_OWNED_RECEIVER_H_
#define MOJO_PUBLIC_CPP_BINDINGS_SELF_OWNED_RECEIVER_H_

#include <memory>
#include <utility>

#include "base/memory/scoped_refptr.h"
#include "base/task/sequenced_task_runner_forward.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"

namespace mojo {

namespace internal {

template <typename Interface>
class SelfOwnedReceiver;

}  // namespace internal

template <typename Interface>
using SelfOwnedReceiverRef =
    base::WeakPtr<internal::SelfOwnedReceiver<Interface>>;

namespace internal {

template <typename Interface>
class SelfOwnedReceiver {
 public:
  // Create a new SelfOwnedReceiver instance. The instance owns itself, cleaning
  // up only in the event of a pipe connection error. Returns a WeakPtr to the
  // new SelfOwnedReceiver instance.
  static SelfOwnedReceiverRef<Interface> Create(
      std::unique_ptr<Interface> impl,
      PendingReceiver<Interface> receiver,
      scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
    SelfOwnedReceiver* self_owned = new SelfOwnedReceiver(
        std::move(impl), std::move(receiver), std::move(task_runner));
    return self_owned->weak_factory_.GetWeakPtr();
  }

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

  // Note: The error handler must not delete the interface implementation.
  //
  // This method may only be called after this SelfOwnedReceiver has been bound
  // to a message pipe.
  //
  // TODO(dcheng): Consider renaming this eventually.
  void set_connection_error_handler(base::OnceClosure error_handler) {
    DCHECK(receiver_.is_bound());
    connection_error_handler_ = std::move(error_handler);
    connection_error_with_reason_handler_.Reset();
  }

  void set_connection_error_with_reason_handler(
      ConnectionErrorWithReasonCallback error_handler) {
    DCHECK(receiver_.is_bound());
    connection_error_with_reason_handler_ = std::move(error_handler);
    connection_error_handler_.Reset();
  }

  // Stops processing incoming messages until
  // ResumeIncomingMethodCallProcessing().
  // Outgoing messages are still sent.
  //
  // No errors are detected on the message pipe while paused.
  //
  // This method may only be called if the object has been bound to a message
  // pipe and there are no associated interfaces running.
  void PauseIncomingMethodCallProcessing() { receiver_.Pause(); }
  void ResumeIncomingMethodCallProcessing() { receiver_.Resume(); }

  // Forces the binding to close. This destroys the SelfOwnedReceiver instance.
  void Close() { delete this; }

  Interface* impl() { return impl_.get(); }

  // 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() { receiver_.FlushForTesting(); }

 private:
  SelfOwnedReceiver(std::unique_ptr<Interface> impl,
                    PendingReceiver<Interface> receiver,
                    scoped_refptr<base::SequencedTaskRunner> task_runner)
      : impl_(std::move(impl)),
        receiver_(impl_.get(), std::move(receiver), std::move(task_runner)) {
    receiver_.set_disconnect_with_reason_handler(base::BindOnce(
        &SelfOwnedReceiver::OnDisconnect, base::Unretained(this)));
  }

  ~SelfOwnedReceiver() = default;

  void OnDisconnect(uint32_t custom_reason, const std::string& description) {
    if (connection_error_handler_) {
      std::move(connection_error_handler_).Run();
    } else if (connection_error_with_reason_handler_) {
      std::move(connection_error_with_reason_handler_)
          .Run(custom_reason, description);
    }
    Close();
  }

  std::unique_ptr<Interface> impl_;
  base::OnceClosure connection_error_handler_;
  ConnectionErrorWithReasonCallback connection_error_with_reason_handler_;
  Receiver<Interface> receiver_;
  base::WeakPtrFactory<SelfOwnedReceiver> weak_factory_{this};
};

}  // namespace internal

// Binds the lifetime of an interface implementation to the lifetime of the
// Receiver. When the Receiver is disconnected (typically by the remote end
// closing the entangled Remote), the implementation will be deleted.
//
// Any incoming method calls or disconnection notifications will be scheduled
// to run on |task_runner|. If |task_runner| is null, this defaults to the
// current SequencedTaskRunner.
template <typename Interface, typename Impl>
SelfOwnedReceiverRef<Interface> MakeSelfOwnedReceiver(
    std::unique_ptr<Impl> impl,
    PendingReceiver<Interface> receiver,
    scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
  return internal::SelfOwnedReceiver<Interface>::Create(
      std::move(impl), std::move(receiver), std::move(task_runner));
}

}  // namespace mojo

#endif  // MOJO_PUBLIC_CPP_BINDINGS_SELF_OWNED_RECEIVER_H_
