// 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.

#ifndef CHROME_BROWSER_ASH_ACCESSIBILITY_ACCESSIBILITY_TEST_UTILS_H_
#define CHROME_BROWSER_ASH_ACCESSIBILITY_ACCESSIBILITY_TEST_UTILS_H_

#include <set>
#include <string>
#include <vector>

#include "ash/constants/ash_extension_constants.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/statistics_recorder.h"
#include "base/run_loop.h"
#include "chrome/browser/extensions/error_console/error_console.h"
#include "chrome/browser/extensions/extension_browser_test_util.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "ui/base/ime/input_method_base.h"
#include "ui/base/ime/input_method_observer.h"

namespace ash {

using ContextType = ::extensions::browser_test_util::ContextType;
using ::extensions::ErrorConsole;

enum class ManifestVersion { kTwo, kThree };
class FullscreenMagnifierController;

// A class that waits for caret bounds changed.
class CaretBoundsChangedWaiter : public ui::InputMethodObserver {
 public:
  explicit CaretBoundsChangedWaiter(ui::InputMethod* input_method);
  CaretBoundsChangedWaiter(const CaretBoundsChangedWaiter&) = delete;
  CaretBoundsChangedWaiter& operator=(const CaretBoundsChangedWaiter&) = delete;
  ~CaretBoundsChangedWaiter() override;

  // Waits for bounds changed within the input method.
  void Wait();

 private:
  // ui::InputMethodObserver:
  void OnFocus() override {}
  void OnBlur() override {}
  void OnTextInputStateChanged(const ui::TextInputClient* client) override {}
  void OnInputMethodDestroyed(const ui::InputMethod* input_method) override {}
  void OnCaretBoundsChanged(const ui::TextInputClient* client) override;

  raw_ptr<ui::InputMethod> input_method_;
  base::RunLoop run_loop_;
};

// Instantiate this class to get errors and warnings for an extension.
// This will catch console.error and console.warn messages as well as
// any uncaught JS errors in the extension and cause a non-fatal
// test failure as well as log the failure message.
//
// If this is used in the test SetUp, ensure the lifecycle lasts past
// the scope of the SetUp method, perhaps by using a member var, e.g.
// console_observer_ = std::make_unique<ExtensionConsoleErrorObserver>(
//        browser()->GetProfile(), extension_misc::kSelectToSpeakExtensionId);
class ExtensionConsoleErrorObserver : public ErrorConsole::Observer {
 public:
  static constexpr char16_t kErrorBrowserIsShuttingDown[] =
      u"The browser is shutting down.";

  ExtensionConsoleErrorObserver(Profile* profile, const char* extension_id);
  virtual ~ExtensionConsoleErrorObserver();

  // ErrorConsole::Observer:
  void OnErrorAdded(const extensions::ExtensionError* error) override;
  void OnErrorConsoleDestroyed() override;

  // Returns whether errors or warnings were received.
  bool HasErrorsOrWarnings();

  // A helper method to return the string content (in UTF8) of the error or
  // warning at the given |index|. This will cause a test failure if there is no
  // such message.
  std::string GetErrorOrWarningAt(size_t index) const;

  // Get the number of errors and warnings received.
  size_t GetErrorsAndWarningsCount() const;

  // Add an allowed error message.
  void AddAllowedError(const std::u16string& allowed);

 private:
  std::vector<std::u16string> errors_;
  raw_ptr<ErrorConsole> error_console_;
  std::set<std::u16string> allowed_errors_;
};

// FullscreenMagnifierController moves the magnifier window with animation
// when the magnifier is set to be enabled. This waiter class lets a consumer
// wait until the animation completes, i.e. after a mouse move.
class MagnifierAnimationWaiter {
 public:
  explicit MagnifierAnimationWaiter(FullscreenMagnifierController* controller);

  MagnifierAnimationWaiter(const MagnifierAnimationWaiter&) = delete;
  MagnifierAnimationWaiter& operator=(const MagnifierAnimationWaiter&) = delete;

  ~MagnifierAnimationWaiter();

  // Wait until the Fullscreen magnifier finishes animating.
  void Wait();

 private:
  void OnTimer();

  raw_ptr<FullscreenMagnifierController> controller_;  // not owned
  scoped_refptr<content::MessageLoopRunner> runner_;
};

// Helper to convert `ManifestVersion` to string.
std::string ManifestVersionToString(ManifestVersion version);

}  // namespace ash
#endif  // CHROME_BROWSER_ASH_ACCESSIBILITY_ACCESSIBILITY_TEST_UTILS_H_
