// 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.

#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_TEST_KEYBOARD_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_TEST_KEYBOARD_H_

#include <windows.h>

#include <functional>
#include <string>

#include "flutter/fml/macros.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/windows/testing/engine_modifier.h"
#include "flutter/shell/platform/windows/testing/wm_builders.h"

#include "gtest/gtest.h"

struct _FlutterPlatformMessageResponseHandle {
  FlutterDesktopBinaryReply callback;
  void* user_data;
};

namespace flutter {
namespace testing {
::testing::AssertionResult _EventEquals(const char* expr_event,
                                        const char* expr_expected,
                                        const FlutterKeyEvent& event,
                                        const FlutterKeyEvent& expected);

// Clone string onto the heap.
//
// If #string is nullptr, returns nullptr. Otherwise, the returned pointer must
// be freed with delete[].
char* clone_string(const char* string);

// Creates a valid Windows LPARAM for WM_KEYDOWN and WM_CHAR from parameters
// given.
//
// While |CreateKeyEventLparam| is flexible, it's recommended to use dedicated
// functions in wm_builders.h, such as |WmKeyDownInfo|.
LPARAM CreateKeyEventLparam(USHORT scancode,
                            bool extended,
                            bool was_down,
                            USHORT repeat_count = 1,
                            bool context_code = 0,
                            bool transition_state = 1);

class MockKeyResponseController {
 public:
  using ResponseCallback = std::function<void(bool)>;
  using EmbedderCallbackHandler =
      std::function<void(const FlutterKeyEvent*, ResponseCallback)>;
  using ChannelCallbackHandler = std::function<void(ResponseCallback)>;
  using TextInputCallbackHandler =
      std::function<void(std::unique_ptr<rapidjson::Document>)>;

  MockKeyResponseController()
      : channel_response_(ChannelRespondFalse),
        embedder_response_(EmbedderRespondFalse),
        text_input_response_(
            [](std::unique_ptr<rapidjson::Document> document) {}) {}

  void SetChannelResponse(ChannelCallbackHandler handler) {
    channel_response_ = std::move(handler);
  }

  void SetEmbedderResponse(EmbedderCallbackHandler handler) {
    embedder_response_ = std::move(handler);
  }

  void SetTextInputResponse(TextInputCallbackHandler handler) {
    text_input_response_ = std::move(handler);
  }

  void HandleChannelMessage(ResponseCallback callback) {
    channel_response_(callback);
  }

  void HandleEmbedderMessage(const FlutterKeyEvent* event,
                             ResponseCallback callback) {
    embedder_response_(event, std::move(callback));
  }

  void HandleTextInputMessage(std::unique_ptr<rapidjson::Document> document) {
    text_input_response_(std::move(document));
  }

 private:
  EmbedderCallbackHandler embedder_response_;
  ChannelCallbackHandler channel_response_;
  TextInputCallbackHandler text_input_response_;

  static void ChannelRespondFalse(ResponseCallback callback) {
    callback(false);
  }

  static void EmbedderRespondFalse(const FlutterKeyEvent* event,
                                   ResponseCallback callback) {
    callback(false);
  }

  FML_DISALLOW_COPY_AND_ASSIGN(MockKeyResponseController);
};

void MockEmbedderApiForKeyboard(
    EngineModifier& modifier,
    std::shared_ptr<MockKeyResponseController> response_controller);

// Simulate a message queue for WM messages.
//
// Subclasses must implement |Win32SendMessage| for how dispatched messages are
// processed.
class MockMessageQueue {
 protected:
  // Push a message to the message queue without dispatching it.
  void PushBack(const Win32Message* message);

  // Dispatch the first message of the message queue and return its result.
  //
  // This method asserts that the queue is not empty.
  LRESULT DispatchFront();

  // Peak the next message in the message queue.
  //
  // See Win32's |PeekMessage| for documentation.
  BOOL Win32PeekMessage(LPMSG lpMsg,
                        UINT wMsgFilterMin,
                        UINT wMsgFilterMax,
                        UINT wRemoveMsg);

  // Simulate dispatching a message to the system.
  virtual LRESULT Win32SendMessage(UINT const message,
                                   WPARAM const wparam,
                                   LPARAM const lparam) = 0;

  std::list<Win32Message> _pending_messages;
  std::list<Win32Message> _sent_messages;
};

}  // namespace testing
}  // namespace flutter

// Expect the |_target| FlutterKeyEvent has the required properties.
#define EXPECT_EVENT_EQUALS(_target, _type, _physical, _logical, _character, \
                            _synthesized)                                    \
  EXPECT_PRED_FORMAT2(_EventEquals, _target,                                 \
                      (FlutterKeyEvent{                                      \
                          .type = _type,                                     \
                          .physical = _physical,                             \
                          .logical = _logical,                               \
                          .character = _character,                           \
                          .synthesized = _synthesized,                       \
                      }));

#endif  // FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_TEST_KEYBOARD_H_
