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

#include "chrome/browser/ui/views/chrome_layout_provider.h"

#include <algorithm>

#include "base/feature_list.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "components/omnibox/common/omnibox_features.h"
#include "ui/base/pointer/touch_ui_controller.h"
#include "ui/gfx/shadow_value.h"

namespace {

ChromeLayoutProvider* g_chrome_layout_provider = nullptr;

}  // namespace

ChromeLayoutProvider::ChromeLayoutProvider() {
  DCHECK_EQ(nullptr, g_chrome_layout_provider);
  g_chrome_layout_provider = this;
}

ChromeLayoutProvider::~ChromeLayoutProvider() {
  DCHECK_EQ(this, g_chrome_layout_provider);
  g_chrome_layout_provider = nullptr;
}

// static
ChromeLayoutProvider* ChromeLayoutProvider::Get() {
  // Check to avoid downcasting a base LayoutProvider.
  DCHECK_EQ(g_chrome_layout_provider, views::LayoutProvider::Get());
  return static_cast<ChromeLayoutProvider*>(views::LayoutProvider::Get());
}

// static
std::unique_ptr<views::LayoutProvider>
ChromeLayoutProvider::CreateLayoutProvider() {
  return std::make_unique<ChromeLayoutProvider>();
}

gfx::Insets ChromeLayoutProvider::GetInsetsMetric(int metric) const {
  DCHECK_LT(metric, views::VIEWS_INSETS_MAX);
  const bool touch_ui = ui::TouchUiController::Get()->touch_ui();
  switch (metric) {
    case views::INSETS_DIALOG:
    case views::INSETS_DIALOG_SUBSECTION: {
      return gfx::Insets::VH(20, 20);
    }
    case views::INSETS_DIALOG_FOOTNOTE: {
      return gfx::Insets::TLBR(10, 20, 15, 20);
    }
    case views::INSETS_CHECKBOX_RADIO_BUTTON: {
      gfx::Insets insets = LayoutProvider::GetInsetsMetric(metric);
      // Checkboxes and radio buttons should be aligned flush to the left edge.
      return gfx::Insets::TLBR(insets.top(), 0, insets.bottom(),
                               insets.right());
    }
    case views::INSETS_VECTOR_IMAGE_BUTTON:
      return gfx::Insets(4);
    case views::InsetsMetric::INSETS_LABEL_BUTTON:
      return touch_ui ? gfx::Insets::VH(8, 8)
                      : LayoutProvider::GetInsetsMetric(metric);
    case INSETS_BOOKMARKS_BAR_BUTTON:
      return touch_ui ? gfx::Insets::VH(8, 10) : gfx::Insets(6);
    case INSETS_TOAST:
      return gfx::Insets::VH(0, 16);
    case INSETS_OMNIBOX_PILL_BUTTON:
      if (!touch_ui) {
        return gfx::Insets::VH(4, 8);
      } else {
        return touch_ui ? gfx::Insets::VH(8, 16) : gfx::Insets::VH(5, 12);
      }
    case INSETS_PAGE_INFO_HOVER_BUTTON: {
      const gfx::Insets insets =
          LayoutProvider::GetInsetsMetric(views::INSETS_LABEL_BUTTON);
      const int horizontal_padding = 20;
      // Hover button in page info requires double the height compared to the
      // label button because it behaves like a menu control.
      return gfx::Insets::VH(insets.height(), horizontal_padding);
    }
    case INSETS_RECENT_ACTIVITY_IMAGE_MARGIN:
      return gfx::Insets::TLBR(0, 12, 0, 16);
    case INSETS_TASK_MANAGER:
      return gfx::Insets::TLBR(4, 20, 20, 20);
    case INSETS_PAGE_INFO_FOOTER_BUTTON:
      return gfx::Insets::VH(12, 20);
    case INSETS_ACTION_APP_MENU_POPUP:
      return gfx::Insets::VH(16, 16);
    case INSETS_ACTION_APP_MENU_ITEM:
      return gfx::Insets::TLBR(0, 16, 0, 12);
    case INSETS_ACTION_APP_MENU_FOOTER:
      return gfx::Insets::TLBR(8, 12, 8, 12);
    case INSETS_ACTION_APP_MENU_FOOTER_BUTTON:
      return gfx::Insets::VH(4, 8);
    case INSETS_PROFILE_SIGNIN_STATUS_CHIP:
      // Relies on the parent menu's margins for vertical alignment
      // with other items.
      return gfx::Insets::VH(0, 12);
    case INSETS_ACTION_APP_MENU_BLOCK_ROW:
      return gfx::Insets::VH(4, 4);
    case INSETS_ACTION_APP_MENU_BLOCK_ENTRY_BUTTON:
      return gfx::Insets::VH(8, 0);
    default:
      return LayoutProvider::GetInsetsMetric(metric);
  }
}

