// 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_COLLECTION_NODE_H_
#define CHROME_BROWSER_UI_VIEWS_TABS_COMMON_TAB_COLLECTION_NODE_H_

#include <memory>
#include <string>
#include <vector>

#include "base/callback_list.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/types/pass_key.h"
#include "chrome/browser/ui/views/tabs/shared/tab_strip_types.h"
#include "components/tabs/public/tab_collection.h"
#include "components/tabs/public/tab_collection_types.h"
#include "ui/gfx/geometry/rect.h"

namespace views {
class View;
}

class TabStripCollectionController;

// The individual collection node of the view model. The root
// node also inherits from this class. It provides extensibility hooks for the
// views to use to implement things like animations.
class TabCollectionNode {
 public:
  // Keep the same as TabCollection::Type with a tab.
  // LINT.IfChange(TYPE)
  enum class Type { TABSTRIP, PINNED, UNPINNED, GROUP, SPLIT, TAB };
  // LINT.ThenChange(components/tabs/public/tab_collection.h:TYPE)

  // Helper type for creating CustomAddChildViewCallbacks with
  // base::BindRepeating.
  typedef views::View* (views::View::*CustomAddChildView)(
      std::unique_ptr<views::View>);
  typedef base::RepeatingCallback<views::View*(std::unique_ptr<views::View>)>
      CustomAddChildViewCallback;
  typedef base::RepeatingCallback<void(views::View* view_to_remove)>
      CustomRemoveChildViewCallback;
  typedef base::RepeatingCallback<void(std::unique_ptr<views::View>,
                                       const gfx::Rect&)>
      CustomAttachChildViewCallback;
  typedef base::RepeatingCallback<std::unique_ptr<views::View>(views::View*)>
      CustomDetachChildViewCallback;
  typedef std::vector<std::unique_ptr<TabCollectionNode>> NodeChildren;

  explicit TabCollectionNode(tabs::ConstChildPtr node_data,
                             TabStripOrientation orientation);
  virtual ~TabCollectionNode();

  // Creates the view for this node. Then, for each child container in children,
  // creates a TabCollectionNode, calls Initialize on it, and then adds the node
  // as a child of this.
  std::unique_ptr<views::View> Initialize();

  // Deinitializes all the child nodes in a recursive manner.
  void Deinitialize();

  // Gets the collection under this subtree that has the associated handle.
  // Returns nullptr if no such node exists.
  TabCollectionNode* GetNodeForHandle(
      const tabs::TabCollectionNodeHandle& handle);
  const TabCollectionNode* GetNodeForHandle(
      const tabs::TabCollectionNodeHandle& handle) const;
  TabCollectionNode* GetParentNodeForHandle(
      const tabs::TabCollectionNodeHandle& handle);
  const TabCollectionNode* GetParentNodeForHandle(
      const tabs::TabCollectionNodeHandle& handle) const;

  // Gets the first direct child of this node that has the associated type.
  // Returns nullptr if no such node exists.
  TabCollectionNode* GetChildNodeOfType(const Type type);

  // Creates a new child and adds it at model_index. If `perform_initialization`
  // is true, then the entire subtree of the node data will be constructed as
  // well, if not, then only the view for the child is constructed and added.
  void AddNewChild(base::PassKey<TabCollectionNode> pass_key,
                   tabs::ConstChildPtr node_data,
                   size_t model_index,
                   bool perform_initialization);

  // Removes the child and removes and destroys the view.
  void RemoveChild(base::PassKey<TabCollectionNode> pass_key,
                   const tabs::TabCollectionNodeHandle& handle,
                   bool perform_deinitialization);

  // Moves the node to the new index within the same parent. Also updates the
  // z-order of the moved child to the highest to ensure it shows over other
  // tabs when animating.
  void MoveChild(base::PassKey<TabCollectionNode> pass_key,
                 const tabs::TabCollectionNodeHandle& handle,
                 int new_index);

  // Moves the node to the new index across different parent nodes.
  static void MoveChild(base::PassKey<TabCollectionNode> pass_key,
                        const tabs::TabCollectionNodeHandle& handle,
                        int new_index,
                        TabCollectionNode* src_parent_node,
                        TabCollectionNode* dst_parent_node);

