// Copyright 2013 The Flutter 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 "flutter/shell/platform/windows/cursor_handler.h"

#include <memory>
#include <vector>

#include "flutter/fml/macros.h"
#include "flutter/shell/platform/common/client_wrapper/include/flutter/method_result_functions.h"
#include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_message_codec.h"
#include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_method_codec.h"
#include "flutter/shell/platform/windows/flutter_windows_view.h"
#include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h"
#include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
#include "flutter/shell/platform/windows/testing/test_binary_messenger.h"
#include "flutter/shell/platform/windows/testing/windows_test.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace flutter {
namespace testing {

namespace {
using ::testing::_;
using ::testing::NotNull;
using ::testing::Return;

static constexpr char kChannelName[] = "flutter/mousecursor";

static constexpr char kActivateSystemCursorMethod[] = "activateSystemCursor";
static constexpr char kCreateCustomCursorMethod[] =
    "createCustomCursor/windows";
static constexpr char kSetCustomCursorMethod[] = "setCustomCursor/windows";
static constexpr char kDeleteCustomCursorMethod[] =
    "deleteCustomCursor/windows";

void SimulateCursorMessage(TestBinaryMessenger* messenger,
                           const std::string& method_name,
                           std::unique_ptr<EncodableValue> arguments,
                           MethodResult<EncodableValue>* result_handler) {
  MethodCall<> call(method_name, std::move(arguments));

  auto message = StandardMethodCodec::GetInstance().EncodeMethodCall(call);

  EXPECT_TRUE(messenger->SimulateEngineMessage(
      kChannelName, message->data(), message->size(),
      [&result_handler](const uint8_t* reply, size_t reply_size) {
        StandardMethodCodec::GetInstance().DecodeAndProcessResponseEnvelope(
            reply, reply_size, result_handler);
      }));
}

}  // namespace

class CursorHandlerTest : public WindowsTest {
 public:
  CursorHandlerTest() = default;
  virtual ~CursorHandlerTest() = default;

 protected:
  FlutterWindowsEngine* engine() { return engine_.get(); }
  FlutterWindowsView* view() { return view_.get(); }
  MockWindowBindingHandler* window() { return window_; }

  void use_headless_engine() {
    FlutterWindowsEngineBuilder builder{GetContext()};

    engine_ = builder.Build();
  }

  void use_engine_with_view() {
    FlutterWindowsEngineBuilder builder{GetContext()};

    auto window = std::make_unique<MockWindowBindingHandler>();

    window_ = window.get();
    EXPECT_CALL(*window_, SetView).Times(1);
    EXPECT_CALL(*window_, GetRenderTarget).WillOnce(Return(nullptr));

    engine_ = builder.Build();
    view_ = std::make_unique<FlutterWindowsView>(std::move(window));

    engine_->SetView(view_.get());
  }

 private:
  std::unique_ptr<FlutterWindowsEngine> engine_;
  std::unique_ptr<FlutterWindowsView> view_;
  MockWindowBindingHandler* window_;

