// Copyright 2016 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 "ui/ozone/platform/drm/mus_thread_proxy.h"

#include "base/bind.h"
#include "base/single_thread_task_runner.h"
#include "base/task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/ozone/platform/drm/gpu/drm_thread.h"
#include "ui/ozone/platform/drm/gpu/proxy_helpers.h"
#include "ui/ozone/platform/drm/host/drm_display_host_manager.h"
#include "ui/ozone/platform/drm/host/drm_overlay_manager.h"

namespace ui {

CursorProxyThread::CursorProxyThread(MusThreadProxy* mus_thread_proxy)
    : mus_thread_proxy_(mus_thread_proxy) {}
CursorProxyThread::~CursorProxyThread() {}

void CursorProxyThread::CursorSet(gfx::AcceleratedWidget window,
                                  const std::vector<SkBitmap>& bitmaps,
                                  const gfx::Point& point,
                                  int frame_delay_ms) {
  mus_thread_proxy_->CursorSet(window, bitmaps, point, frame_delay_ms);
}
void CursorProxyThread::Move(gfx::AcceleratedWidget window,
                             const gfx::Point& point) {
  mus_thread_proxy_->Move(window, point);
}
void CursorProxyThread::InitializeOnEvdev() {
  mus_thread_proxy_->InitializeOnEvdev();
}

MusThreadProxy::MusThreadProxy()
    : ws_task_runner_(base::ThreadTaskRunnerHandle::Get()),
      drm_thread_(nullptr),
      weak_ptr_factory_(this) {}

void MusThreadProxy::InitializeOnEvdev() {}

MusThreadProxy::~MusThreadProxy() {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  for (GpuThreadObserver& observer : gpu_thread_observers_)
    observer.OnGpuThreadRetired();
}

// This is configured on the GPU thread.
void MusThreadProxy::SetDrmThread(DrmThread* thread) {
  base::AutoLock acquire(lock_);
  drm_thread_ = thread;
}

void MusThreadProxy::ProvideManagers(DrmDisplayHostManager* display_manager,
                                     DrmOverlayManager* overlay_manager) {
  display_manager_ = display_manager;
  overlay_manager_ = overlay_manager;
}

void MusThreadProxy::StartDrmThread() {
  DCHECK(drm_thread_);
  drm_thread_->Start();

  drm_thread_->task_runner()->PostTask(
      FROM_HERE, base::Bind(&MusThreadProxy::DispatchObserversFromDrmThread,
                            base::Unretained(this)));
}

void MusThreadProxy::DispatchObserversFromDrmThread() {
  ws_task_runner_->PostTask(FROM_HERE, base::Bind(&MusThreadProxy::RunObservers,
                                                  base::Unretained(this)));
}

void MusThreadProxy::RunObservers() {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  for (GpuThreadObserver& observer : gpu_thread_observers_)
    observer.OnGpuThreadReady();
}

void MusThreadProxy::AddGpuThreadObserver(GpuThreadObserver* observer) {
  DCHECK(on_window_server_thread_.CalledOnValidThread());

  gpu_thread_observers_.AddObserver(observer);
  if (IsConnected())
    observer->OnGpuThreadReady();
}

void MusThreadProxy::RemoveGpuThreadObserver(GpuThreadObserver* observer) {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  gpu_thread_observers_.RemoveObserver(observer);
}

bool MusThreadProxy::IsConnected() {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  base::AutoLock acquire(lock_);
  if (drm_thread_)
    return drm_thread_->IsRunning();
  return false;
}

// Services needed for DrmDisplayHostMananger.
void MusThreadProxy::RegisterHandlerForDrmDisplayHostManager(
    DrmDisplayHostManager* handler) {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  display_manager_ = handler;
}

void MusThreadProxy::UnRegisterHandlerForDrmDisplayHostManager() {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  display_manager_ = nullptr;
}

bool MusThreadProxy::GpuCreateWindow(gfx::AcceleratedWidget widget) {
  DCHECK(drm_thread_->IsRunning());
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE, base::Bind(&DrmThread::CreateWindow,
                            base::Unretained(drm_thread_), widget));
  return true;
}

