#include <iostream>

#include "platform_window/platform_window.h"

#include <windows.h>

#define WINDOW_HANDLE static_cast<HWND>(window_handle_)

bool                          mouse_tracking_ = FALSE;
std::vector<platform_window*> windows;

platform_window* get_window_from_hwnd(const HWND hwnd) {
	for (const auto& window: windows) {
		if (window->get_window_handle() == hwnd) {
			return window;
		}
	}
	return nullptr;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	bool processed = false;
	// 窗口关闭事件
	if (uMsg == WM_CLOSE) {
		std::erase_if(windows, [hwnd](platform_window* window) {
			if (window->get_window_handle() == hwnd) {
				window->close();
				return true;
			}
			return false;
		});
		processed = true;
	}

	// 窗口销毁事件
	if (uMsg == WM_DESTROY) {
		PostQuitMessage(0);
		processed = true;
	}

	// 窗口移动事件
	if (uMsg == WM_MOVE) {
		if (const auto window = get_window_from_hwnd(hwnd)) { window->on_move(LOWORD(lParam), HIWORD(lParam)); }
		processed = true;
	}

	// 窗口大小改变事件
	if (uMsg == WM_SIZE) {
		if (const auto window = get_window_from_hwnd(hwnd)) {
			window->on_resize(LOWORD(lParam), HIWORD(lParam));
		}
		processed = true;
	}

	// 鼠标移动事件
	if (uMsg == WM_MOUSEMOVE) {
		if (const auto window = get_window_from_hwnd(hwnd)) {
			const int x = LOWORD(lParam);
			const int y = HIWORD(lParam);
			const Eigen::Vector2f pos(static_cast<float>(x), static_cast<float>(y));
			window->handle_mouse_move(pos);
		}
		if (!mouse_tracking_) {
			TRACKMOUSEEVENT tme;
			tme.cbSize = sizeof(TRACKMOUSEEVENT);
			tme.dwFlags = TME_LEAVE;
			tme.hwndTrack = hwnd;
			tme.dwHoverTime = HOVER_DEFAULT;
			TrackMouseEvent(&tme);
			mouse_tracking_ = TRUE;
		}
		processed = true;
	}

	// 鼠标按键事件
	const auto lmr_button = uMsg >= WM_LBUTTONDOWN && uMsg <= WM_MBUTTONDBLCLK;
	const auto x_button = uMsg >= WM_XBUTTONDOWN && uMsg <= WM_XBUTTONDBLCLK;
	if (lmr_button || x_button) {
		if (const auto window = get_window_from_hwnd(hwnd)) {
			const int x = LOWORD(lParam);
			const int y = HIWORD(lParam);
			const Eigen::Vector2f pos(static_cast<float>(x), static_cast<float>(y));
			const auto action = platform_event_to_mouse_action(uMsg, wParam);
			const auto button = platform_event_to_mouse_button(uMsg, wParam);
			if (action == mouse_action::press)
				window->handle_mouse_button_down(pos, button);
			else if (action == mouse_action::release)
				window->handle_mouse_button_up(pos, button);
			else if (action == mouse_action::dbl_click)
				window->handle_mouse_dbl_click(pos, button);
		}
		processed = true;
	}

	// 鼠标滚轮事件
	if (uMsg == WM_MOUSEWHEEL || uMsg == WM_MOUSEHWHEEL) {
		if (const auto window = get_window_from_hwnd(hwnd)) {
			const int x = LOWORD(lParam);
			const int y = HIWORD(lParam);
			const Eigen::Vector2f pos(static_cast<float>(x), static_cast<float>(y));
			const auto delta = platform_event_to_wheel_event(uMsg, wParam, lParam);
			window->handle_mouse_wheel(pos, delta);
		}
		processed = true;
	}

	if (uMsg == WM_MOUSELEAVE) {
		mouse_tracking_ = FALSE;
		if (const auto window = get_window_from_hwnd(hwnd)) {
			window->handle_mouse_leave();
		}
		processed = true;
	}

	if (processed) { return 0; }
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

platform_window::platform_window(int32_t in_width, int32_t in_height, const wchar_t* in_title) {
	WNDCLASS wc      = {};
	wc.lpfnWndProc   = WindowProc;
	wc.hInstance     = GetModuleHandle(nullptr);
	wc.lpszClassName = L"mirage_window_class";
	wc.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
	wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
	wc.hIcon         = LoadIcon(NULL, IDI_WINLOGO);
	RegisterClass(&wc);

	RECT rect = { 0, 0, in_width, in_height };
	AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);

	window_handle_ = (void*)CreateWindowW(
		L"mirage_window_class", in_title,
		WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		CW_USEDEFAULT, CW_USEDEFAULT,
		rect.right - rect.left, rect.bottom - rect.top,
		NULL, NULL, GetModuleHandleW(NULL), NULL
	);

	if (!window_handle_) {
		std::cerr << "Failed to create window" << std::endl;
		return;
	}

	windows.push_back(this);
}

void platform_window::show() { ShowWindow(WINDOW_HANDLE, SW_SHOW); }
void platform_window::hide() { ShowWindow(WINDOW_HANDLE, SW_HIDE); }