  FML_DISALLOW_COPY_AND_ASSIGN(CursorHandlerTest);
};

TEST_F(CursorHandlerTest, ActivateSystemCursor) {
  use_engine_with_view();

  TestBinaryMessenger messenger;
  CursorHandler cursor_handler(&messenger, engine());

  EXPECT_CALL(*window(), UpdateFlutterCursor("click")).Times(1);

  bool success = false;
  MethodResultFunctions<> result_handler(
      [&success](const EncodableValue* result) {
        success = true;
        EXPECT_EQ(result, nullptr);
      },
      nullptr, nullptr);

  SimulateCursorMessage(&messenger, kActivateSystemCursorMethod,
                        std::make_unique<EncodableValue>(EncodableMap{
                            {EncodableValue("device"), EncodableValue(0)},
                            {EncodableValue("kind"), EncodableValue("click")},
                        }),
                        &result_handler);

  EXPECT_TRUE(success);
}

TEST_F(CursorHandlerTest, ActivateSystemCursorRequiresView) {
  use_headless_engine();

  TestBinaryMessenger messenger;
  CursorHandler cursor_handler(&messenger, engine());

  bool error = false;
  MethodResultFunctions<> result_handler(
      nullptr,
      [&error](const std::string& error_code, const std::string& error_message,
               const EncodableValue* value) {
        error = true;
        EXPECT_EQ(error_message,
                  "Cursor is not available in Windows headless mode");
      },
      nullptr);

  SimulateCursorMessage(&messenger, kActivateSystemCursorMethod,
                        std::make_unique<EncodableValue>(EncodableMap{
                            {EncodableValue("device"), EncodableValue(0)},
                            {EncodableValue("kind"), EncodableValue("click")},
                        }),
                        &result_handler);

  EXPECT_TRUE(error);
}

TEST_F(CursorHandlerTest, CreateCustomCursor) {
  use_engine_with_view();

  TestBinaryMessenger messenger;
  CursorHandler cursor_handler(&messenger, engine());

  // Create a 4x4 raw BGRA test cursor buffer.
  std::vector<uint8_t> buffer(4 * 4 * 4, 0);

  bool success = false;
  MethodResultFunctions<> result_handler(
      [&success](const EncodableValue* result) {
        success = true;
        EXPECT_EQ(std::get<std::string>(*result), "hello");
      },
      nullptr, nullptr);

  SimulateCursorMessage(&messenger, kCreateCustomCursorMethod,
                        std::make_unique<EncodableValue>(EncodableMap{
                            {EncodableValue("name"), EncodableValue("hello")},
                            {EncodableValue("buffer"), EncodableValue(buffer)},
                            {EncodableValue("width"), EncodableValue(4)},
                            {EncodableValue("height"), EncodableValue(4)},
                            {EncodableValue("hotX"), EncodableValue(0.0)},
                            {EncodableValue("hotY"), EncodableValue(0.0)},
                        }),
                        &result_handler);

  EXPECT_TRUE(success);
}

TEST_F(CursorHandlerTest, SetCustomCursor) {
  use_engine_with_view();

  TestBinaryMessenger messenger;
  CursorHandler cursor_handler(&messenger, engine());

  // Create a 4x4 raw BGRA test cursor buffer.
  std::vector<uint8_t> buffer(4 * 4 * 4, 0);

  bool success = false;
  MethodResultFunctions<> create_result_handler(nullptr, nullptr, nullptr);
  MethodResultFunctions<> set_result_handler(
      [&success](const EncodableValue* result) {
        success = true;
        EXPECT_EQ(result, nullptr);
      },
      nullptr, nullptr);

  EXPECT_CALL(*window(), SetFlutterCursor(/*cursor=*/NotNull())).Times(1);

  SimulateCursorMessage(&messenger, kCreateCustomCursorMethod,
                        std::make_unique<EncodableValue>(EncodableMap{
                            {EncodableValue("name"), EncodableValue("hello")},
                            {EncodableValue("buffer"), EncodableValue(buffer)},
                            {EncodableValue("width"), EncodableValue(4)},
                            {EncodableValue("height"), EncodableValue(4)},
                            {EncodableValue("hotX"), EncodableValue(0.0)},
                            {EncodableValue("hotY"), EncodableValue(0.0)},
                        }),
                        &create_result_handler);

  SimulateCursorMessage(&messenger, kSetCustomCursorMethod,
                        std::make_unique<EncodableValue>(EncodableMap{
                            {EncodableValue("name"), EncodableValue("hello")},
                        }),
                        &set_result_handler);

  EXPECT_TRUE(success);
}

TEST_F(CursorHandlerTest, SetCustomCursorRequiresView) {
  use_headless_engine();

  TestBinaryMessenger messenger;
  CursorHandler cursor_handler(&messenger, engine());

  // Create a 4x4 raw BGRA test cursor buffer.
  std::vector<uint8_t> buffer(4 * 4 * 4, 0);

  bool error = false;
  MethodResultFunctions<> create_result_handler(nullptr, nullptr, nullptr);
  MethodResultFunctions<> set_result_handler(
      nullptr,
      [&error](const std::string& error_code, const std::string& error_message,
               const EncodableValue* value) {
        error = true;
        EXPECT_EQ(error_message,
                  "Cursor is not available in Windows headless mode");
      },
      nullptr);

  SimulateCursorMessage(&messenger, kCreateCustomCursorMethod,
                        std::make_unique<EncodableValue>(EncodableMap{
                            {EncodableValue("name"), EncodableValue("hello")},
                            {EncodableValue("buffer"), EncodableValue(buffer)},
                            {EncodableValue("width"), EncodableValue(4)},
                            {EncodableValue("height"), EncodableValue(4)},
                            {EncodableValue("hotX"), EncodableValue(0.0)},
                            {EncodableValue("hotY"), EncodableValue(0.0)},
                        }),
                        &create_result_handler);

  SimulateCursorMessage(&messenger, kSetCustomCursorMethod,
                        std::make_unique<EncodableValue>(EncodableMap{
                            {EncodableValue("name"), EncodableValue("hello")},
                        }),
                        &set_result_handler);

  EXPECT_TRUE(error);
}

TEST_F(CursorHandlerTest, SetNonexistentCustomCursor) {
  use_engine_with_view();

  TestBinaryMessenger messenger;
  CursorHandler cursor_handler(&messenger, engine());

  bool error = false;
  MethodResultFunctions<> result_handler(
      nullptr,
      [&error](const std::string& error_code, const std::string& error_message,
               const EncodableValue* value) {
        error = true;
        EXPECT_EQ(
            error_message,
            "The custom cursor identified by the argument key cannot be found");
      },
      nullptr);

  EXPECT_CALL(*window(), SetFlutterCursor).Times(0);

  SimulateCursorMessage(&messenger, kSetCustomCursorMethod,
                        std::make_unique<EncodableValue>(EncodableMap{
                            {EncodableValue("name"), EncodableValue("hello")},
                        }),
                        &result_handler);

  EXPECT_TRUE(error);
}

TEST_F(CursorHandlerTest, DeleteCustomCursor) {
  use_engine_with_view();

  TestBinaryMessenger messenger;
  CursorHandler cursor_handler(&messenger, engine());

  // Create a 4x4 raw BGRA test cursor buffer.
  std::vector<uint8_t> buffer(4 * 4 * 4, 0);

  bool success = false;
  MethodResultFunctions<> create_result_handler(nullptr, nullptr, nullptr);
  MethodResultFunctions<> delete_result_handler(
      [&success](const EncodableValue* result) {
        success = true;
        EXPECT_EQ(result, nullptr);
      },
      nullptr, nullptr);

  SimulateCursorMessage(&messenger, kCreateCustomCursorMethod,
                        std::make_unique<EncodableValue>(EncodableMap{
                            {EncodableValue("name"), EncodableValue("hello")},
                            {EncodableValue("buffer"), EncodableValue(buffer)},
                            {EncodableValue("width"), EncodableValue(4)},
                            {EncodableValue("height"), EncodableValue(4)},
                            {EncodableValue("hotX"), EncodableValue(0.0)},
                            {EncodableValue("hotY"), EncodableValue(0.0)},
                        }),
                        &create_result_handler);

  SimulateCursorMessage(&messenger, kDeleteCustomCursorMethod,
                        std::make_unique<EncodableValue>(EncodableMap{
                            {EncodableValue("name"), EncodableValue("hello")},
                        }),
                        &delete_result_handler);

  EXPECT_TRUE(success);
}

TEST_F(CursorHandlerTest, DeleteNonexistentCustomCursor) {
  use_engine_with_view();

  TestBinaryMessenger messenger;
  CursorHandler cursor_handler(&messenger, engine());

  bool success = false;
  MethodResultFunctions<> result_handler(
      [&success](const EncodableValue* result) {
        success = true;
        EXPECT_EQ(result, nullptr);
      },
      nullptr, nullptr);

  SimulateCursorMessage(&messenger, kDeleteCustomCursorMethod,
                        std::make_unique<EncodableValue>(EncodableMap{
                            {EncodableValue("name"), EncodableValue("fake")},
                        }),
                        &result_handler);

  EXPECT_TRUE(success);
}

}  // namespace testing
}  // namespace flutter