bool MusThreadProxy::GpuDestroyWindow(gfx::AcceleratedWidget widget) {
  DCHECK(drm_thread_->IsRunning());
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE, base::Bind(&DrmThread::DestroyWindow,
                            base::Unretained(drm_thread_), widget));
  return true;
}

bool MusThreadProxy::GpuWindowBoundsChanged(gfx::AcceleratedWidget widget,
                                            const gfx::Rect& bounds) {
  DCHECK(drm_thread_->IsRunning());
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE, base::Bind(&DrmThread::SetWindowBounds,
                            base::Unretained(drm_thread_), widget, bounds));
  return true;
}

void MusThreadProxy::CursorSet(gfx::AcceleratedWidget widget,
                               const std::vector<SkBitmap>& bitmaps,
                               const gfx::Point& location,
                               int frame_delay_ms) {
  DCHECK(drm_thread_->IsRunning());
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE,
      base::Bind(&DrmThread::SetCursor, base::Unretained(drm_thread_), widget,
                 bitmaps, location, frame_delay_ms));
}

void MusThreadProxy::Move(gfx::AcceleratedWidget widget,
                          const gfx::Point& location) {
  // NOTE: Input events skip the main thread to avoid jank.
  DCHECK(drm_thread_->IsRunning());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE, base::Bind(&DrmThread::MoveCursor,
                            base::Unretained(drm_thread_), widget, location));
}

// Services needed for DrmOverlayManager.
void MusThreadProxy::RegisterHandlerForDrmOverlayManager(
    DrmOverlayManager* handler) {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  overlay_manager_ = handler;
}

void MusThreadProxy::UnRegisterHandlerForDrmOverlayManager() {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  overlay_manager_ = nullptr;
}

bool MusThreadProxy::GpuCheckOverlayCapabilities(
    gfx::AcceleratedWidget widget,
    const std::vector<OverlayCheck_Params>& overlays) {
  DCHECK(drm_thread_->IsRunning());
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  auto callback =
      base::Bind(&MusThreadProxy::GpuCheckOverlayCapabilitiesCallback,
                 weak_ptr_factory_.GetWeakPtr());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE, base::Bind(&DrmThread::CheckOverlayCapabilities,
                            base::Unretained(drm_thread_), widget, overlays,
                            CreateSafeCallback(callback)));
  return true;
}

bool MusThreadProxy::GpuRefreshNativeDisplays() {
  DCHECK(drm_thread_->IsRunning());
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  auto callback = base::Bind(&MusThreadProxy::GpuRefreshNativeDisplaysCallback,
                             weak_ptr_factory_.GetWeakPtr());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE,
      base::Bind(&DrmThread::RefreshNativeDisplays,
                 base::Unretained(drm_thread_), CreateSafeCallback(callback)));
  return true;
}

bool MusThreadProxy::GpuConfigureNativeDisplay(int64_t id,
                                               const DisplayMode_Params& mode,
                                               const gfx::Point& origin) {
  DCHECK(drm_thread_->IsRunning());
  DCHECK(on_window_server_thread_.CalledOnValidThread());

  auto callback = base::Bind(&MusThreadProxy::GpuConfigureNativeDisplayCallback,
                             weak_ptr_factory_.GetWeakPtr());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE, base::Bind(&DrmThread::ConfigureNativeDisplay,
                            base::Unretained(drm_thread_), id, mode, origin,
                            CreateSafeCallback(callback)));
  return true;
}

bool MusThreadProxy::GpuDisableNativeDisplay(int64_t id) {
  DCHECK(drm_thread_->IsRunning());
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  auto callback = base::Bind(&MusThreadProxy::GpuDisableNativeDisplayCallback,
                             weak_ptr_factory_.GetWeakPtr());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE, base::Bind(&DrmThread::DisableNativeDisplay,
                            base::Unretained(drm_thread_), id,
                            CreateSafeCallback(callback)));
  return true;
}

bool MusThreadProxy::GpuTakeDisplayControl() {
  DCHECK(drm_thread_->IsRunning());
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  auto callback = base::Bind(&MusThreadProxy::GpuTakeDisplayControlCallback,
                             weak_ptr_factory_.GetWeakPtr());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE,
      base::Bind(&DrmThread::TakeDisplayControl, base::Unretained(drm_thread_),
                 CreateSafeCallback(callback)));
  return true;
}