int ChromeLayoutProvider::GetDistanceMetric(int metric) const {
  DCHECK_GE(metric, views::VIEWS_DISTANCE_START);
  DCHECK_LT(metric, views::VIEWS_DISTANCE_MAX);

  if (metric < views::VIEWS_DISTANCE_END) {
    return LayoutProvider::GetDistanceMetric(metric);
  }

  switch (static_cast<ChromeDistanceMetric>(metric)) {
    case DISTANCE_CONTENT_LIST_VERTICAL_SINGLE:
      return 4;
    case DISTANCE_CONTENT_LIST_VERTICAL_MULTI:
      return 8;
    case DISTANCE_EXTENSIONS_MENU_WIDTH:
      return kMediumDialogWidth;
    case DISTANCE_EXTENSIONS_MENU_BUTTON_ICON_SIZE:
      return 20;
    case DISTANCE_EXTENSIONS_MENU_BUTTON_ICON_SMALL_SIZE:
      return 16;
    case DISTANCE_EXTENSIONS_MENU_EXTENSION_ICON_SIZE:
      return 28;
    case DISTANCE_EXTENSIONS_MENU_ICON_SPACING:
      return (GetDistanceMetric(DISTANCE_EXTENSIONS_MENU_EXTENSION_ICON_SIZE) -
              GetDistanceMetric(DISTANCE_EXTENSIONS_MENU_BUTTON_ICON_SIZE)) /
             2;
    case DISTANCE_EXTENSIONS_MENU_BUTTON_MARGIN:
      return 12;
    case DISTANCE_EXTENSIONS_MENU_LABEL_ICON_SPACING:
      return 4;
    case DISTANCE_RELATED_CONTROL_HORIZONTAL_SMALL:
      return 16;
    case DISTANCE_RELATED_CONTROL_VERTICAL_SMALL:
      return 8;
    case DISTANCE_BUTTON_MINIMUM_WIDTH:
      return GetDistanceMetric(views::DISTANCE_DIALOG_BUTTON_MINIMUM_WIDTH);
    case DISTANCE_RELATED_LABEL_HORIZONTAL_LIST:
      return 8;
    case DISTANCE_SUBSECTION_HORIZONTAL_INDENT:
      return 0;
    case DISTANCE_TOAST_CONTROL_VERTICAL:
      return 8;
    case DISTANCE_TOAST_LABEL_VERTICAL:
      return 12;
    case DISTANCE_UNRELATED_CONTROL_HORIZONTAL_LARGE:
      return 16;
    case DISTANCE_UNRELATED_CONTROL_VERTICAL_LARGE:
      return 16;
    case DISTANCE_STANDALONE_BUBBLE_PREFERRED_WIDTH:
      return kMediumDialogWidth;
    case DISTANCE_BETWEEN_PRIMARY_AND_SECONDARY_LABELS_HORIZONTAL:
      return 24;
    case DISTANCE_OMNIBOX_CELL_VERTICAL_PADDING:
      return 12;
    case DISTANCE_OMNIBOX_TWO_LINE_CELL_VERTICAL_PADDING:
      return 4;
    case DISTANCE_SIDE_PANEL_HEADER_VECTOR_ICON_SIZE:
      return 16;
    case DISTANCE_SIDE_PANEL_HEADER_BUTTON_MINIMUM_SIZE:
      return 20;
    case DISTANCE_SIDE_PANEL_HEADER_INTERIOR_MARGIN_HORIZONTAL:
      return 4;
    case DISTANCE_SIDE_PANEL_CONTENT_RADIUS:
      return 8;
    case DISTANCE_HORIZONTAL_SEPARATOR_PADDING_PAGE_INFO_VIEW:
      return 20;
    case DISTANCE_INFOBAR_HORIZONTAL_ICON_LABEL_PADDING:
      return 12;
    case DISTANCE_INFOBAR_HEIGHT:
      // Spec says height of button should be 36dp, vertical padding on both
      // top and bottom should be 8dp.
      // The new refreshed button height is 20 + (2 * 6) = 32dp.
      // Therefore, the total infobar height is 32dp + 2 * 12.
      return 32 + 2 * 12;
    case DISTANCE_PERMISSION_PROMPT_HORIZONTAL_ICON_LABEL_PADDING:
      return 8;
    case DISTANCE_RICH_HOVER_BUTTON_ICON_HORIZONTAL:
      return 8;
    case DISTANCE_TASK_MANAGER_SEARCH_BAR_ICON_AND_BUTTON_HORIZONTAL_SPACING:
      return 4;
    case DISTANCE_TASK_MANAGER_SEARCH_ICON_SIZE:
      return 20;
    case DISTANCE_TASK_MANAGER_SEARCH_BAR_MIN_WIDTH:
      return 160;
    case DISTANCE_TASK_MANAGER_SEARCH_BAR_MIN_HEIGHT:
      return 36;
    case DISTANCE_TASK_MANAGER_TAB_HEIGHT:
      return 54;
    case DISTANCE_TOAST_BUBBLE_BETWEEN_CHILD_SPACING:
    case DISTANCE_TOAST_BUBBLE_BROWSER_WINDOW_MARGIN:
      return 4;
    case DISTANCE_TOAST_BUBBLE_BETWEEN_LABEL_ACTION_BUTTON_SPACING:
    case DISTANCE_TOAST_BUBBLE_BETWEEN_LABEL_MENU_BUTTON_SPACING:
      return 16;
    case DISTANCE_TOAST_BUBBLE_HEIGHT:
      return 48;
    case DISTANCE_TOAST_BUBBLE_HEIGHT_ACTION_BUTTON:
      return 36;
    case DISTANCE_TOAST_BUBBLE_HEIGHT_CONTENT:
      return 24;
    case DISTANCE_TOAST_BUBBLE_ICON_SIZE:
    case DISTANCE_TOAST_BUBBLE_MENU_ICON_SIZE:
      return 20;
    case DISTANCE_TOAST_BUBBLE_LEADING_ICON_SIDE_MARGINS:
      return 2;
    case DISTANCE_TOAST_BUBBLE_MARGIN_LEFT:
      return 12;
    case DISTANCE_TOAST_BUBBLE_MARGIN_RIGHT_ACTION_BUTTON:
      return 6;
    case DISTANCE_TOAST_BUBBLE_MARGIN_RIGHT_CLOSE_BUTTON:
    case DISTANCE_TOAST_BUBBLE_MARGIN_RIGHT_MENU_BUTTON:
      return 10;
    case DISTANCE_TOAST_BUBBLE_MARGIN_RIGHT_LABEL:
      return 16;
    case DISTANCE_RECENT_ACTIVITY_AVATAR_SIZE:
      return 32;
    case DISTANCE_RECENT_ACTIVITY_AVATAR_FALLBACK_SIZE:
      return 24;
    case DISTANCE_RECENT_ACTIVITY_FAVICON_CONTAINER_RADIUS:
      return 9;
    case DISTANCE_RECENT_ACTIVITY_FAVICON_CONTAINER_BORDER_WIDTH:
      return 2;
    case DISTANCE_RECENT_ACTIVITY_FAVICON_CONTAINER_PADDING:
      return 4;
    case DISTANCE_RECENT_ACTIVITY_FAVICON_CONTAINER_OFFSET_FROM_AVATAR:
      return 2;
    case DISTANCE_RECENT_ACTIVITY_CONTAINER_RADIUS:
      return 8;
    case DISTANCE_RECENT_ACTIVITY_CONTAINER_VERTICAL_MARGIN:
      return 10;
    case DISTANCE_RECENT_ACTIVITY_CONTAINER_VERTICAL_PADDING:
      return 6;
    case DISTANCE_RECENT_ACTIVITY_ROW_VERTICAL_PADDING:
      return 6;
    case DISTANCE_ACCOUNT_INFO_ROW_AVATAR_EMAIL:
      return 8;
    case DISTANCE_COLLABORATION_MESSAGING_AVATAR_FALLBACK_ICON_PADDING:
      return 2;
    case DISTANCE_COLLABORATION_MESSAGING_AVATAR_FALLBACK_ICON_BORDER_SIZE:
      return 1;
    case DISTANCE_TEXTFIELD_ACCOUNT_CARD_VERTICAL:
      return 4;
    case DISTANCE_FEATURE_FIRST_RUN_INFO_BOX_ICON_SIZE:
      return 16;
    case DISTANCE_FEATURE_FIRST_RUN_INFO_BOX_PADDING:
      return 12;
    case DISTANCE_FEATURE_FIRST_RUN_INFO_BOX_ROUNDED_BORDER_RADIUS:
      return 12;
    case DISTANCE_FEATURE_FIRST_RUN_INFO_BOX_VERTICAL:
      return 1;
    case DISTANCE_INFOBAR_BUTTON_VERTICAL_PADDING:
      return 8;
    case DISTANCE_INFOBAR_BUTTON_HORIZONTAL_PADDING:
      return 12;
    // Block-style Action App Menu layout constants.
    case DISTANCE_ACTION_APP_MENU_MINIMUM_WIDTH:
      return 440;
    case DISTANCE_ACTION_APP_MENU_CONTAINER_CORNER_RADIUS:
      return 8;
    case DISTANCE_ACTION_APP_MENU_ITEM_FIRST_TOP_PADDING:
      return 14;
    case DISTANCE_ACTION_APP_MENU_ITEM_LAST_BOTTOM_PADDING:
      return 14;
    case DISTANCE_ACTION_APP_MENU_ITEM_DEFAULT_VERTICAL_MARGIN:
      return 8;
    case DISTANCE_ACTION_APP_MENU_ICON_SIZE:
      return 16;
    case DISTANCE_ACTION_APP_MENU_FULL_ITEM_HEIGHT:
      return 32;
    case DISTANCE_ACTION_APP_MENU_BLOCK_ENTRY_WIDTH:
      return 105;
    case DISTANCE_ACTION_APP_MENU_BLOCK_ENTRY_HEIGHT:
      return 56;
    case DISTANCE_ACTION_APP_MENU_BLOCK_ENTRY_ICON_SIZE:
      return 20;
    case DISTANCE_ACTION_APP_MENU_FOOTER_BUTTON_SPACING:
      return 12;
    case DISTANCE_ACTION_APP_MENU_FOOTER_BUTTON_BETWEEN_CHILD_SPACING:
    case DISTANCE_ACTION_APP_MENU_FOOTER_BUTTON_CORNER_RADIUS:
    case DISTANCE_ACTION_APP_MENU_HEADER_VERTICAL_MARGIN:
    case DISTANCE_ACTION_APP_MENU_BLOCK_ROW_SPACING:
    case DISTANCE_ACTION_APP_MENU_BLOCK_ENTRY_CORNER_RADIUS:
      return 8;
    case DISTANCE_ACTION_APP_MENU_BLOCK_ENTRY_BETWEEN_CHILD_SPACING:
      return 4;
  }
  NOTREACHED();
}

int ChromeLayoutProvider::GetSnappedDialogWidth(int min_width) const {
  for (int snap_point :
       {kSmallDialogWidth, kMediumDialogWidth, kLargeDialogWidth}) {
    if (min_width <= snap_point) {
      return snap_point;
    }
  }

  return ((min_width + 15) / 16) * 16;
}

const views::TypographyProvider& ChromeLayoutProvider::GetTypographyProvider()
    const {
  return typography_provider_;
}

bool ChromeLayoutProvider::ShouldShowWindowIcon() const {
  return false;
}
