// Copyright (c) 2026 Athul Iddya.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "shell/browser/ui/views/electron_frame_view_layout_linux.h"

#include "base/functional/bind.h"
#include "shell/browser/native_window_views.h"
#include "ui/linux/nav_button_provider.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/caption_button_layout_constants.h"
#include "ui/views/window/frame_view_linux.h"

namespace electron {

namespace {

ui::WindowFrameProvider* NullFrameProvider(bool tiled, bool maximized) {
  return nullptr;
}

}  // namespace

ElectronFrameViewLayoutLinux::ElectronFrameViewLayoutLinux(
    NativeWindowViews* window,
    ui::NavButtonProvider* nav_buttons)
    : views::NativeFrameViewLayoutLinux(
          nav_buttons,
          base::BindRepeating(&NullFrameProvider)),
      window_(window),
      nav_buttons_(nav_buttons),
      wants_frame_(
          !window->IsTranslucent() &&
          (window->HasShadow() || window->IsWindowControlsOverlayEnabled())) {}

ElectronFrameViewLayoutLinux::~ElectronFrameViewLayoutLinux() = default;

gfx::Insets ElectronFrameViewLayoutLinux::GetRestoredFrameBorderInsets() const {
  if (window_->IsTranslucent())
    return gfx::Insets();

  return views::FrameViewLayoutLinux::GetRestoredFrameBorderInsets();
}

int ElectronFrameViewLayoutLinux::GetWCOContentHeight() const {
  int custom_height = window_->titlebar_overlay_height();
  if (custom_height)
    return custom_height;

  if (nav_buttons_)
    return nav_buttons_->GetNavButtonHeight(
               view()->GetWidget()->IsMaximized()) +
           nav_buttons_->GetTopAreaSpacing().top() +
           nav_buttons_->GetTopAreaSpacing().bottom();

  return views::GetCaptionButtonLayoutSize(
             views::CaptionButtonLayoutSize::kNonBrowserCaption)
      .height();
}

int ElectronFrameViewLayoutLinux::GetTopAreaHeight() const {
  if (!ShouldShowTitlebarAndBorder())
    return 0;

  int border_top = GetFrameBorderInsets().top();
  return border_top + (window_->IsWindowControlsOverlayEnabled()
                           ? GetWCOContentHeight()
                           : 0);
}

int ElectronFrameViewLayoutLinux::GetClientTopOffset() const {
  // Content starts at the frame border; WCO buttons overlay via layers.
  return GetFrameBorderInsets().top();
}

gfx::ShadowValues ElectronFrameViewLayoutLinux::GetShadowValues(
    bool active) const {
  if (!window_->HasShadow())
    return gfx::ShadowValues();
  // FrameViewLayoutLinux::GetShadowValues() returns {} when
  // widget->IsMaximized()/IsFullscreen(), which makes
  // GetRestoredFrameBorderInsets() — and so CalculateInsetsInDIP() — depend
  // on the optimistic applied_state set by TriggerStateChanges.
  // On compositors that decline set_maximized (e.g.
  // sway floating) the two CalculateInsetsInDIP calls per configure then
  // disagree and the window shrinks by ~30x41px per maximize().
  if (tiled())
    return gfx::ShadowValues();
  auto* provider = views::LayoutProvider::Get();
  if (!provider)
    return gfx::ShadowValues();
  return gfx::ShadowValue::MakeMdShadowValues(
      provider->GetShadowElevationMetric(active ? views::Emphasis::kMaximum
                                                : views::Emphasis::kMedium));
}

gfx::RoundedCornersF ElectronFrameViewLayoutLinux::GetCornerRadii() const {
  if (!window_->has_rounded_corners())
    return gfx::RoundedCornersF();
  if (window_->IsMaximized() || window_->IsFullscreen())
    return gfx::RoundedCornersF();

  // Chrome rounds only the top corners, so mirror its default value to all 4.
  return gfx::RoundedCornersF(
      views::FrameViewLayoutLinux::GetCornerRadii().upper_left());
}

bool ElectronFrameViewLayoutLinux::ShouldShowTitlebarAndBorder() const {
  if (window_->IsTranslucent())
    return false;
  return !view()->GetWidget()->IsFullscreen();
}

gfx::Insets ElectronFrameViewLayoutLinux::GetTopAreaBorderInsets() const {
  return gfx::Insets();
}

views::FrameViewLayoutLinux::ButtonLayoutParams
ElectronFrameViewLayoutLinux::GetButtonLayoutParams(
    views::FrameButton button_id,
    views::Button* button) const {
  return nav_buttons_
             ? views::NativeFrameViewLayoutLinux::GetButtonLayoutParams(
                   button_id, button)
             : views::FrameViewLayoutLinux::GetButtonLayoutParams(button_id,
                                                                  button);
}

gfx::Insets ElectronFrameViewLayoutLinux::GetTopAreaSpacing() const {
  return nav_buttons_ ? views::NativeFrameViewLayoutLinux::GetTopAreaSpacing()
                      : gfx::Insets();
}

gfx::Rect ElectronFrameViewLayoutLinux::GetWindowControlsOverlayRect() const {
  // Convert frame-view titlebar coordinates to client-view-relative.
  gfx::Insets insets = GetFrameBorderInsets();
  int client_left = insets.left();
  int client_right = view()->width() - insets.right();
  int x = std::max(0, minimum_titlebar_x_ - client_left);
  int right = std::min(client_right, maximum_titlebar_x_) - client_left;
  int height = GetTopAreaHeight() - GetClientTopOffset();
  return gfx::Rect(x, 0, std::max(0, right - x), height);
}

gfx::Rect ElectronFrameViewLayoutLinux::GetLeadingButtonRect() const {
  gfx::Insets insets = GetFrameBorderInsets();
  int x = insets.left();
  if (minimum_titlebar_x_ <= x)
    return gfx::Rect();
  int y = GetClientTopOffset();
  int height = GetTopAreaHeight() - y;
  return gfx::Rect(x, y, minimum_titlebar_x_ - x, height);
}

gfx::Rect ElectronFrameViewLayoutLinux::GetTrailingButtonRect() const {
  gfx::Insets insets = GetFrameBorderInsets();
  int right = view()->width() - insets.right();
  if (maximum_titlebar_x_ >= right)
    return gfx::Rect();
  int y = GetClientTopOffset();
  int height = GetTopAreaHeight() - y;
  return gfx::Rect(maximum_titlebar_x_, y, right - maximum_titlebar_x_, height);
}

void ElectronFrameViewLayoutLinux::LayoutWindowControls() {
  if (!window_->IsWindowControlsOverlayEnabled())
    return;

  views::FrameViewLayoutLinux::LayoutWindowControls();
}

}  // namespace electron
