// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/tabs/public/tab_collection_storage.h"

#include <memory>

#include "base/notimplemented.h"
#include "components/tabs/public/tab_collection.h"
#include "components/tabs/public/tab_interface.h"

namespace tabs {

TabCollectionStorage::TabCollectionStorage(TabCollection& owner)
    : owning_collection_(owner) {}

TabCollectionStorage::~TabCollectionStorage() {
  for (auto& child : children_) {
    if (std::holds_alternative<ScopedTab>(child)) {
      TabInterface* tab = std::get<ScopedTab>(child).get();
      if (tab) {
        tab->OnReparented(nullptr, owning_collection_->GetPassKey());
      }
    }
  }
}

bool TabCollectionStorage::ContainsTab(const TabInterface* tab) const {
  CHECK(tab);
  return GetIndexOfTab(tab).has_value();
}

TabInterface* TabCollectionStorage::GetTabAtIndex(size_t index) const {
  CHECK(index < GetChildrenCount() && index >= 0);
  CHECK(std::holds_alternative<ScopedTab>(children_[index]));
  const ScopedTab& tab = std::get<ScopedTab>(children_[index]);
  return tab.get();
}

bool TabCollectionStorage::ContainsCollection(
    TabCollection* tab_collection) const {
  CHECK(tab_collection);
  return GetIndexOfCollection(tab_collection).has_value();
}

TabInterface* TabCollectionStorage::AddTab(ScopedTab tab, size_t index) {
  CHECK(index <= GetChildrenCount() && index >= 0);
  CHECK(tab);

  TabInterface* tab_ptr = tab.get();
  children_.insert(children_.begin() + index, std::move(tab));
  owning_collection_->OnTabAddedToTree();
  return tab_ptr;
}

void TabCollectionStorage::MoveTab(TabInterface* tab, size_t dst_index) {
  CHECK(tab);
  CHECK(dst_index < GetChildrenCount() && dst_index >= 0);
  ScopedTab tab_to_move = RemoveTab(tab);
  CHECK(tab_to_move);
  AddTab(std::move(tab_to_move), dst_index);
}

ScopedTab TabCollectionStorage::RemoveTab(TabInterface* tab) {
  CHECK(tab);
  for (size_t i = 0; i < children_.size(); ++i) {
    if (std::holds_alternative<ScopedTab>(children_[i])) {
      ScopedTab& stored_tab = std::get<ScopedTab>(children_[i]);
      if (stored_tab.get() == tab) {
        auto removed_tab = std::move(stored_tab);
        children_.erase(children_.begin() + i);
        owning_collection_->OnTabRemovedFromTree();
        return removed_tab;
      }
    }
  }
  NOTREACHED();
}

TabCollection* TabCollectionStorage::AddCollection(
    std::unique_ptr<TabCollection> collection,
    size_t index) {
  CHECK(index <= GetChildrenCount() && index >= 0);
  CHECK(collection);

  TabCollection* collection_ptr = collection.get();
  children_.insert(children_.begin() + index, std::move(collection));
  owning_collection_->OnCollectionAddedToTree(collection_ptr);
  return collection_ptr;
}

void TabCollectionStorage::MoveCollection(TabCollection* collection,
                                          size_t dst_index) {
  CHECK(collection);
  CHECK(dst_index < GetChildrenCount() && dst_index >= 0);
  std::unique_ptr<TabCollection> tab_collection_to_move =
      RemoveCollection(collection);
  CHECK(tab_collection_to_move);
  AddCollection(std::move(tab_collection_to_move), dst_index);
}

std::unique_ptr<TabCollection> TabCollectionStorage::RemoveCollection(
    TabCollection* collection) {
  CHECK(collection);
  for (size_t i = 0; i < children_.size(); ++i) {
    if (std::holds_alternative<std::unique_ptr<TabCollection>>(children_[i])) {
      std::unique_ptr<TabCollection>& stored_tab_collection =
          std::get<std::unique_ptr<TabCollection>>(children_[i]);
      if (stored_tab_collection.get() == collection) {
        auto removed_tab_collection = std::move(stored_tab_collection);
        children_.erase(children_.begin() + i);
        owning_collection_->OnCollectionRemovedFromTree(
            removed_tab_collection.get());
        return removed_tab_collection;
      }
    }
  }
  NOTREACHED();
}

std::optional<size_t> TabCollectionStorage::GetIndexOfTab(
    const TabInterface* const tab) const {
  CHECK(tab);
  const auto it = std::find_if(
      children_.begin(), children_.end(), [tab](const auto& child) {
        return std::holds_alternative<ScopedTab>(child) &&
               std::get<ScopedTab>(child).get() == tab;
      });
  return it == children_.end() ? std::nullopt
                               : std::optional<size_t>(it - children_.begin());
}

std::optional<size_t> TabCollectionStorage::GetIndexOfCollection(
    TabCollection* tab_collection) const {
  CHECK(tab_collection);
  const auto it = std::find_if(
      children_.begin(), children_.end(), [tab_collection](const auto& child) {
        return std::holds_alternative<std::unique_ptr<TabCollection>>(child) &&
               std::get<std::unique_ptr<TabCollection>>(child).get() ==
                   tab_collection;
      });
  return it == children_.end() ? std::nullopt
                               : std::optional<size_t>(it - children_.begin());
}

size_t TabCollectionStorage::GetChildrenCount() const {
  return children_.size();
}
}  // namespace tabs
