#include "display_service.h"

#include <unistd.h>

#include <algorithm>
#include <sstream>
#include <string>
#include <vector>

#include <android-base/file.h>
#include <android-base/properties.h>
#include <dvr/dvr_display_types.h>
#include <pdx/default_transport/service_endpoint.h>
#include <pdx/rpc/remote_method.h>
#include <private/android_filesystem_config.h>
#include <private/dvr/display_protocol.h>
#include <private/dvr/numeric.h>
#include <private/dvr/trusted_uids.h>
#include <private/dvr/types.h>

#include "DisplayHardware/DisplayIdentification.h"

using android::dvr::display::DisplayProtocol;
using android::pdx::Channel;
using android::pdx::ErrorStatus;
using android::pdx::Message;
using android::pdx::Status;
using android::pdx::default_transport::Endpoint;
using android::pdx::rpc::DispatchRemoteMethod;

namespace {

const char kDvrLensMetricsProperty[] = "ro.dvr.lens_metrics";
const char kDvrDeviceMetricsProperty[] = "ro.dvr.device_metrics";
const char kDvrDeviceConfigProperty[] = "ro.dvr.device_configuration";

}  // namespace

namespace android {
namespace dvr {

DisplayService::DisplayService(Hwc2::Composer* hidl,
                               hwc2_display_t primary_display_id,
                               RequestDisplayCallback request_display_callback)
    : BASE("DisplayService",
           Endpoint::Create(display::DisplayProtocol::kClientPath)) {
    hardware_composer_.Initialize(
        hidl, primary_display_id, request_display_callback);
}

bool DisplayService::IsInitialized() const {
  return BASE::IsInitialized() && hardware_composer_.IsInitialized();
}

std::string DisplayService::DumpState(size_t /*max_length*/) {
  std::ostringstream stream;

  auto surfaces = GetDisplaySurfaces();
  std::sort(surfaces.begin(), surfaces.end(), [](const auto& a, const auto& b) {
    return a->surface_id() < b->surface_id();
  });

  stream << "Application Surfaces:" << std::endl;

  size_t count = 0;
  for (const auto& surface : surfaces) {
    if (surface->surface_type() == SurfaceType::Application) {
      stream << "Surface " << count++ << ":";
      stream << " surface_id=" << surface->surface_id()
             << " process_id=" << surface->process_id()
             << " user_id=" << surface->user_id()
             << " visible=" << surface->visible()
             << " z_order=" << surface->z_order();

      stream << " queue_ids=";
      auto queue_ids = surface->GetQueueIds();
      std::sort(queue_ids.begin(), queue_ids.end());
      for (int32_t id : queue_ids) {
        if (id != queue_ids[0])
          stream << ",";
        stream << id;
      }
      stream << std::endl;
    }
  }
  stream << std::endl;

  stream << "Direct Surfaces:" << std::endl;

  count = 0;
  for (const auto& surface : surfaces) {
    if (surface->surface_type() == SurfaceType::Direct) {
      stream << "Surface " << count++ << ":";
      stream << " surface_id=" << surface->surface_id()
             << " process_id=" << surface->process_id()
             << " user_id=" << surface->user_id()
             << " visible=" << surface->visible()
             << " z_order=" << surface->z_order();

      stream << " queue_ids=";
      auto queue_ids = surface->GetQueueIds();
      std::sort(queue_ids.begin(), queue_ids.end());
      for (int32_t id : queue_ids) {
        if (id != queue_ids[0])
          stream << ",";
        stream << id;
      }
      stream << std::endl;
    }
  }
  stream << std::endl;

  stream << hardware_composer_.Dump();
  return stream.str();
}

void DisplayService::OnChannelClose(pdx::Message& message,
                                    const std::shared_ptr<Channel>& channel) {
  if (auto surface = std::static_pointer_cast<DisplaySurface>(channel)) {
    surface->OnSetAttributes(message,
                             {{display::SurfaceAttribute::Visible,
                               display::SurfaceAttributeValue{false}}});
  }
}

// First-level dispatch for display service messages. Directly handles messages
// that are independent of the display surface (metrics, creation) and routes
// surface-specific messages to the per-instance handlers.
Status<void> DisplayService::HandleMessage(pdx::Message& message) {
  ALOGD_IF(TRACE, "DisplayService::HandleMessage: opcode=%d", message.GetOp());
  ATRACE_NAME("DisplayService::HandleMessage");

  switch (message.GetOp()) {
    case DisplayProtocol::GetMetrics::Opcode:
      DispatchRemoteMethod<DisplayProtocol::GetMetrics>(
          *this, &DisplayService::OnGetMetrics, message);
      return {};

    case DisplayProtocol::GetConfigurationData::Opcode:
      DispatchRemoteMethod<DisplayProtocol::GetConfigurationData>(
          *this, &DisplayService::OnGetConfigurationData, message);
      return {};

    case DisplayProtocol::GetDisplayIdentificationPort::Opcode:
      DispatchRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>(
          *this, &DisplayService::OnGetDisplayIdentificationPort, message);
      return {};

    case DisplayProtocol::CreateSurface::Opcode:
      DispatchRemoteMethod<DisplayProtocol::CreateSurface>(
          *this, &DisplayService::OnCreateSurface, message);
      return {};

    case DisplayProtocol::SetupGlobalBuffer::Opcode:
      DispatchRemoteMethod<DisplayProtocol::SetupGlobalBuffer>(
          *this, &DisplayService::OnSetupGlobalBuffer, message);
      return {};

    case DisplayProtocol::DeleteGlobalBuffer::Opcode:
      DispatchRemoteMethod<DisplayProtocol::DeleteGlobalBuffer>(
          *this, &DisplayService::OnDeleteGlobalBuffer, message);
      return {};

    case DisplayProtocol::GetGlobalBuffer::Opcode:
      DispatchRemoteMethod<DisplayProtocol::GetGlobalBuffer>(
          *this, &DisplayService::OnGetGlobalBuffer, message);
      return {};

    case DisplayProtocol::IsVrAppRunning::Opcode:
      DispatchRemoteMethod<DisplayProtocol::IsVrAppRunning>(
          *this, &DisplayService::IsVrAppRunning, message);
      return {};

    // Direct the surface specific messages to the surface instance.
    case DisplayProtocol::SetAttributes::Opcode:
    case DisplayProtocol::CreateQueue::Opcode:
    case DisplayProtocol::GetSurfaceInfo::Opcode:
      return HandleSurfaceMessage(message);

    default:
      return Service::HandleMessage(message);
  }
}

Status<display::Metrics> DisplayService::OnGetMetrics(
    pdx::Message& /*message*/) {
  const auto& params = hardware_composer_.GetPrimaryDisplayParams();
  return {{static_cast<uint32_t>(params.width),
           static_cast<uint32_t>(params.height),
           static_cast<uint32_t>(params.dpi.x),
           static_cast<uint32_t>(params.dpi.y),
           static_cast<uint32_t>(params.vsync_period_ns),
           0,
           0,
           0,
           0.0,
           {},
           {}}};
}

pdx::Status<std::string> DisplayService::OnGetConfigurationData(
    pdx::Message& /*message*/, display::ConfigFileType config_type) {
  std::string property_name;
  DisplayIdentificationData display_identification_data;
  switch (config_type) {
    case display::ConfigFileType::kLensMetrics:
      property_name = kDvrLensMetricsProperty;
      break;
    case display::ConfigFileType::kDeviceMetrics:
      property_name = kDvrDeviceMetricsProperty;
      break;
    case display::ConfigFileType::kDeviceConfiguration:
      property_name = kDvrDeviceConfigProperty;
      break;
    case display::ConfigFileType::kDeviceEdid:
      display_identification_data =
          hardware_composer_.GetCurrentDisplayIdentificationData();
      if (display_identification_data.size() == 0) {
        return ErrorStatus(ENOENT);
      }
      return std::string(display_identification_data.begin(),
                         display_identification_data.end());
    default:
      return ErrorStatus(EINVAL);
  }
  std::string file_path = base::GetProperty(property_name, "");
  if (file_path.empty()) {
    return ErrorStatus(ENOENT);
  }

  std::string data;
  if (!base::ReadFileToString(file_path, &data)) {
    return ErrorStatus(errno);
  }

  return std::move(data);
}

pdx::Status<uint8_t> DisplayService::OnGetDisplayIdentificationPort(
    pdx::Message& /*message*/) {
  return hardware_composer_.GetCurrentDisplayPort();
}

// Creates a new DisplaySurface and associates it with this channel. This may
// only be done once per channel.
Status<display::SurfaceInfo> DisplayService::OnCreateSurface(
    pdx::Message& message, const display::SurfaceAttributes& attributes) {
  // A surface may only be created once per channel.
  if (message.GetChannel())
    return ErrorStatus(EINVAL);

  ALOGI_IF(TRACE, "DisplayService::OnCreateSurface: cid=%d",
           message.GetChannelId());

  // Use the channel id as the unique surface id.
  const int surface_id = message.GetChannelId();
  const int process_id = message.GetProcessId();
  const int user_id = message.GetEffectiveUserId();

  ALOGI_IF(TRACE,
           "DisplayService::OnCreateSurface: surface_id=%d process_id=%d",
           surface_id, process_id);

  auto surface_status =
      DisplaySurface::Create(this, surface_id, process_id, user_id, attributes);
  if (!surface_status) {
    ALOGE("DisplayService::OnCreateSurface: Failed to create surface: %s",
          surface_status.GetErrorMessage().c_str());
    return ErrorStatus(surface_status.error());
  }
  auto surface = surface_status.take();
  message.SetChannel(surface);

  // Update the surface with the attributes supplied with the create call. For
  // application surfaces this has the side effect of notifying the display
  // manager of the new surface. For direct surfaces, this may trigger a mode
  // change, depending on the value of the visible attribute.
  surface->OnSetAttributes(message, attributes);

  return {{surface->surface_id(), surface->visible(), surface->z_order()}};
}

void DisplayService::SurfaceUpdated(SurfaceType surface_type,
                                    display::SurfaceUpdateFlags update_flags) {
  ALOGD_IF(TRACE, "DisplayService::SurfaceUpdated: update_flags=%x",
           update_flags.value());
  if (update_flags.value() != 0) {
    if (surface_type == SurfaceType::Application)
      NotifyDisplayConfigurationUpdate();
    else
      UpdateActiveDisplaySurfaces();
  }
}

pdx::Status<BorrowedNativeBufferHandle> DisplayService::OnSetupGlobalBuffer(
    pdx::Message& message, DvrGlobalBufferKey key, size_t size,
    uint64_t usage) {
  const int user_id = message.GetEffectiveUserId();
  const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);

