// Copyright 2013 The Flutter 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 "flutter_platform_node_delegate.h"

#include <utility>

#include "flutter/third_party/accessibility/ax/ax_action_data.h"
#include "flutter/third_party/accessibility/gfx/geometry/rect_conversions.h"

namespace flutter {

FlutterPlatformNodeDelegate::FlutterPlatformNodeDelegate() = default;

FlutterPlatformNodeDelegate::~FlutterPlatformNodeDelegate() = default;

void FlutterPlatformNodeDelegate::Init(std::weak_ptr<OwnerBridge> bridge,
                                       ui::AXNode* node) {
  bridge_ = std::move(bridge);
  ax_node_ = node;
}

ui::AXNode* FlutterPlatformNodeDelegate::GetAXNode() const {
  return ax_node_;
}

bool FlutterPlatformNodeDelegate::AccessibilityPerformAction(
    const ui::AXActionData& data) {
  AccessibilityNodeId target = ax_node_->id();
  auto bridge_ptr = bridge_.lock();
  BASE_DCHECK(bridge_ptr);
  switch (data.action) {
    case ax::mojom::Action::kDoDefault:
      bridge_ptr->DispatchAccessibilityAction(
          target, FlutterSemanticsAction::kFlutterSemanticsActionTap, {});
      return true;
    case ax::mojom::Action::kFocus:
      bridge_ptr->SetLastFocusedId(target);
      bridge_ptr->DispatchAccessibilityAction(
          target,
          FlutterSemanticsAction::
              kFlutterSemanticsActionDidGainAccessibilityFocus,
          {});
      return true;
    case ax::mojom::Action::kScrollToMakeVisible:
      bridge_ptr->DispatchAccessibilityAction(
          target, FlutterSemanticsAction::kFlutterSemanticsActionShowOnScreen,
          {});
      return true;
    // TODO(chunhtai): support more actions.
    default:
      return false;
  }
  return false;
}

const ui::AXNodeData& FlutterPlatformNodeDelegate::GetData() const {
  return ax_node_->data();
}

gfx::NativeViewAccessible FlutterPlatformNodeDelegate::GetParent() {
  if (!ax_node_->parent()) {
    return nullptr;
  }
  auto bridge_ptr = bridge_.lock();
  BASE_DCHECK(bridge_ptr);
  return bridge_ptr->GetNativeAccessibleFromId(ax_node_->parent()->id());
}

gfx::NativeViewAccessible FlutterPlatformNodeDelegate::GetFocus() {
  auto bridge_ptr = bridge_.lock();
  BASE_DCHECK(bridge_ptr);
  AccessibilityNodeId last_focused = bridge_ptr->GetLastFocusedId();
  if (last_focused == ui::AXNode::kInvalidAXID) {
    return nullptr;
  }
  return bridge_ptr->GetNativeAccessibleFromId(last_focused);
}

int FlutterPlatformNodeDelegate::GetChildCount() const {
  return static_cast<int>(ax_node_->GetUnignoredChildCount());
}

gfx::NativeViewAccessible FlutterPlatformNodeDelegate::ChildAtIndex(int index) {
  auto bridge_ptr = bridge_.lock();
  BASE_DCHECK(bridge_ptr);
  AccessibilityNodeId child = ax_node_->GetUnignoredChildAtIndex(index)->id();
  return bridge_ptr->GetNativeAccessibleFromId(child);
}

gfx::Rect FlutterPlatformNodeDelegate::GetBoundsRect(
    const ui::AXCoordinateSystem coordinate_system,
    const ui::AXClippingBehavior clipping_behavior,
    ui::AXOffscreenResult* offscreen_result) const {
  auto bridge_ptr = bridge_.lock();
  BASE_DCHECK(bridge_ptr);
  // TODO(chunhtai): We need to apply screen dpr in here.
  // https://github.com/flutter/flutter/issues/74283
  const bool clip_bounds =
      clipping_behavior == ui::AXClippingBehavior::kClipped;
  bool offscreen = false;
  gfx::RectF bounds =
      bridge_ptr->RelativeToGlobalBounds(ax_node_, offscreen, clip_bounds);
  if (offscreen_result != nullptr) {
    *offscreen_result = offscreen ? ui::AXOffscreenResult::kOffscreen
                                  : ui::AXOffscreenResult::kOnscreen;
  }
  return gfx::ToEnclosingRect(bounds);
}

std::weak_ptr<FlutterPlatformNodeDelegate::OwnerBridge>
FlutterPlatformNodeDelegate::GetOwnerBridge() const {
  return bridge_;
}

}  // namespace flutter
