// Copyright 2025 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_UI_VIEWS_TABS_COMMON_TAB_STRIP_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_TABS_COMMON_TAB_STRIP_VIEW_H_

#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/tabs/public/tab_interface.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget_observer.h"

class PrefService;
class TabCollectionNode;
class PinnedTabContainerView;
class UnpinnedTabContainerView;
class TabScrollButtonContainer;

namespace tabs {
class UnpinnedTabScrollTypeRecorder;
class UnpinnedTabScrollableStateRecorder;
}  // namespace tabs

namespace views {
class ScrollView;
class Separator;
}  // namespace views

// The view class for tab strip which holds the pinned and unpinned
// regions and associates them to their scroll views. It also is responsible for
// scrolling to the active tab view when the active tab changes.
// Layout is managed by `TabStripViewLayout`.
class TabStripView final : public views::View,
                           public views::WidgetObserver {
  METADATA_HEADER(TabStripView, views::View)

 public:
  explicit TabStripView(TabCollectionNode* collection_node);
  TabStripView(const TabStripView&) = delete;
  TabStripView& operator=(const TabStripView&) = delete;
  ~TabStripView() override;

  views::Separator* GetTabsSeparator() const { return tabs_separator_; }

  PinnedTabContainerView* GetPinnedTabsContainer() const;
  UnpinnedTabContainerView* GetUnpinnedTabsContainer() const;
  TabScrollButtonContainer* GetScrollButtonContainer() const;
  bool IsTabScrollButtonsPinned() const;

  views::ScrollView* pinned_tabs_scroll_view() const {
    return pinned_tabs_scroll_view_;
  }
  views::ScrollView* unpinned_tabs_scroll_view() const {
    return unpinned_tabs_scroll_view_;
  }

  void SetCollapsedState(bool is_collapsed);
  void SetIsAnimatingSize(bool is_animating);

  bool IsPositionInWindowCaption(const gfx::Point& point);

  // views::View:
  void AddedToWidget() override;
  void RemovedFromWidget() override;
  void OnMouseEntered(const ui::MouseEvent& event) override;
  void OnMouseExited(const ui::MouseEvent& event) override;

  // views::WidgetObserver:
  void OnWidgetActivationChanged(views::Widget* widget, bool active) override;

  void OnChildMoved(TabCollectionNode* moved_node);
  void OnTabChanged(const tabs::TabInterface* active_tab);

  // Scrolls the scroll view(s) to ensure that the specified target tabs/views
  // are visible within the viewport.
  //
  // - `active_tab` (or `view1`) is treated as the primary view, and `new_tab`
  // (or
  //   `view2`) is treated as the secondary view.
  // - Attempts to scroll so that the combined bounds of both views are visible
  //   simultaneously within the viewport.
  // - If the combined bounds exceed the height of the scroll viewport, it falls
  //   back to prioritizing the primary view (the active tab).
  // - If the views belong to separate scroll containers (e.g., pinned vs.
  //   unpinned), each container independently scrolls to reveal its target
  //   view.
  void ScrollToFitTabs(const tabs::TabInterface* active_tab,
                       const tabs::TabInterface* new_tab);
  void ScrollToFitViews(views::View* view1, views::View* view2);

  void RecordMousePressedInTab();
  bool IsFocusInTabStrip();

  views::ScrollView* GetUnpinnedTabsScrollViewForTesting() {
    return unpinned_tabs_scroll_view_;
  }
  views::ScrollView* GetPinnedTabsScrollViewForTesting() {
    return pinned_tabs_scroll_view_;
  }
  views::View* GetPrimaryScrollTargetForTesting() const;
  views::View* GetSecondaryScrollTargetForTesting() const;
  tabs::UnpinnedTabScrollableStateRecorder*
  unpinned_tab_scrollable_state_recorder_for_testing() {
    return unpinned_tab_scrollable_state_recorder_.get();
  }

 private:
  class TargetViewsTracker;

  views::View* AddScrollViewContents(std::unique_ptr<views::View> view);
  void RemoveScrollViewContents(views::View* view);
  void SetScrollViewProperties(views::ScrollView* scroll_view);
  void ResetCollectionNode();

  // Invoked after layout has been invoked for target views' associated
  // ScrollView. Performs a best-effort attempt to ensure target views are
  // visible in the viewport (prioritizing the active tab view if not all fit).
  void EnsureViewsVisibleInViewportPostLayout(views::ScrollView* scroll_view);

  // Enables and disables overflow visuals on `scroll_view` respectively. Used
  // in combination to avoid visual artifacts caused by repeatedly scrolling-in
  // animating views.
  void EnableOverflowVisuals(views::ScrollView* scroll_view);
  void DisableOverflowVisuals(views::ScrollView* scroll_view);

  gfx::Rect GetBoundsInScrollViewContents(views::View* view,
                                          views::ScrollView* scroll_view);

  void UpdateColors();

  bool IsFrameActive() const;

  void HideHoverCardOnScroll();

  PrefService* GetPrefs() const;

  // Updates the main-axis space allocated for unpinned tabs (e.g. total
  // available tab strip width minus pinned container width in horizontal mode).
  void SetAvailableUnpinnedSpace(views::SizeBound space) const;

  friend class TabStripViewLayout;

  PrefChangeRegistrar pref_change_registrar_;
  raw_ptr<TabCollectionNode> collection_node_ = nullptr;
  raw_ptr<views::ScrollView> pinned_tabs_scroll_view_ = nullptr;
  raw_ptr<PinnedTabContainerView> pinned_tabs_container_view_ = nullptr;
  raw_ptr<views::Separator> tabs_separator_ = nullptr;
  raw_ptr<views::ScrollView> unpinned_tabs_scroll_view_ = nullptr;
  raw_ptr<UnpinnedTabContainerView> unpinned_tabs_container_view_ = nullptr;
  raw_ptr<TabScrollButtonContainer> tab_scroll_button_container_ = nullptr;
  bool is_collapsed_ = false;

  // Used for seek time metrics from the time the mouse enters the tabstrip.
  std::optional<base::TimeTicks> mouse_entered_tabstrip_time_;
  // Used to track if the time from mouse entered to tab switch been reported.
  bool has_reported_time_mouse_entered_to_switch_ = false;

  std::unique_ptr<TargetViewsTracker> target_views_tracker_;
  // Records the type of scroll that the user does on the unpinned tabs.
  std::unique_ptr<tabs::UnpinnedTabScrollTypeRecorder>
      unpinned_tab_scroll_type_recorder_;
  // Records, every five minutes, whether the unpinned tabs are scrollable
  // or not.
  std::unique_ptr<tabs::UnpinnedTabScrollableStateRecorder>
      unpinned_tab_scrollable_state_recorder_;

  base::CallbackListSubscription node_destroyed_subscription_;
  base::CallbackListSubscription paint_as_active_subscription_;
  std::vector<base::CallbackListSubscription> callback_subscriptions_;

  base::ScopedObservation<views::Widget, views::WidgetObserver>
      widget_observation_{this};
};

#endif  // CHROME_BROWSER_UI_VIEWS_TABS_COMMON_TAB_STRIP_VIEW_H_
