// Copyright 2014 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/core/handle_table.h"

#include <stdint.h>

#include <limits>

// #include "base/trace_event/memory_dump_manager.h"

namespace mojo {
namespace core {

namespace {

// const char* GetNameForDispatcherType(Dispatcher::Type type) {
//   switch (type) {
//     case Dispatcher::Type::UNKNOWN:
//       return "unknown";
//     case Dispatcher::Type::MESSAGE_PIPE:
//       return "message_pipe";
//     case Dispatcher::Type::DATA_PIPE_PRODUCER:
//       return "data_pipe_producer";
//     case Dispatcher::Type::DATA_PIPE_CONSUMER:
//       return "data_pipe_consumer";
//     case Dispatcher::Type::SHARED_BUFFER:
//       return "shared_buffer";
//     case Dispatcher::Type::WATCHER:
//       return "watcher";
//     case Dispatcher::Type::PLATFORM_HANDLE:
//       return "platform_handle";
//     case Dispatcher::Type::INVITATION:
//       return "invitation";
//   }
//   NOTREACHED();
//   return "unknown";
// }

}  // namespace

HandleTable::HandleTable() {}

HandleTable::~HandleTable() {}

base::Lock& HandleTable::GetLock() {
  return lock_;
}

MojoHandle HandleTable::AddDispatcher(scoped_refptr<Dispatcher> dispatcher) {
  // Oops, we're out of handles.
  if (next_available_handle_ == MOJO_HANDLE_INVALID)
    return MOJO_HANDLE_INVALID;

  MojoHandle handle = next_available_handle_++;
  auto result =
      handles_.insert(std::make_pair(handle, Entry(std::move(dispatcher))));
  DCHECK(result.second);

  return handle;
}

bool HandleTable::AddDispatchersFromTransit(
    const std::vector<Dispatcher::DispatcherInTransit>& dispatchers,
    MojoHandle* handles) {
  // Oops, we're out of handles.
  if (next_available_handle_ == MOJO_HANDLE_INVALID)
    return false;

  DCHECK_LE(dispatchers.size(), std::numeric_limits<uint32_t>::max());
  // If this insertion would cause handle overflow, we're out of handles.
  if (next_available_handle_ + dispatchers.size() < next_available_handle_)
    return false;

  for (size_t i = 0; i < dispatchers.size(); ++i) {
    MojoHandle handle = MOJO_HANDLE_INVALID;
    if (dispatchers[i].dispatcher) {
      handle = next_available_handle_++;
      auto result = handles_.insert(
          std::make_pair(handle, Entry(dispatchers[i].dispatcher)));
      DCHECK(result.second);
    }
    handles[i] = handle;
  }

  return true;
}

scoped_refptr<Dispatcher> HandleTable::GetDispatcher(MojoHandle handle) const {
  auto it = handles_.find(handle);
  if (it == handles_.end())
    return nullptr;
  return it->second.dispatcher;
}

MojoResult HandleTable::GetAndRemoveDispatcher(
    MojoHandle handle,
    scoped_refptr<Dispatcher>* dispatcher) {
  auto it = handles_.find(handle);
  if (it == handles_.end())
    return MOJO_RESULT_INVALID_ARGUMENT;
  if (it->second.busy)
    return MOJO_RESULT_BUSY;

  *dispatcher = std::move(it->second.dispatcher);
  handles_.erase(it);
  return MOJO_RESULT_OK;
}

MojoResult HandleTable::BeginTransit(
    const MojoHandle* handles,
    size_t num_handles,
    std::vector<Dispatcher::DispatcherInTransit>* dispatchers) {
  dispatchers->reserve(dispatchers->size() + num_handles);
  for (size_t i = 0; i < num_handles; ++i) {
    auto it = handles_.find(handles[i]);
    if (it == handles_.end())
      return MOJO_RESULT_INVALID_ARGUMENT;
    if (it->second.busy)
      return MOJO_RESULT_BUSY;

    Dispatcher::DispatcherInTransit d;
    d.local_handle = handles[i];
    d.dispatcher = it->second.dispatcher;
    if (!d.dispatcher->BeginTransit())
      return MOJO_RESULT_BUSY;
    it->second.busy = true;
    dispatchers->push_back(d);
  }
  return MOJO_RESULT_OK;
}

void HandleTable::CompleteTransitAndClose(
    const std::vector<Dispatcher::DispatcherInTransit>& dispatchers) {
  for (const auto& dispatcher : dispatchers) {
    auto it = handles_.find(dispatcher.local_handle);
    DCHECK(it != handles_.end() && it->second.busy);
    handles_.erase(it);
    dispatcher.dispatcher->CompleteTransitAndClose();
  }
}

void HandleTable::CancelTransit(
    const std::vector<Dispatcher::DispatcherInTransit>& dispatchers) {
  for (const auto& dispatcher : dispatchers) {
    auto it = handles_.find(dispatcher.local_handle);
    DCHECK(it != handles_.end() && it->second.busy);
    it->second.busy = false;
    dispatcher.dispatcher->CancelTransit();
  }
}

void HandleTable::GetActiveHandlesForTest(std::vector<MojoHandle>* handles) {
  handles->clear();
  for (const auto& entry : handles_)
    handles->push_back(entry.first);
}

// MemoryDumpProvider implementation.
// bool HandleTable::OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
//                                base::trace_event::ProcessMemoryDump* pmd) {
//   // Create entries for all relevant dispatcher types to ensure they are present
//   // in the final dump.
//   std::map<Dispatcher::Type, int> handle_count;
//   handle_count[Dispatcher::Type::MESSAGE_PIPE];
//   handle_count[Dispatcher::Type::DATA_PIPE_PRODUCER];
//   handle_count[Dispatcher::Type::DATA_PIPE_CONSUMER];
//   handle_count[Dispatcher::Type::SHARED_BUFFER];
//   handle_count[Dispatcher::Type::WATCHER];
//   handle_count[Dispatcher::Type::PLATFORM_HANDLE];
//   handle_count[Dispatcher::Type::INVITATION];

//   // Count the number of each dispatcher type.
//   {
//     base::AutoLock lock(GetLock());
//     for (const auto& entry : handles_) {
//       ++handle_count[entry.second.dispatcher->GetType()];
//     }
//   }

//   for (const auto& entry : handle_count) {
//     base::trace_event::MemoryAllocatorDump* inner_dump =
//         pmd->CreateAllocatorDump(std::string("mojo/") +
//                                  GetNameForDispatcherType(entry.first));
//     inner_dump->AddScalar(
//         base::trace_event::MemoryAllocatorDump::kNameObjectCount,
//         base::trace_event::MemoryAllocatorDump::kUnitsObjects, entry.second);
//   }

//   return true;
// }

HandleTable::Entry::Entry() {}

HandleTable::Entry::Entry(scoped_refptr<Dispatcher> dispatcher)
    : dispatcher(std::move(dispatcher)) {}

HandleTable::Entry::Entry(const Entry& other) = default;

HandleTable::Entry::~Entry() {}

}  // namespace core
}  // namespace mojo
