// 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.

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

#include "base/logging.h"
#include "mojo/public/cpp/bindings/interface_id.h"
#include "mojo/public/cpp/bindings/lib/serialization.h"
#include "mojo/public/cpp/bindings/lib/validation_context.h"
#include "mojo/public/cpp/bindings/lib/validation_util.h"
#include "mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h"
#include "mojo/public/interfaces/bindings/pipe_control_messages.mojom.h"

namespace mojo {

PipeControlMessageHandler::PipeControlMessageHandler(
    PipeControlMessageHandlerDelegate* delegate)
    : delegate_(delegate) {}

PipeControlMessageHandler::~PipeControlMessageHandler() {}

void PipeControlMessageHandler::SetDescription(const std::string& description) {
  description_ = description;
}

// static
bool PipeControlMessageHandler::IsPipeControlMessage(const Message* message) {
  return !IsValidInterfaceId(message->interface_id());
}

bool PipeControlMessageHandler::Accept(Message* message) {
  if (!Validate(message))
    return false;

  if (message->name() == pipe_control::kRunOrClosePipeMessageId)
    return RunOrClosePipe(message);

  NOTREACHED();
  return false;
}

bool PipeControlMessageHandler::Validate(Message* message) {
  internal::ValidationContext validation_context(
      message->payload(), message->payload_num_bytes(),
      message->handles()->size(), 0, message, description_.c_str());

  if (message->name() == pipe_control::kRunOrClosePipeMessageId) {
    if (!internal::ValidateMessageIsRequestWithoutResponse(
            message, &validation_context)) {
      return false;
    }
    return internal::ValidateMessagePayload<
        pipe_control::internal::RunOrClosePipeMessageParams_Data>(
        message, &validation_context);
  }

  return false;
}

bool PipeControlMessageHandler::RunOrClosePipe(Message* message) {
  pipe_control::internal::RunOrClosePipeMessageParams_Data* params =
      reinterpret_cast<
          pipe_control::internal::RunOrClosePipeMessageParams_Data*>(
          message->mutable_payload());
  pipe_control::RunOrClosePipeMessageParamsPtr params_ptr;
  internal::Deserialize<pipe_control::RunOrClosePipeMessageParamsDataView>(
      params, &params_ptr, message);

  if (params_ptr->input->is_peer_associated_endpoint_closed_event()) {
    const auto& event =
        params_ptr->input->get_peer_associated_endpoint_closed_event();

    absl::optional<DisconnectReason> reason;
    if (event->disconnect_reason) {
      reason.emplace(event->disconnect_reason->custom_reason,
                     event->disconnect_reason->description);
    }
    return delegate_->OnPeerAssociatedEndpointClosed(event->id, reason);
  }

  if (params_ptr->input->is_flush_async()) {
    // NOTE: There's nothing to do here but let the attached pipe go out of
    // scoped and be closed. This means that the corresponding PendingFlush will
    // eventually be signalled, unblocking the endpoint which is waiting on it,
    // if any.
    return true;
  }

  if (params_ptr->input->is_pause_until_flush_completes()) {
    return delegate_->WaitForFlushToComplete(std::move(
        params_ptr->input->get_pause_until_flush_completes()->flush_pipe));
  }

  DVLOG(1) << "Unsupported command in a RunOrClosePipe message pipe control "
           << "message. Closing the pipe.";
  return false;
}

}  // namespace mojo
