// 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 "chrome/browser/ash/arc/input_overlay/test/test_utils.h"

#include <cstddef>

#include "ash/public/cpp/window_properties.h"
#include "base/test/task_environment.h"
#include "chrome/browser/ash/app_list/arc/arc_app_test.h"
#include "chrome/browser/ash/arc/input_overlay/actions/action.h"
#include "chrome/browser/ash/arc/input_overlay/arc_input_overlay_metrics.h"
#include "chrome/browser/ash/arc/input_overlay/touch_injector.h"
#include "chromeos/ash/experiences/arc/test/fake_app_instance.h"
#include "chromeos/strings/grit/chromeos_strings.h"
#include "chromeos/ui/base/app_types.h"
#include "chromeos/ui/base/window_properties.h"
#include "components/ukm/test_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"

namespace arc::input_overlay {

std::unique_ptr<views::Widget> CreateArcWindow(
    aura::Window* root_window,
    const gfx::Rect& bounds,
    const std::string& package_name) {
  views::Widget::InitParams params(
      views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET,
      views::Widget::InitParams::TYPE_WINDOW);
  params.bounds = bounds;
  params.context = root_window;
  // `chromeos::kAppTypeKey` property should be assigned before widget init.
  // It simulates the situation that
  // `AppServiceAppWindowShelfController::OnWindowInitialized()` is called
  // before `ArcInputOverlayManager::OnWindowInitialized()`;
  params.init_properties_container.SetProperty(chromeos::kAppTypeKey,
                                               chromeos::AppType::ARC_APP);
  auto widget = std::make_unique<views::Widget>();
  widget->Init(std::move(params));
  widget->widget_delegate()->SetCanResize(true);
  widget->GetNativeWindow()->SetProperty(ash::kAppIDKey, std::string("app_id"));
  widget->GetNativeWindow()->SetProperty(ash::kArcPackageNameKey, package_name);
  widget->Show();
  widget->Activate();

  return widget;
}

// Make sure the tasks run synchronously when creating the window.
std::unique_ptr<views::Widget> CreateArcWindowSyncAndWait(
    base::test::TaskEnvironment* task_environment,
    aura::Window* root_window,
    const gfx::Rect& bounds,
    const std::string& package_name) {
  task_environment->RunUntilIdle();
  auto window = CreateArcWindow(root_window, bounds, package_name);
  // I/O takes time here.
  task_environment->FastForwardBy(kIORead);
  return window;
}

void CheckActions(TouchInjector* injector,
                  size_t expect_size,
                  const std::vector<ActionType>& expect_types,
                  const std::vector<int>& expect_ids) {
  CHECK(injector) << "The touch injector should be not nullptr.";
  CHECK_EQ(expect_size, expect_types.size())
      << "Expected size for expect_types: " << expect_size;
  CHECK_EQ(expect_size, expect_ids.size())
      << "Expected size for expect_ids: " << expect_size;

  EXPECT_EQ(expect_size, injector->actions().size());
  for (size_t i = 0; i < expect_size; i++) {
    EXPECT_EQ(expect_types[i], injector->actions()[i]->GetType());
    EXPECT_EQ(expect_ids[i], injector->actions()[i]->id());
  }
}

void SimulatedAppInstalled(base::test::TaskEnvironment* task_environment,
                           ArcAppTest& arc_app_test,
                           const std::string& package_name,
                           bool is_gc_opt_out,
                           bool is_game) {
  auto package = arc::mojom::ArcPackageInfo::New();
  package->package_name = package_name;
  package->game_controls_opt_out = is_gc_opt_out;
  arc_app_test.AddPackage(package->Clone());

  std::vector<arc::mojom::AppInfoPtr> apps;
  apps.emplace_back(arc::mojom::AppInfo::New(package_name, package_name,
                                             package_name + ".activiy"))
      ->app_category = is_game ? arc::mojom::AppCategory::kGame
                               : arc::mojom::AppCategory::kProductivity;
  arc_app_test.app_instance()->SendPackageAppListRefreshed(package_name, apps);
  task_environment->RunUntilIdle();
}

std::u16string GetControlName(ActionType action_type,
                              std::u16string key_string) {
  int control_type_id = 0;
  switch (action_type) {
    case ActionType::TAP:
      control_type_id = IDS_INPUT_OVERLAY_BUTTON_TYPE_SINGLE_BUTTON_LABEL;
      break;
    case ActionType::MOVE:
      control_type_id = IDS_INPUT_OVERLAY_BUTTON_TYPE_JOYSTICK_BUTTON_LABEL;
      break;
    default:
      NOTREACHED();
  }

  if (key_string.empty()) {
    return l10n_util::GetStringFUTF16(
        IDS_INPUT_OVERLAY_CONTROL_NAME_LABEL_UNASSIGNED_TEMPLATE,
        l10n_util::GetStringUTF16(control_type_id));
  }
  return l10n_util::GetStringFUTF16(
      IDS_INPUT_OVERLAY_CONTROL_NAME_LABEL_TEMPLATE,
      l10n_util::GetStringUTF16(control_type_id), key_string);
}

void VerifyEditingListFunctionTriggeredUkmEvent(
    const ukm::TestAutoSetUkmRecorder& ukm_recorder,
    size_t expected_entry_size,
    int64_t expect_histograms_value) {
  EXPECT_GE(expected_entry_size, 1u);
  const auto ukm_entries = ukm_recorder.GetEntriesByName(
      BuildGameControlsUkmEventName(kEditingListFunctionTriggeredHistogram));
  EXPECT_EQ(expected_entry_size, ukm_entries.size());
  ukm::TestAutoSetUkmRecorder::ExpectEntryMetric(
      ukm_entries[expected_entry_size - 1u],
      ukm::builders::GameControls_EditingListFunctionTriggered::kFunctionName,
      expect_histograms_value);
}

void VerifyButtonOptionsMenuFunctionTriggeredUkmEvent(
    const ukm::TestAutoSetUkmRecorder& ukm_recorder,
    size_t expected_entry_size,
    size_t index,
    int64_t expect_histograms_value) {
  EXPECT_LT(index, expected_entry_size);
  const auto ukm_entries =
      ukm_recorder.GetEntriesByName(BuildGameControlsUkmEventName(
          kButtonOptionsMenuFunctionTriggeredHistogram));
  EXPECT_EQ(expected_entry_size, ukm_entries.size());
  ukm::TestAutoSetUkmRecorder::ExpectEntryMetric(
      ukm_entries[index],
      ukm::builders::GameControls_ButtonOptionsMenuFunctionTriggered::
          kFunctionName,
      expect_histograms_value);
}

void VerifyPlayWithGameControlsHistogram(
    const base::HistogramTester& histograms,
    const std::vector<int>& histograms_values) {
  DCHECK_EQ(2u, histograms_values.size());
  const std::string histogram_name =
      BuildGameControlsHistogramName(kPlayGameWithGameControlsHistogram);
  histograms.ExpectBucketCount(histogram_name, false, histograms_values[0]);
  histograms.ExpectBucketCount(histogram_name, true, histograms_values[1]);
}

}  // namespace arc::input_overlay
