// Copyright 2018 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/views_bridge_mac/bridge_factory_impl.h"

#include "base/no_destructor.h"
#include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
#include "ui/base/cocoa/remote_accessibility_api.h"
#include "ui/views_bridge_mac/alert.h"
#include "ui/views_bridge_mac/bridged_native_widget_host_helper.h"
#include "ui/views_bridge_mac/bridged_native_widget_impl.h"

namespace views_bridge_mac {

using views::BridgedNativeWidgetImpl;
using views::BridgedNativeWidgetHostHelper;

namespace {

class Bridge : public BridgedNativeWidgetHostHelper {
 public:
  Bridge(uint64_t bridge_id,
         mojom::BridgedNativeWidgetAssociatedRequest bridge_request,
         mojom::BridgedNativeWidgetHostAssociatedPtrInfo host_ptr,
         mojom::TextInputHostAssociatedPtrInfo text_input_host_ptr) {
    host_ptr_.Bind(std::move(host_ptr),
                   ui::WindowResizeHelperMac::Get()->task_runner());
    text_input_host_ptr_.Bind(std::move(text_input_host_ptr),
                              ui::WindowResizeHelperMac::Get()->task_runner());
    bridge_impl_ = std::make_unique<BridgedNativeWidgetImpl>(
        bridge_id, host_ptr_.get(), this, text_input_host_ptr_.get());
    bridge_impl_->BindRequest(
        std::move(bridge_request),
        base::BindOnce(&Bridge::OnConnectionError, base::Unretained(this)));
  }

 private:
  ~Bridge() override {}

  void OnConnectionError() { delete this; }

  // BridgedNativeWidgetHostHelper:
  id GetNativeViewAccessible() override {
    if (!remote_accessibility_element_) {
      int64_t browser_pid = 0;
      std::vector<uint8_t> element_token;
      host_ptr_->GetRootViewAccessibilityToken(&browser_pid, &element_token);
      [NSAccessibilityRemoteUIElement
          registerRemoteUIProcessIdentifier:browser_pid];
      remote_accessibility_element_ =
          ui::RemoteAccessibility::GetRemoteElementFromToken(element_token);
    }
    return remote_accessibility_element_.get();
  }
  void DispatchKeyEvent(ui::KeyEvent* event) override {
    bool event_handled = false;
    host_ptr_->DispatchKeyEventRemote(std::make_unique<ui::KeyEvent>(*event),
                                      &event_handled);
    if (event_handled)
      event->SetHandled();
  }
  bool DispatchKeyEventToMenuController(ui::KeyEvent* event) override {
    bool event_swallowed = false;
    bool event_handled = false;
    host_ptr_->DispatchKeyEventToMenuControllerRemote(
        std::make_unique<ui::KeyEvent>(*event), &event_swallowed,
        &event_handled);
    if (event_handled)
      event->SetHandled();
    return event_swallowed;
  }
  void GetWordAt(const gfx::Point& location_in_content,
                 bool* found_word,
                 gfx::DecoratedText* decorated_word,
                 gfx::Point* baseline_point) override {
    *found_word = false;
  }
  views_bridge_mac::DragDropClient* GetDragDropClient() override {
    // Drag-drop only doesn't work across mojo yet.
    return nullptr;
  }
  ui::TextInputClient* GetTextInputClient() override {
    // Text input doesn't work across mojo yet.
    return nullptr;
  }

  mojom::BridgedNativeWidgetHostAssociatedPtr host_ptr_;
  mojom::TextInputHostAssociatedPtr text_input_host_ptr_;

  std::unique_ptr<BridgedNativeWidgetImpl> bridge_impl_;
  base::scoped_nsobject<NSAccessibilityRemoteUIElement>
      remote_accessibility_element_;
};

}  // namespace

// static
BridgeFactoryImpl* BridgeFactoryImpl::Get() {
  static base::NoDestructor<BridgeFactoryImpl> factory;
  return factory.get();
}

void BridgeFactoryImpl::BindRequest(
    mojom::BridgeFactoryAssociatedRequest request) {
  binding_.Bind(std::move(request),
                ui::WindowResizeHelperMac::Get()->task_runner());
}

void BridgeFactoryImpl::CreateAlert(mojom::AlertBridgeRequest bridge_request) {
  // The resulting object manages its own lifetime.
  ignore_result(new AlertBridge(std::move(bridge_request)));
}

void BridgeFactoryImpl::CreateBridgedNativeWidget(
    uint64_t bridge_id,
    mojom::BridgedNativeWidgetAssociatedRequest bridge_request,
    mojom::BridgedNativeWidgetHostAssociatedPtrInfo host,
    mojom::TextInputHostAssociatedPtrInfo text_input_host) {
  // The resulting object will be destroyed when its message pipe is closed.
  ignore_result(new Bridge(bridge_id, std::move(bridge_request),
                           std::move(host), std::move(text_input_host)));
}

BridgeFactoryImpl::BridgeFactoryImpl() : binding_(this) {}

BridgeFactoryImpl::~BridgeFactoryImpl() {}

}  // namespace views_bridge_mac
