// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/arc/accessibility/arc_serialization_delegate.h"

#include <memory>

#include "base/memory/raw_ptr.h"
#include "chrome/browser/ash/arc/accessibility/arc_serialization_delegate.h"
#include "chrome/test/base/chrome_ash_test_base.h"
#include "components/exo/client_controlled_shell_surface.h"
#include "components/exo/surface.h"
#include "components/exo/test/shell_surface_builder.h"
#include "components/exo/wm_helper.h"
#include "services/accessibility/android/accessibility_info_data_wrapper.h"
#include "services/accessibility/android/android_accessibility_util.h"
#include "services/accessibility/android/ax_tree_source_android.h"
#include "services/accessibility/android/test/android_accessibility_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace arc {
class TestAccessibilityInfoDataWrapper
    : public ax::android::AccessibilityInfoDataWrapper {
 public:
  explicit TestAccessibilityInfoDataWrapper(
      ax::android::AXTreeSourceAndroid* tree_source_)
      : AccessibilityInfoDataWrapper(tree_source_) {}

  // AccessibilityInfoDataWrapper overrides:
  bool IsNode() const override { return false; }
  ax::android::mojom::AccessibilityNodeInfoData* GetNode() const override {
    return nullptr;
  }
  ax::android::mojom::AccessibilityWindowInfoData* GetWindow() const override {
    return nullptr;
  }
  int32_t GetId() const override { return id_; }
  const gfx::Rect GetBounds() const override { return bounds_; }
  bool IsVisibleToUser() const override { return true; }
  bool IsWebNode() const override { return false; }
  bool IsIgnored() const override { return false; }
  bool IsImportantInAndroid() const override { return true; }
  bool IsFocusableInFullFocusMode() const override { return true; }
  bool IsAccessibilityFocusableContainer() const override { return true; }
  void PopulateAXRole(ui::AXNodeData* out_data) const override {}
  void PopulateAXState(ui::AXNodeData* out_data) const override {}
  std::string ComputeAXName(bool do_recursive) const override { return ""; }
  void GetChildren(
      std::vector<raw_ptr<AccessibilityInfoDataWrapper, VectorExperimental>>*
          children) const override {}
  int32_t GetWindowId() const override { return 1; }

  int32_t id_ = 1;
  gfx::Rect bounds_;
};

class TestTreeSource : public ax::android::AXTreeSourceAndroid {
 public:
  explicit TestTreeSource(aura::Window* window)
      : AXTreeSourceAndroid(nullptr,
                            std::make_unique<ArcSerializationDelegate>(),
                            window) {}
  ax::android::AccessibilityInfoDataWrapper* GetRoot() const override {
    return root_;
  }
  raw_ptr<ax::android::AccessibilityInfoDataWrapper> root_;
};

class ArcSerializationDelegateTest : public ChromeAshTestBase {
 public:
  ArcSerializationDelegateTest() = default;

  std::unique_ptr<exo::WMHelper> wm_helper = std::make_unique<exo::WMHelper>();
};

TEST_F(ArcSerializationDelegateTest, NonRootNodeBounds) {
  auto shell_surface = exo::test::ShellSurfaceBuilder({200, 200})
                           .SetGeometry(gfx::Rect(10, 10, 200, 200))
                           .BuildClientControlledShellSurface();

  TestTreeSource tree_source(shell_surface->GetWidget()->GetNativeWindow());
  TestAccessibilityInfoDataWrapper root(&tree_source);
  root.bounds_ = gfx::Rect(10, 10, 200, 200);
  tree_source.root_ = &root;

  TestAccessibilityInfoDataWrapper data(&tree_source);
  data.id_ = 2;
  data.bounds_ = gfx::Rect(20, 20, 30, 40);

  ui::AXNodeData out_data;
  tree_source.serialization_delegate().PopulateBounds(data, out_data);

  EXPECT_EQ(gfx::RectF(10, 10, 30, 40), out_data.relative_bounds.bounds);
}

TEST_F(ArcSerializationDelegateTest, RootNodeBounds) {
  UpdateDisplay("400x300");
  auto shell_surface = exo::test::ShellSurfaceBuilder({200, 200})
                           .SetGeometry(gfx::Rect(10, 10, 200, 200))
                           .BuildClientControlledShellSurface();

  TestTreeSource tree_source(shell_surface->GetWidget()->GetNativeWindow());
  TestAccessibilityInfoDataWrapper data(&tree_source);
  data.bounds_ = gfx::Rect(10, 10, 200, 200);
  tree_source.root_ = &data;

  ui::AXNodeData out_data;
  tree_source.serialization_delegate().PopulateBounds(data, out_data);

  EXPECT_EQ(gfx::RectF(0, 0, 200, 200), out_data.relative_bounds.bounds);
}

TEST_F(ArcSerializationDelegateTest, RootNodeBoundsOnExternalDisplay) {
  UpdateDisplay("400x300,600x500");

  auto shell_surface = exo::test::ShellSurfaceBuilder({200, 200})
                           .SetGeometry(gfx::Rect(410, 10, 200, 200))
                           .BuildClientControlledShellSurface();

  TestTreeSource tree_source(shell_surface->GetWidget()->GetNativeWindow());
  TestAccessibilityInfoDataWrapper data(&tree_source);
  data.bounds_ = gfx::Rect(10, 10, 200, 200);
  tree_source.root_ = &data;

  ui::AXNodeData out_data;
  tree_source.serialization_delegate().PopulateBounds(data, out_data);

  EXPECT_EQ(gfx::RectF(0, 0, 200, 200), out_data.relative_bounds.bounds);
}

TEST_F(ArcSerializationDelegateTest, BoundsScalingPiArc) {
  UpdateDisplay("400x300*2");  // 2x device scale factor.

  // With default_scale_cancellation, Android has default (1x) scale factor.
  wm_helper->SetDefaultScaleCancellation(true);
  auto shell_surface = exo::test::ShellSurfaceBuilder()
                           .SetGeometry(gfx::Rect(10, 10, 100, 100))
                           .EnableDefaultScaleCancellation()
                           .BuildClientControlledShellSurface();

  TestTreeSource tree_source(shell_surface->GetWidget()->GetNativeWindow());
  TestAccessibilityInfoDataWrapper data(&tree_source);
  data.bounds_ = gfx::Rect(10, 10, 100, 100);  // PX in Android.
  tree_source.root_ = &data;

  ui::AXNodeData out_data;
  tree_source.serialization_delegate().PopulateBounds(data, out_data);

  EXPECT_EQ(gfx::RectF(0, 0, 200, 200), out_data.relative_bounds.bounds);
}

TEST_F(ArcSerializationDelegateTest, BoundsScalingFromRvcArcAndLater) {
  UpdateDisplay("400x300*2");  // 2x device scale factor.

  // Without default_scale_cancellation, Android use the same (2x) scale factor.
  wm_helper->SetDefaultScaleCancellation(false);
  auto shell_surface = exo::test::ShellSurfaceBuilder({200, 200})
                           .SetGeometry(gfx::Rect(10, 10, 200, 200))
                           .BuildClientControlledShellSurface();

  TestTreeSource tree_source(shell_surface->GetWidget()->GetNativeWindow());
  TestAccessibilityInfoDataWrapper data(&tree_source);
  data.bounds_ = gfx::Rect(20, 20, 200, 200);  // PX in Android.
  tree_source.root_ = &data;

  ui::AXNodeData out_data;
  tree_source.serialization_delegate().PopulateBounds(data, out_data);

  EXPECT_EQ(gfx::RectF(0, 0, 200, 200), out_data.relative_bounds.bounds);
}

}  // namespace arc
