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

#include "ui/views/window/custom_frame_view.h"

#include <vector>

#include "base/macros.h"
#include "build/build_config.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/window/window_button_order_provider.h"

namespace views {

namespace {

// Allows for the control of whether or not the widget can minimize/maximize or
// not. This can be set after initial setup in order to allow testing of both
// forms of delegates. By default this can minimize and maximize.
class MinimizeAndMaximizeStateControlDelegate : public WidgetDelegateView {
 public:
  MinimizeAndMaximizeStateControlDelegate()
        : can_maximize_(true),
          can_minimize_(true) {}
  ~MinimizeAndMaximizeStateControlDelegate() override {}

  void set_can_maximize(bool can_maximize) {
    can_maximize_ = can_maximize;
  }

  void set_can_minimize(bool can_minimize) {
    can_minimize_ = can_minimize;
  }

  // WidgetDelegate:
  bool CanMaximize() const override { return can_maximize_; }
  bool CanMinimize() const override { return can_minimize_; }

 private:
  bool can_maximize_;
  bool can_minimize_;

  DISALLOW_COPY_AND_ASSIGN(MinimizeAndMaximizeStateControlDelegate);
};

}  // namespace

class CustomFrameViewTest : public ViewsTestBase {
 public:
  CustomFrameViewTest() {}
  ~CustomFrameViewTest() override {}

  CustomFrameView* custom_frame_view() {
    return custom_frame_view_;
  }

  MinimizeAndMaximizeStateControlDelegate*
        minimize_and_maximize_state_control_delegate() {
    return minimize_and_maximize_state_control_delegate_;
  }

  Widget* widget() {
    return widget_;
  }

  // ViewsTestBase:
  void SetUp() override;
  void TearDown() override;

 protected:
  const std::vector<views::FrameButton>& leading_buttons() {
    return WindowButtonOrderProvider::GetInstance()->leading_buttons();
  }

  const std::vector<views::FrameButton>& trailing_buttons() {
    return WindowButtonOrderProvider::GetInstance()->trailing_buttons();
  }

  ImageButton* minimize_button() {
    return custom_frame_view_->minimize_button_;
  }

  ImageButton* maximize_button() {
    return custom_frame_view_->maximize_button_;
  }

  ImageButton* restore_button() {
    return custom_frame_view_->restore_button_;
  }

  ImageButton* close_button() {
    return custom_frame_view_->close_button_;
  }

  gfx::Rect title_bounds() {
    return custom_frame_view_->title_bounds_;
  }

  void SetWindowButtonOrder(
      const std::vector<views::FrameButton> leading_buttons,
      const std::vector<views::FrameButton> trailing_buttons);

 private:
  // Parent container for |custom_frame_view_|
  Widget* widget_;

  // Owned by |widget_|
  CustomFrameView* custom_frame_view_;

  // Delegate of |widget_| which controls minimizing and maximizing
  MinimizeAndMaximizeStateControlDelegate*
        minimize_and_maximize_state_control_delegate_;