  if (!trusted) {
    ALOGE(
        "DisplayService::OnSetupGlobalBuffer: Permission denied for user_id=%d",
        user_id);
    return ErrorStatus(EPERM);
  }
  return SetupGlobalBuffer(key, size, usage);
}

pdx::Status<void> DisplayService::OnDeleteGlobalBuffer(pdx::Message& message,
                                                       DvrGlobalBufferKey key) {
  const int user_id = message.GetEffectiveUserId();
  const bool trusted = (user_id == AID_ROOT) || IsTrustedUid(user_id);

  if (!trusted) {
    ALOGE(
        "DisplayService::OnDeleteGlobalBuffer: Permission denied for "
        "user_id=%d",
        user_id);
    return ErrorStatus(EPERM);
  }
  return DeleteGlobalBuffer(key);
}

pdx::Status<BorrowedNativeBufferHandle> DisplayService::OnGetGlobalBuffer(
    pdx::Message& /* message */, DvrGlobalBufferKey key) {
  ALOGD_IF(TRACE, "DisplayService::OnGetGlobalBuffer: key=%d", key);
  auto global_buffer = global_buffers_.find(key);
  if (global_buffer != global_buffers_.end())
    return {BorrowedNativeBufferHandle(*global_buffer->second, 0)};
  else
    return pdx::ErrorStatus(EINVAL);
}

// Calls the message handler for the DisplaySurface associated with this
// channel.
Status<void> DisplayService::HandleSurfaceMessage(pdx::Message& message) {
  auto surface = std::static_pointer_cast<DisplaySurface>(message.GetChannel());
  ALOGW_IF(!surface,
           "DisplayService::HandleSurfaceMessage: surface is nullptr!");

  if (surface)
    return surface->HandleMessage(message);
  else
    return ErrorStatus(EINVAL);
}

std::shared_ptr<DisplaySurface> DisplayService::GetDisplaySurface(
    int surface_id) const {
  return std::static_pointer_cast<DisplaySurface>(GetChannel(surface_id));
}

std::vector<std::shared_ptr<DisplaySurface>>
DisplayService::GetDisplaySurfaces() const {
  return GetChannels<DisplaySurface>();
}

std::vector<std::shared_ptr<DirectDisplaySurface>>
DisplayService::GetVisibleDisplaySurfaces() const {
  std::vector<std::shared_ptr<DirectDisplaySurface>> visible_surfaces;

  ForEachDisplaySurface(
      SurfaceType::Direct,
      [&](const std::shared_ptr<DisplaySurface>& surface) mutable {
        if (surface->visible()) {
          visible_surfaces.push_back(
              std::static_pointer_cast<DirectDisplaySurface>(surface));
          surface->ClearUpdate();
        }
      });

  return visible_surfaces;
}

void DisplayService::UpdateActiveDisplaySurfaces() {
  auto visible_surfaces = GetVisibleDisplaySurfaces();
  ALOGD_IF(TRACE,
           "DisplayService::UpdateActiveDisplaySurfaces: %zd visible surfaces",
           visible_surfaces.size());
  hardware_composer_.SetDisplaySurfaces(std::move(visible_surfaces));
}

pdx::Status<BorrowedNativeBufferHandle> DisplayService::SetupGlobalBuffer(
    DvrGlobalBufferKey key, size_t size, uint64_t usage) {
  auto global_buffer = global_buffers_.find(key);
  if (global_buffer == global_buffers_.end()) {
    auto ion_buffer = std::make_unique<IonBuffer>(static_cast<int>(size), 1,
                                                  HAL_PIXEL_FORMAT_BLOB, usage);

    // Some buffers are used internally. If they were configured with an
    // invalid size or format, this will fail.
    int result = hardware_composer_.OnNewGlobalBuffer(key, *ion_buffer.get());
    if (result < 0)
      return ErrorStatus(result);
    global_buffer =
        global_buffers_.insert(std::make_pair(key, std::move(ion_buffer)))
            .first;
  }

  return {BorrowedNativeBufferHandle(*global_buffer->second, 0)};
}

pdx::Status<void> DisplayService::DeleteGlobalBuffer(DvrGlobalBufferKey key) {
  auto global_buffer = global_buffers_.find(key);
  if (global_buffer != global_buffers_.end()) {
    // Some buffers are used internally.
    hardware_composer_.OnDeletedGlobalBuffer(key);
    global_buffers_.erase(global_buffer);
  }

  return {0};
}

void DisplayService::SetDisplayConfigurationUpdateNotifier(
    DisplayConfigurationUpdateNotifier update_notifier) {
  update_notifier_ = update_notifier;
}

void DisplayService::NotifyDisplayConfigurationUpdate() {
  if (update_notifier_)
    update_notifier_();
}

Status<bool> DisplayService::IsVrAppRunning(pdx::Message& /*message*/) {
  bool visible = false;
  ForEachDisplaySurface(
      SurfaceType::Application,
      [&visible](const std::shared_ptr<DisplaySurface>& surface) {
        if (surface->visible())
          visible = true;
      });

  return {visible};
}

}  // namespace dvr
}  // namespace android