bool MusThreadProxy::GpuRelinquishDisplayControl() {
  DCHECK(drm_thread_->IsRunning());
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  auto callback =
      base::Bind(&MusThreadProxy::GpuRelinquishDisplayControlCallback,
                 weak_ptr_factory_.GetWeakPtr());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE,
      base::Bind(&DrmThread::RelinquishDisplayControl,
                 base::Unretained(drm_thread_), CreateSafeCallback(callback)));
  return true;
}

bool MusThreadProxy::GpuAddGraphicsDevice(const base::FilePath& path,
                                          const base::FileDescriptor& fd) {
  DCHECK(drm_thread_->IsRunning());
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE, base::Bind(&DrmThread::AddGraphicsDevice,
                            base::Unretained(drm_thread_), path, fd));
  return true;
}

bool MusThreadProxy::GpuRemoveGraphicsDevice(const base::FilePath& path) {
  DCHECK(drm_thread_->IsRunning());
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE, base::Bind(&DrmThread::RemoveGraphicsDevice,
                            base::Unretained(drm_thread_), path));
  return true;
}

bool MusThreadProxy::GpuGetHDCPState(int64_t display_id) {
  DCHECK(drm_thread_->IsRunning());
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  auto callback = base::Bind(&MusThreadProxy::GpuGetHDCPStateCallback,
                             weak_ptr_factory_.GetWeakPtr());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE,
      base::Bind(&DrmThread::GetHDCPState, base::Unretained(drm_thread_),
                 display_id, CreateSafeCallback(callback)));
  return true;
}

bool MusThreadProxy::GpuSetHDCPState(int64_t display_id, HDCPState state) {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  DCHECK(drm_thread_->IsRunning());
  auto callback = base::Bind(&MusThreadProxy::GpuSetHDCPStateCallback,
                             weak_ptr_factory_.GetWeakPtr());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE,
      base::Bind(&DrmThread::SetHDCPState, base::Unretained(drm_thread_),
                 display_id, state, CreateSafeCallback(callback)));
  return true;
}

bool MusThreadProxy::GpuSetColorCorrection(
    int64_t id,
    const std::vector<GammaRampRGBEntry>& degamma_lut,
    const std::vector<GammaRampRGBEntry>& gamma_lut,
    const std::vector<float>& correction_matrix) {
  DCHECK(drm_thread_->IsRunning());
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  drm_thread_->task_runner()->PostTask(
      FROM_HERE,
      base::Bind(&DrmThread::SetColorCorrection, base::Unretained(drm_thread_),
                 id, degamma_lut, gamma_lut, correction_matrix));
  return true;
}

void MusThreadProxy::GpuCheckOverlayCapabilitiesCallback(
    gfx::AcceleratedWidget widget,
    const std::vector<OverlayCheck_Params>& overlays) const {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  overlay_manager_->GpuSentOverlayResult(widget, overlays);
}

void MusThreadProxy::GpuConfigureNativeDisplayCallback(int64_t display_id,
                                                       bool success) const {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  display_manager_->GpuConfiguredDisplay(display_id, success);
}

void MusThreadProxy::GpuRefreshNativeDisplaysCallback(
    const std::vector<DisplaySnapshot_Params>& displays) const {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  display_manager_->GpuHasUpdatedNativeDisplays(displays);
}

void MusThreadProxy::GpuDisableNativeDisplayCallback(int64_t display_id,
                                                     bool success) const {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  display_manager_->GpuConfiguredDisplay(display_id, success);
}

void MusThreadProxy::GpuTakeDisplayControlCallback(bool success) const {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  display_manager_->GpuTookDisplayControl(success);
}

void MusThreadProxy::GpuRelinquishDisplayControlCallback(bool success) const {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  display_manager_->GpuRelinquishedDisplayControl(success);
}

void MusThreadProxy::GpuGetHDCPStateCallback(int64_t display_id,
                                             bool success,
                                             HDCPState state) const {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  display_manager_->GpuReceivedHDCPState(display_id, success, state);
}

void MusThreadProxy::GpuSetHDCPStateCallback(int64_t display_id,
                                             bool success) const {
  DCHECK(on_window_server_thread_.CalledOnValidThread());
  display_manager_->GpuUpdatedHDCPState(display_id, success);
}

}  // namespace ui