void platform_window::close() {
	if (!window_handle_) { return; }
	on_close_delegate.broadcast(this);
	DestroyWindow(WINDOW_HANDLE);
	window_handle_ = nullptr;
}

void platform_window::maximize() { ShowWindow(WINDOW_HANDLE, SW_MAXIMIZE); }
void platform_window::minimize() { ShowWindow(WINDOW_HANDLE, SW_MINIMIZE); }

bool platform_window::is_visible() const {
	// 判断最小化和隐藏状态
	return IsWindowVisible(WINDOW_HANDLE) && !IsIconic(WINDOW_HANDLE);
}

void platform_window::move(int x, int y) {
	RECT rect;
	GetWindowRect(WINDOW_HANDLE, &rect);
	MoveWindow(WINDOW_HANDLE, x, y, rect.right - rect.left, rect.bottom - rect.top, TRUE);

	on_move(x, y);
}

void platform_window::resize(int width, int height) {
	RECT rect;
	GetWindowRect(WINDOW_HANDLE, &rect);
	MoveWindow(WINDOW_HANDLE, rect.left, rect.top, width, height, TRUE);
}

Eigen::Vector2i platform_window::get_window_size() const {
	// 获取窗口大小, 包括边框和标题栏
	RECT rect;
	if (GetWindowRect(WINDOW_HANDLE, &rect)) {
		int width  = rect.right - rect.left;
		int height = rect.bottom - rect.top;
		return Eigen::Vector2i(width, height);
	}
	return Eigen::Vector2i(0, 0);
}

Eigen::Vector2i platform_window::get_window_frame_size() const {
	// 获取窗口大小, 不包括边框和标题栏 (客户区大小)
	RECT rect;
	if (GetClientRect(WINDOW_HANDLE, &rect)) {
		int width  = rect.right - rect.left;
		int height = rect.bottom - rect.top;
		return Eigen::Vector2i(width, height);
	}
	return Eigen::Vector2i(0, 0);
}

float platform_window::get_window_dpi_scale() const {
	return 1.f;
}

Eigen::Vector2i platform_window::get_window_position() const {
	RECT rect;
	if (!GetWindowRect(WINDOW_HANDLE, &rect)) { return {}; }
	return { rect.left, rect.top };
}

void* platform_window::get_window_handle() const { return window_handle_; }

platform_window& platform_window::set_title(const wchar_t* title) {
	SetWindowText(WINDOW_HANDLE, title);
	return *this;
}

platform_window& platform_window::set_has_minimize_button(bool has_minimize_button) {
	auto style = GetWindowLong(WINDOW_HANDLE, GWL_STYLE);
	if (has_minimize_button) { style |= WS_MINIMIZEBOX; } else { style &= ~WS_MINIMIZEBOX; }
	SetWindowLong(WINDOW_HANDLE, GWL_STYLE, style);
	return *this;
}

platform_window& platform_window::set_has_maximize_button(bool has_maximize_button) {
	auto style = GetWindowLong(WINDOW_HANDLE, GWL_STYLE);
	if (has_maximize_button) { style |= WS_MAXIMIZEBOX; } else { style &= ~WS_MAXIMIZEBOX; }
	SetWindowLong(WINDOW_HANDLE, GWL_STYLE, style);
	return *this;
}

platform_window& platform_window::set_has_close_button(bool has_close_button) {
	auto style = GetWindowLong(WINDOW_HANDLE, GWL_STYLE);
	if (has_close_button) { style |= WS_SYSMENU; } else { style &= ~WS_SYSMENU; }
	SetWindowLong(WINDOW_HANDLE, GWL_STYLE, style);
	return *this;
}

platform_window& platform_window::set_has_border(bool has_border) {
	auto style = GetWindowLong(WINDOW_HANDLE, GWL_STYLE);
	if (has_border) { style |= WS_BORDER; } else { style &= ~WS_BORDER; }
	SetWindowLong(WINDOW_HANDLE, GWL_STYLE, style);
	return *this;
}

platform_window& platform_window::set_has_caption(bool has_caption) {
	auto style = GetWindowLong(WINDOW_HANDLE, GWL_STYLE);
	if (has_caption) { style |= WS_CAPTION; } else { style &= ~WS_CAPTION; }
	SetWindowLong(WINDOW_HANDLE, GWL_STYLE, style);
	return *this;
}

platform_window& platform_window::set_has_resizable_border(bool has_resizable_border) {
	auto style = GetWindowLong(WINDOW_HANDLE, GWL_STYLE);
	if (has_resizable_border) { style |= WS_THICKFRAME; } else { style &= ~WS_THICKFRAME; }
	SetWindowLong(WINDOW_HANDLE, GWL_STYLE, style);
	return *this;
}

platform_window& platform_window::set_topmost(bool is_topmost) {
	auto style = GetWindowLong(WINDOW_HANDLE, GWL_EXSTYLE);
	if (is_topmost) { style |= WS_EX_TOPMOST; } else { style &= ~WS_EX_TOPMOST; }
	SetWindowLong(WINDOW_HANDLE, GWL_EXSTYLE, style);
	return *this;
}

bool platform_window::poll_events() {
	MSG  msg;
	bool has_messages = false;
	while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
		has_messages = true;
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return has_messages;
}

const std::vector<platform_window*>& platform_window::get_windows() { return windows; }
