// 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_ARC_INPUT_OVERLAY_DISPLAY_OVERLAY_CONTROLLER_H_
#define CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_DISPLAY_OVERLAY_CONTROLLER_H_

#include <string>
#include <string_view>

#include "ash/public/cpp/arc_game_controls_flag.h"
#include "ash/public/cpp/window_properties.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/ash/arc/input_overlay/actions/input_element.h"
#include "chrome/browser/ash/arc/input_overlay/arc_input_overlay_metrics.h"
#include "ui/aura/window_observer.h"
#include "ui/compositor/property_change_reason.h"
#include "ui/events/event.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/views/widget/unique_widget_ptr.h"

namespace views {
class View;
class Widget;
}  // namespace views

namespace arc::input_overlay {

class Action;
class ActionEditMenu;
class ActionViewListItem;
class ButtonOptionsMenu;
class DeleteEditShortcut;
class EditingList;
class InputMappingView;
class RichNudge;
class TargetView;
class TouchInjector;
class TouchInjectorObserver;

// DisplayOverlayController manages the input mapping view, view and edit mode,
// menu, and educational dialog. It also handles the visibility of the
// `ActionEditMenu` and `MessageView` by listening to the `LocatedEvent`.
class DisplayOverlayController : public ui::EventHandler,
                                 public aura::WindowObserver {
 public:
  explicit DisplayOverlayController(TouchInjector* touch_injector);
  DisplayOverlayController(const DisplayOverlayController&) = delete;
  DisplayOverlayController& operator=(const DisplayOverlayController&) = delete;
  ~DisplayOverlayController() override;

  void SetDisplayMode(DisplayMode mode);

  // Get the bounds of `menu_entry_` in screen coordinates.
  std::optional<gfx::Rect> GetOverlayMenuEntryBounds();

  void OnInputBindingChange(Action* action,
                            std::unique_ptr<InputElement> input_element);

  // Save changes to actions, without changing the display mode afterward.
  void SaveToProtoFile();
  // Save the changes when users press the save button after editing.
  void OnCustomizeSave();
  const std::string& GetPackageName() const;
  // Once the menu state is loaded from protobuf data, it should be applied on
  // the view. For example, `InputMappingView` may not be visible if it is
  // hidden or input overlay is disabled.
  void OnApplyMenuState();
  // Get window state type.
  InputOverlayWindowStateType GetWindowStateType() const;

  // For editor.
  void AddNewAction(ActionType action_type, const gfx::Point& target_pos);
  void RemoveAction(Action* action);
  // Creates a new action with guidance from the reference action, and deletes
  // the reference action.
  void ChangeActionType(Action* reference_action_, ActionType type);
  void RemoveActionNewState(Action* action);

  // Returns the size of active actions which include the deleted default
  // actions.
  size_t GetActiveActionsSize() const;
  // Returns true if there is only one user added action.
  bool HasSingleUserAddedAction() const;
  // Return true if action is not deleted.
  bool IsActiveAction(Action* action) const;

  MappingSource GetMappingSource() const;

  // Add UIs to observer touch injector change.
  void AddTouchInjectorObserver(TouchInjectorObserver* observer);
  void RemoveTouchInjectorObserver(TouchInjectorObserver* observer);

  void AddButtonOptionsMenuWidget(Action* action);
  void RemoveButtonOptionsMenuWidget();
  void SetButtonOptionsMenuWidgetVisibility(bool is_visible);

  void AddDeleteEditShortcutWidget(ActionViewListItem* anchor_view);
  void RemoveDeleteEditShortcutWidget();

  void EnterButtonPlaceMode(ActionType action_type);
  // Exits button placement mode after adding a new action if `is_action_added`
  // is true or giving up by pressing key `esc` if `is_action_added` is false.
  void ExitButtonPlaceMode(bool is_action_added);
  void UpdateButtonPlacementNudgeAnchorRect();

  void AddActionHighlightWidget(Action* action);
  void RemoveActionHighlightWidget();
  void HideActionHighlightWidget();
  // Hides the action highlight if the action highlight is anchored to
  // `action`'s view.
  void HideActionHighlightWidgetForAction(Action* action);

  // `widget` bounds is in screen coordinate. `bounds_in_root_window` is the
  // window bounds in associated game window's root window. Convert
  // `bounds_in_root_window` in screen coordinates to set `widget` bounds.
  void UpdateWidgetBoundsInRootWindow(views::Widget* widget,
                                      const gfx::Rect& bounds_in_root_window);

  ActionViewListItem* GetEditingListItemForAction(Action* action);

  // ui::EventHandler:
  void OnMouseEvent(ui::MouseEvent* event) override;
  void OnTouchEvent(ui::TouchEvent* event) override;
  void OnKeyEvent(ui::KeyEvent* event) override;

  // aura::WindowObserver:
  void OnWindowBoundsChanged(aura::Window* window,
                             const gfx::Rect& old_bounds,
                             const gfx::Rect& new_bounds,
                             ui::PropertyChangeReason reason) override;
  void OnWindowPropertyChanged(aura::Window* window,
                               const void* key,
                               intptr_t old) override;

  const TouchInjector* touch_injector() const { return touch_injector_; }

 private:
  friend class ActionView;
  friend class ArcInputOverlayManagerTest;
  friend class ButtonOptionsMenu;
  friend class DisplayOverlayControllerTest;
  friend class EditingList;
  friend class EditingListTest;
  friend class EditLabelTest;
  friend class EducationalView;
  friend class GameControlsTestBase;
  friend class InputMappingView;
  friend class OverlayViewTestBase;
  friend class RichNudgeTest;

  // If `on_overlay` is true, set event target on overlay layer. Otherwise, set
  // event target on the layer underneath the overlay layer.
  void SetEventTarget(views::Widget* overlay_widget, bool on_overlay);

  // Update display mode when initializing DisplayOverlayController or the
  // window property is changed on `ash::kArcGameControlsFlagsKey`.
  void UpdateDisplayMode();

  views::Widget* GetOverlayWidget();
  views::View* GetOverlayWidgetContentsView();
  bool HasMenuView() const;
  // Sets input mapping visibility according to `visible` and stores the setting
  // if `store_visible_state` is true.
  void SetInputMappingVisible(bool visible, bool store_visible_state = false);
  bool GetInputMappingViewVisible() const;

  void SetTouchInjectorEnable(bool enable);
  bool GetTouchInjectorEnable();

  // Close `MessageView` if `LocatedEvent` happens outside
  // of their view bounds.
  void ProcessPressedEvent(const ui::LocatedEvent& event);

  // When the input is processed on overlay in edit mode, PlaceholderActivity
  // task window becomes the front task window. This ensures the target task
  // window is moved back to the front of task stack on ARC side for view mode.
  void EnsureTaskWindowToFrontForViewMode(views::Widget* overlay_widget);

  void UpdateForBoundsChanged();

  // For beta.
  void RemoveAllWidgets();

  void AddInputMappingWidget();
  void RemoveInputMappingWidget();
  InputMappingView* GetInputMapping();

  // Stacks input mapping at the bottom and under the game dashboard UIs.
  void StackInputMappingAtBottomForViewMode();

  void AddEditingListWidget();
  void RemoveEditingListWidget();
  void SetEditingListVisibility(bool visible);
  EditingList* GetEditingList();

  ButtonOptionsMenu* GetButtonOptionsMenu();

  // Shows or removes target view when in or out button place mode.
  void AddTargetWidget(ActionType action_type);
  void RemoveTargetWidget();
  TargetView* GetTargetView() const;

  void AddRichNudge();
  void RemoveRichNudge();
  RichNudge* GetRichNudge() const;

  DeleteEditShortcut* GetDeleteEditShortcut() const;

  // Update widget bounds if the view content is changed or the app window
  // bounds are changed.
  void UpdateButtonOptionsMenuWidgetBounds();
  void UpdateInputMappingWidgetBounds();
  void UpdateEditingListWidgetBounds();
  void UpdateTargetWidgetBounds();

  // `TouchInjector` only rewrite events in `kView` mode. When changing between
  // edit mode and view mode or the feature is disabled from menu or if the game
  // dashboard menu shows up, it needs to tell `TouchInjector` if it can rewrite
  // events.
  void UpdateEventRewriteCapability();

  // Returns a list of visible widgets that are available to be traversed in the
  // edit mode.
  std::vector<views::Widget*> GetTraversableWidgets() const;

  void MaybeChangeFocusWidget(ui::KeyEvent& event);

  // Owned by `ArcInputOverlayManager`.
  const raw_ptr<TouchInjector> touch_injector_;

  DisplayMode display_mode_ = DisplayMode::kNone;

  // For beta.
  std::unique_ptr<views::Widget> input_mapping_widget_;
  std::unique_ptr<views::Widget> editing_list_widget_;
  std::unique_ptr<views::Widget> button_options_widget_;
  std::unique_ptr<views::Widget> target_widget_;
  std::unique_ptr<views::Widget> action_highlight_widget_;
  views::UniqueWidgetPtr delete_edit_shortcut_widget_;
  views::UniqueWidgetPtr rich_nudge_widget_;
};

}  // namespace arc::input_overlay

#endif  // CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_DISPLAY_OVERLAY_CONTROLLER_H_