  DISALLOW_COPY_AND_ASSIGN(CustomFrameViewTest);
};

void CustomFrameViewTest::SetUp() {
  ViewsTestBase::SetUp();

  minimize_and_maximize_state_control_delegate_ =
      new MinimizeAndMaximizeStateControlDelegate;
  widget_ = new Widget;
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
  params.delegate = minimize_and_maximize_state_control_delegate_;
  params.remove_standard_frame = true;
  widget_->Init(params);

  custom_frame_view_ = new CustomFrameView;
  widget_->non_client_view()->SetFrameView(custom_frame_view_);
}

void CustomFrameViewTest::TearDown() {
  widget_->CloseNow();

  ViewsTestBase::TearDown();
}

void CustomFrameViewTest::SetWindowButtonOrder(
    const std::vector<views::FrameButton> leading_buttons,
    const std::vector<views::FrameButton> trailing_buttons) {
  WindowButtonOrderProvider::GetInstance()->
      SetWindowButtonOrder(leading_buttons, trailing_buttons);
}

// Tests that there is a default button ordering before initialization causes
// a configuration file check.
TEST_F(CustomFrameViewTest, DefaultButtons) {
  const std::vector<views::FrameButton>& trailing = trailing_buttons();
  EXPECT_EQ(trailing.size(), 3u);
  EXPECT_TRUE(leading_buttons().empty());
  EXPECT_EQ(trailing[0], FRAME_BUTTON_MINIMIZE);
  EXPECT_EQ(trailing[1], FRAME_BUTTON_MAXIMIZE);
  EXPECT_EQ(trailing[2], FRAME_BUTTON_CLOSE);
}

// Tests that layout places the buttons in order, that the restore button is
// hidden and the buttons are placed after the title.
TEST_F(CustomFrameViewTest, DefaultButtonLayout) {
  Widget* parent = widget();
  CustomFrameView* view = custom_frame_view();
  view->Init(parent);
  parent->SetBounds(gfx::Rect(0, 0, 300, 100));
  parent->Show();

  EXPECT_LT(minimize_button()->x(), maximize_button()->x());
  EXPECT_LT(maximize_button()->x(), close_button()->x());
  EXPECT_FALSE(restore_button()->visible());

  EXPECT_GT(minimize_button()->x(),
            title_bounds().x() + title_bounds().width());
}

// Tests that setting the buttons to leading places them before the title.
TEST_F(CustomFrameViewTest, LeadingButtonLayout) {
  Widget* parent = widget();
  CustomFrameView* view = custom_frame_view();

  std::vector<views::FrameButton> leading;
  leading.push_back(views::FRAME_BUTTON_CLOSE);
  leading.push_back(views::FRAME_BUTTON_MINIMIZE);
  leading.push_back(views::FRAME_BUTTON_MAXIMIZE);

  std::vector<views::FrameButton> trailing;

  SetWindowButtonOrder(leading, trailing);

  view->Init(parent);
  parent->SetBounds(gfx::Rect(0, 0, 300, 100));
  parent->Show();
  EXPECT_LT(close_button()->x(), minimize_button()->x());
  EXPECT_LT(minimize_button()->x(), maximize_button()->x());
  EXPECT_FALSE(restore_button()->visible());
  EXPECT_LT(maximize_button()->x() + maximize_button()->width(),
            title_bounds().x());
}

// Tests that layouts occurring while maximized swap the maximize button for the
// restore button
TEST_F(CustomFrameViewTest, MaximizeRevealsRestoreButton) {
  Widget* parent = widget();
  CustomFrameView* view = custom_frame_view();
  view->Init(parent);
  parent->SetBounds(gfx::Rect(0, 0, 300, 100));
  parent->Show();

  ASSERT_FALSE(restore_button()->visible());
  ASSERT_TRUE(maximize_button()->visible());

  parent->Maximize();
  view->Layout();

#if defined(OS_MACOSX)
  // Restore buttons do not exist on Mac. The maximize button is instead a kind
  // of toggle, but has no effect on frame decorations.
  EXPECT_FALSE(restore_button()->visible());
  EXPECT_TRUE(maximize_button()->visible());
#else
  EXPECT_TRUE(restore_button()->visible());
  EXPECT_FALSE(maximize_button()->visible());
#endif
}

// Tests that when the parent cannot maximize that the maximize button is not
// visible
TEST_F(CustomFrameViewTest, CannotMaximizeHidesButton) {
  Widget* parent = widget();
  CustomFrameView* view = custom_frame_view();
  MinimizeAndMaximizeStateControlDelegate* delegate =
        minimize_and_maximize_state_control_delegate();
  delegate->set_can_maximize(false);

  view->Init(parent);
  parent->SetBounds(gfx::Rect(0, 0, 300, 100));
  parent->Show();

  EXPECT_FALSE(restore_button()->visible());
  EXPECT_FALSE(maximize_button()->visible());
}

// Tests that when the parent cannot minimize that the minimize button is not
// visible
TEST_F(CustomFrameViewTest, CannotMinimizeHidesButton) {
  Widget* parent = widget();
  CustomFrameView* view = custom_frame_view();
  MinimizeAndMaximizeStateControlDelegate* delegate =
      minimize_and_maximize_state_control_delegate();
  delegate->set_can_minimize(false);

  view->Init(parent);
  parent->SetBounds(gfx::Rect(0, 0, 300, 100));
  parent->Show();

  EXPECT_FALSE(minimize_button()->visible());
}

// Tests that when maximized that the edge button has an increased width.
TEST_F(CustomFrameViewTest, LargerEdgeButtonsWhenMaximized) {
  Widget* parent = widget();
  CustomFrameView* view = custom_frame_view();

  // Custom ordering to have a button on each edge.
  std::vector<views::FrameButton> leading;
  leading.push_back(views::FRAME_BUTTON_CLOSE);
  leading.push_back(views::FRAME_BUTTON_MAXIMIZE);
  std::vector<views::FrameButton> trailing;
  trailing.push_back(views::FRAME_BUTTON_MINIMIZE);
  SetWindowButtonOrder(leading, trailing);

  view->Init(parent);
  parent->SetBounds(gfx::Rect(0, 0, 300, 100));
  parent->Show();

  gfx::Rect close_button_initial_bounds = close_button()->bounds();
  gfx::Rect minimize_button_initial_bounds = minimize_button()->bounds();

  parent->Maximize();
  view->Layout();

#if defined(OS_MACOSX)
  // On Mac, "Maximize" should not alter the frame. Only fullscreen does that.
  EXPECT_EQ(close_button()->bounds().width(),
            close_button_initial_bounds.width());
  EXPECT_EQ(minimize_button()->bounds().width(),
            minimize_button_initial_bounds.width());
#else
  EXPECT_GT(close_button()->bounds().width(),
            close_button_initial_bounds.width());
  EXPECT_GT(minimize_button()->bounds().width(),
            minimize_button_initial_bounds.width());
#endif
}

}  // namespace views
