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

#include "ui/views/widget/sublevel_manager.h"

#include <AppKit/AppKit.h>

#include <algorithm>
#include <memory>
#include <string>
#include <tuple>
#include <utility>

#include "base/mac/mac_util.h"
#include "build/buildflag.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/test/widget_test.h"

namespace views {

enum WidgetShowType { kShowActive, kShowInactive };

class SublevelManagerMacTest
    : public ViewsTestBase,
      public testing::WithParamInterface<
          std::tuple<WidgetShowType, Widget::InitParams::Activatable>> {
 public:
  SublevelManagerMacTest() = default;

  std::unique_ptr<Widget> CreateChildWidget(
      Widget* parent,
      ui::ZOrderLevel level,
      int sublevel,
      Widget::InitParams::Activatable activatable) {
    Widget::InitParams params = CreateParamsForTestWidget();
    params.z_order = level;
    params.sublevel = sublevel;
    params.activatable = activatable;
    params.parent = parent->GetNativeView();
    return CreateTestWidget(std::move(params));
  }

  // Call Show() or ShowInactive() depending on WidgetShowType.
  void ShowWidget(const std::unique_ptr<Widget>& widget) {
    WidgetShowType show_type = std::get<WidgetShowType>(GetParam());
    if (show_type == WidgetShowType::kShowActive) {
      widget->Show();
    } else {
      widget->ShowInactive();
    }
    test::WidgetVisibleWaiter(widget.get()).Wait();
  }

  static std::string PrintTestName(
      const ::testing::TestParamInfo<SublevelManagerMacTest::ParamType>& info) {
    std::string test_name;
    switch (std::get<WidgetShowType>(info.param)) {
      case WidgetShowType::kShowActive:
        test_name += "ShowActive";
        break;
      case WidgetShowType::kShowInactive:
        test_name += "ShowInactive";
        break;
    }
    test_name += "_";
    switch (std::get<Widget::InitParams::Activatable>(info.param)) {
      case Widget::InitParams::Activatable::kNo:
        test_name += "NotActivatable";
        break;
      case Widget::InitParams::Activatable::kYes:
        test_name += "Activatable";
        break;
      default:
        NOTREACHED();
    }
    return test_name;
  }
};

// Disabled widgets are ignored when its siblings are re-ordered.
TEST_P(SublevelManagerMacTest, ExplicitUntrack) {
  std::unique_ptr<Widget> root =
      CreateTestWidget(Widget::InitParams::CLIENT_OWNS_WIDGET);
  std::unique_ptr<Widget> root2 =
      CreateTestWidget(Widget::InitParams::CLIENT_OWNS_WIDGET);
  std::array<std::unique_ptr<Widget>, 3> children;

  ShowWidget(root);
  ShowWidget(root2);
  for (size_t i = 0; i < children.size(); i++) {
    children[i] = CreateChildWidget(
        root.get(), ui::ZOrderLevel::kNormal, i,
        std::get<Widget::InitParams::Activatable>(GetParam()));
    ShowWidget(children[i]);

    // Disable the second widget.
    if (i == 1) {
      children[i]->parent()->GetSublevelManager()->OnWidgetChildRemoved(
          children[i]->parent(), children[i].get());
    }
  }

  NSWindow* root_nswindow = root->GetNativeWindow().GetNativeNSWindow();
  NSWindow* root2_nswindow = root2->GetNativeWindow().GetNativeNSWindow();
  NSWindow* child2_nswindow =
      children[1]->GetNativeWindow().GetNativeNSWindow();

  // Reparent `child2` to root2 at the NSWindow level but not at the Widget
  // level.
  [root_nswindow removeChildWindow:child2_nswindow];
  [root2_nswindow addChildWindow:child2_nswindow ordered:NSWindowAbove];

  children[1]->GetSublevelManager()->EnsureOwnerSublevel();

  // The parent of child2 does not change.
  EXPECT_EQ([child2_nswindow parentWindow], root2_nswindow);
}

INSTANTIATE_TEST_SUITE_P(
    ,
    SublevelManagerMacTest,
    ::testing::Combine(
        ::testing::Values(WidgetShowType::kShowActive,
                          WidgetShowType::kShowInactive),
        ::testing::Values(Widget::InitParams::Activatable::kNo,
                          Widget::InitParams::Activatable::kYes)),
    SublevelManagerMacTest::PrintTestName);

}  // namespace views