  tabs::ConstChildPtr GetNodeData() const { return node_data_; }
  tabs::TabCollectionNodeHandle GetHandle() const;
  Type type() const { return type_; }
  TabStripOrientation orientation() const { return orientation_; }
  const NodeChildren& children() const { return children_; }
  views::View* view() const { return node_view_; }
  std::vector<views::View*> GetDirectChildren() const;

  void set_add_child_to_node(CustomAddChildViewCallback add_child_to_node) {
    add_child_to_node_ = std::move(add_child_to_node);
  }

  void set_remove_child_from_node(
      CustomRemoveChildViewCallback remove_child_from_node) {
    remove_child_from_node_ = std::move(remove_child_from_node);
  }

  void set_attach_child_to_node(
      CustomAttachChildViewCallback attach_child_to_node) {
    attach_child_to_node_ = std::move(attach_child_to_node);
  }

  void set_detach_child_from_node(
      CustomDetachChildViewCallback detach_child_from_node) {
    detach_child_from_node_ = std::move(detach_child_from_node);
  }

  base::CallbackListSubscription RegisterWillDestroyCallback(
      base::OnceClosure callback);

  // Runs `callback` whenever a tab's selection or activation changes.
  base::CallbackListSubscription RegisterTabSelectionChangedCallback(
      base::RepeatingClosure callback);

  void NotifyTabSelectionChanged();

  void SetController(TabStripCollectionController* controller);
  TabStripCollectionController* GetController() {
    return tab_strip_controller_;
  }
  const TabStripCollectionController* GetController() const {
    return tab_strip_controller_;
  }

 protected:
  // Returns the pass key to be used by derived classes so that methods such as
  // SetData may only be performed by a `TabCollectionNode`.
  base::PassKey<TabCollectionNode> GetPassKey() const {
    return base::PassKey<TabCollectionNode>();
  }

  static std::unique_ptr<views::View> CreateViewForNode(
      TabCollectionNode* node_for_view);

  // Adds `child_node` to `children_`.
  void AddChildNode(std::unique_ptr<TabCollectionNode> child_node,
                    size_t model_index);

  // Creates node_view_, then returns the unique_ptr to the view.
  std::unique_ptr<views::View> CreateAndSetView();

  // Updates the focus order of the child view at the specified `child_index`.
  // This ensures that follows the logical order of the tab collection by
  // re-linking the view's focus neighbors based on its current position in the
  // children list.
  void EnsureFocusOrder(size_t child_index);

  base::OnceClosureList on_will_destroy_callback_list_;
  base::RepeatingClosureList on_tab_selection_changed_callback_list_;

  const Type type_;
  const tabs::TabCollectionNodeHandle handle_;
  tabs::ConstChildPtr node_data_;

  // 1:1 mapping of the collections children.
  NodeChildren children_;

  // add_child_to_node_ must be assigned when constructing the node_view in
  // Initialize so that the children that are created know how to be added to
  // the View Hierarchy. Used when the default AddChildView behavior needs to be
  // overridden.
  CustomAddChildViewCallback add_child_to_node_;

  // Custom callback to remove a child view, used when the default
  // RemoveChildViewT behavior needs to be overridden if the view hierarchy does
  // not match the view model hierarchy.
  CustomRemoveChildViewCallback remove_child_from_node_;

  // Custom callback invoked when reparent an existing view as child to another
  // container. Used when the default AddChildView behavior needs to be
  // overridden.
  CustomAttachChildViewCallback attach_child_to_node_;

  // Custom callback invoked when reparent an existing view as child to another
  // container. Used when the default RemoveChildViewT behavior needs to be
  // overridden.
  CustomDetachChildViewCallback detach_child_from_node_;

  // The view created for this node. (for tab:tabview, for unpinned: the
  // unpinned_container_view).
  raw_ptr<views::View> node_view_ = nullptr;

  // Allows views to create the Tab Context Menu.
  raw_ptr<TabStripCollectionController> tab_strip_controller_ = nullptr;

  const TabStripOrientation orientation_;
};

#endif  // CHROME_BROWSER_UI_VIEWS_TABS_COMMON_TAB_COLLECTION_NODE_H_
