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

#import "ios/chrome/browser/tab_switcher/ui_bundled/tab_utils.h"

#import <set>

#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/browser/browser_list.h"
#import "ios/chrome/browser/shared/model/web_state_list/tab_group.h"
#import "ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/grid_item_identifier.h"
#import "ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_context_menu/tab_item.h"
#import "ios/chrome/browser/tab_switcher/ui_bundled/tab_group_item.h"
#import "ios/chrome/browser/tab_switcher/ui_bundled/tab_switcher_item.h"
#import "ios/chrome/browser/tabs/model/tab_title_util.h"
#import "ios/web/public/web_state.h"

namespace {

// Magic padding to fit UX mocks for items sizes. See where it is used for more
// details.
constexpr CGFloat kMagicPadding = 50;
// Items aspect ratios.
constexpr CGFloat kPortraitAspectRatio = 4. / 3.;
// Different width thresholds for determining the columns count.
constexpr CGFloat kSmallWidthThreshold = 500;
constexpr CGFloat kLargeWidthThreshold = 1000;

}  // namespace

CGFloat TabGridItemAspectRatio(CGSize size, UIWindowScene* window_scene) {
  const CGFloat width = size.width;
  const CGFloat height = size.height;

  CGRect screen_bounds = CGRectZero;
  if (@available(iOS 26, *)) {
    screen_bounds = window_scene.effectiveGeometry.coordinateSpace.bounds;
  } else if (window_scene) {
    screen_bounds = window_scene.screen.bounds;
  }

  const CGFloat screen_width = CGRectGetWidth(screen_bounds);
  const CGFloat screen_height = CGRectGetHeight(screen_bounds);
  const CGFloat screen_aspect_ratio = (screen_width > 0 && screen_height > 0)
                                          ? screen_height / screen_width
                                          : kPortraitAspectRatio;

  // On iPad Landscape with 3/4 - 1/4 Split View, the 3/4 width is just a bit
  // smaller than the height, but design-wise, a landscape aspect ratio should
  // be preferred. Pad a bit the width with a magic constant before comparing to
  // the height.
  return width + kMagicPadding > height ? screen_aspect_ratio
                                        : kPortraitAspectRatio;
}

NSInteger TabGridColumnsCount(CGSize size,
                              UIContentSizeCategory content_size_category) {
  const CGFloat width = size.width;

  NSInteger count;
  if (width < kSmallWidthThreshold) {
    count = 2;
  } else if (width < kLargeWidthThreshold) {
    count = 3;
  } else {
    count = 4;
  }

  // If Dynamic Type uses an Accessibility setting, just remove a column.
  if (UIContentSizeCategoryIsAccessibilityCategory(content_size_category)) {
    count -= 1;
  }

  return count;
}

TabItem* GetTabItem(WebStateList* web_state_list,
                    WebStateSearchCriteria criteria) {
  web::WebState* web_state = GetWebState(web_state_list, criteria);
  if (!web_state) {
    return nil;
  }

  TabItem* item =
      [[TabItem alloc] initWithTitle:tab_util::GetTabTitle(web_state)
                                 URL:web_state->GetVisibleURL()];
  return item;
}

bool HasDuplicateGroupsAndTabsIdentifiers(NSArray<GridItemIdentifier*>* items) {
  std::set<web::WebStateID> identifiers;
  std::set<const TabGroup*> groups;
  for (GridItemIdentifier* item in items) {
    switch (item.type) {
      case GridItemType::kInactiveTabsButton:
        NOTREACHED();
      case GridItemType::kTab:
        identifiers.insert(item.tabSwitcherItem.identifier);
        break;
      case GridItemType::kGroup:
        groups.insert(item.tabGroupItem.tabGroup);
        break;
      case GridItemType::kSuggestedActions:
        NOTREACHED();
      case GridItemType::kActivitySummary:
        NOTREACHED();
    }
  }
  return (identifiers.size() + groups.size()) != items.count;
}

bool HasDuplicateIdentifiers(NSArray<TabSwitcherItem*>* items) {
  std::set<web::WebStateID> identifiers;
  for (TabSwitcherItem* item in items) {
    identifiers.insert(item.identifier);
  }
  return identifiers.size() != items.count;
}

Browser* GetBrowserForTabWithCriteria(BrowserList* browser_list,
                                      WebStateSearchCriteria criteria,
                                      bool is_otr_tab) {
  const BrowserList::BrowserType browser_types =
      is_otr_tab ? BrowserList::BrowserType::kIncognito
                 : BrowserList::BrowserType::kRegularAndInactive;
  std::set<Browser*> browsers = browser_list->BrowsersOfType(browser_types);
  for (Browser* browser : browsers) {
    WebStateList* web_state_list = browser->GetWebStateList();
    int index = GetWebStateIndex(web_state_list, criteria);
    if (index != WebStateList::kInvalidIndex) {
      return browser;
    }
  }
  return nullptr;
}

web::WebState* GetWebStateForTabWithCriteria(BrowserList* browser_list,
                                             WebStateSearchCriteria criteria,
                                             bool is_otr_tab) {
  Browser* browser =
      GetBrowserForTabWithCriteria(browser_list, criteria, is_otr_tab);

  if (!browser) {
    return nullptr;
  }

  WebStateList* web_state_list = browser->GetWebStateList();
  int index = GetWebStateIndex(web_state_list, criteria);

  if (index != WebStateList::kInvalidIndex) {
    return web_state_list->GetWebStateAt(index);
  }

  return nullptr;
}
