#include "flutter_window.h"

#include <optional>
#include <variant>

#include "flutter/generated_plugin_registrant.h"

FlutterWindow::FlutterWindow(const flutter::DartProject& project)
    : project_(project) {}

FlutterWindow::~FlutterWindow() {}

bool FlutterWindow::OnCreate() {
  if (!Win32Window::OnCreate()) {
    return false;
  }

  RECT frame = GetClientArea();

  // The size here must match the window dimensions to avoid unnecessary surface
  // creation / destruction in the startup path.
  flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
      frame.right - frame.left, frame.bottom - frame.top, project_);
  // Ensure that basic setup of the controller was successful.
  if (!flutter_controller_->engine() || !flutter_controller_->view()) {
    return false;
  }
  RegisterPlugins(flutter_controller_->engine());
  SetChildContent(flutter_controller_->view()->GetNativeWindow());

  // 注册窗口控制通道：Dart 端可请求按视频比例调整窗口尺寸。
  window_channel_ =
      std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
          flutter_controller_->engine()->messenger(), "app/window",
          &flutter::StandardMethodCodec::GetInstance());
  window_channel_->SetMethodCallHandler(
      [this](const flutter::MethodCall<flutter::EncodableValue>& call,
             std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>
                 result) {
        if (call.method_name() == "resizeToVideoAspect") {
          double aspect = 0.0;
          if (const auto* value = std::get_if<double>(call.arguments())) {
            aspect = *value;
          } else if (const auto* ivalue = std::get_if<int>(call.arguments())) {
            aspect = static_cast<double>(*ivalue);
          }
          ResizeToVideoAspect(aspect);
          result->Success();
        } else {
          result->NotImplemented();
        }
      });

  flutter_controller_->engine()->SetNextFrameCallback([&]() {
    this->Show();
  });

  // Flutter can complete the first frame before the "show window" callback is
  // registered. The following call ensures a frame is pending to ensure the
  // window is shown. It is a no-op if the first frame hasn't completed yet.
  flutter_controller_->ForceRedraw();

  return true;
}

void FlutterWindow::OnDestroy() {
  if (flutter_controller_) {
    flutter_controller_ = nullptr;
  }

  Win32Window::OnDestroy();
}

void FlutterWindow::ResizeToVideoAspect(double aspect) {
  if (aspect <= 0.0) {
    return;
  }
  HWND hwnd = GetHandle();
  if (!hwnd) {
    return;
  }

  RECT window_rect;
  RECT client_rect;
  GetWindowRect(hwnd, &window_rect);
  GetClientRect(hwnd, &client_rect);

  const int window_w = window_rect.right - window_rect.left;
  const int window_h = window_rect.bottom - window_rect.top;
  const int client_w = client_rect.right - client_rect.left;
  const int client_h = client_rect.bottom - client_rect.top;
  // 边框（含标题栏）占用的额外像素。
  const int border_w = window_w - client_w;
  const int border_h = window_h - client_h;

  // 保持窗口宽度不变，按比例计算客户区高度。
  int target_client_w = client_w;
  int target_client_h = static_cast<int>(target_client_w / aspect + 0.5);
  int target_window_w = window_w;
  int target_window_h = target_client_h + border_h;

  // 获取窗口所在显示器的可用工作区。
  HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
  MONITORINFO monitor_info;
  monitor_info.cbSize = sizeof(MONITORINFO);
  GetMonitorInfo(monitor, &monitor_info);
  const int work_h = monitor_info.rcWork.bottom - monitor_info.rcWork.top;

  // 高度超出屏幕：改为以屏幕高度为准的合适窗口大小。
  if (target_window_h > work_h) {
    target_window_h = work_h;
    target_client_h = target_window_h - border_h;
    target_client_w = static_cast<int>(target_client_h * aspect + 0.5);
    target_window_w = target_client_w + border_w;
  }

  // 计算位置：尽量保持当前左上角，同时确保窗口落在工作区内。
  int new_left = window_rect.left;
  int new_top = window_rect.top;
  if (new_left + target_window_w > monitor_info.rcWork.right) {
    new_left = monitor_info.rcWork.right - target_window_w;
  }
  if (new_top + target_window_h > monitor_info.rcWork.bottom) {
    new_top = monitor_info.rcWork.bottom - target_window_h;
  }
  if (new_left < monitor_info.rcWork.left) {
    new_left = monitor_info.rcWork.left;
  }
  if (new_top < monitor_info.rcWork.top) {
    new_top = monitor_info.rcWork.top;
  }

  SetWindowPos(hwnd, nullptr, new_left, new_top, target_window_w,
               target_window_h, SWP_NOZORDER | SWP_NOACTIVATE);
}

LRESULT
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
                              WPARAM const wparam,
                              LPARAM const lparam) noexcept {
  // Give Flutter, including plugins, an opportunity to handle window messages.
  if (flutter_controller_) {
    std::optional<LRESULT> result =
        flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
                                                      lparam);
    if (result) {
      return *result;
    }
  }

  switch (message) {
    case WM_FONTCHANGE:
      flutter_controller_->engine()->ReloadSystemFonts();
      break;
  }

  return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
}
