
#include <Windows.h>
#include <Spectre/System/Log.h>
#include <Spectre/Input/InputModule.h>
#include "Win32Input.h"
#include "Win32Mouse.h"

namespace sp {

static Vector2f _normalizePos(int x, int y, RECT area)
{
	Vector2f out;

	out.x = x / ((float) (area.right - area.left));
	out.y = y / ((float) (area.bottom - area.top));

	return out;
}

static RECT GetClientArea(HWND hwnd) {

	RECT rect;
	GetClientRect(hwnd, &rect);
	return rect;
}

void Win32Mouse::init()
{
}

Vector2f Win32Mouse::getPosition() const
{
	return m_position;
}

Vector2f Win32Mouse::getAbsPosition() const
{
	return m_abs_position;
}

bool Win32Mouse::isButtonDown(Mouse::Button button) const
{
	int btn;

	switch(button) {
	case Mouse::Left : 		btn = GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON; 	break;
	case Mouse::Right : 	btn = GetSystemMetrics(SM_SWAPBUTTON) ? VK_LBUTTON : VK_RBUTTON; 	break;
	case Mouse::Middle : 	btn = VK_MBUTTON; 	break;
	case Mouse::XButton1 : 	btn = VK_XBUTTON1; 	break;
	case Mouse::XButton2 : 	btn = VK_XBUTTON2; 	break;
	default: btn = 0;
	}

	return ::GetAsyncKeyState(btn) & 0x8000;
}

void Win32Mouse::update(InputModule *input)
{
	HWND handle;
	POINT p;

	// Update absolute position
	GetCursorPos(&p);
	m_abs_position = Vector2f(p.x, p.y);

	// Update relative position
	handle = ::GetCapture();
	if (handle) {
		// Translate position to focued window.
		ScreenToClient(handle, &p);
		m_position = Vector2f(p.x, p.y);
	}
}

bool Win32Mouse::handleMessage(MSG msg, Event& event)
{
	switch(msg.message) {
	case WM_LBUTTONUP :
	case WM_LBUTTONDOWN :
	case WM_RBUTTONUP :
	case WM_RBUTTONDOWN :
	case WM_MBUTTONUP :
	case WM_MBUTTONDOWN :
	case WM_XBUTTONUP :
	case WM_XBUTTONDOWN :
		event.type = Event::MouseButton;
		break;
	}

	switch(msg.message) {
	case WM_LBUTTONUP :
	case WM_LBUTTONDOWN :
		event.mouseButton.button = Mouse::Button::Left;
		event.mouseButton.pressed = msg.message == WM_LBUTTONDOWN;
		return true;

	case WM_RBUTTONUP :
	case WM_RBUTTONDOWN :
		event.mouseButton.button = Mouse::Button::Right;
		event.mouseButton.pressed = msg.message == WM_RBUTTONDOWN;
		return true;

	case WM_MBUTTONUP :
	case WM_MBUTTONDOWN :
		event.mouseButton.button = Mouse::Button::Right;
		event.mouseButton.pressed = msg.message == WM_MBUTTONDOWN;
		return true;

	case WM_XBUTTONUP :
	case WM_XBUTTONDOWN :
		event.mouseButton.button = GET_XBUTTON_WPARAM(msg.wParam) == XBUTTON1
			? Mouse::Button::XButton1 : Mouse::Button::XButton2;
		event.mouseButton.pressed = msg.message == WM_XBUTTONDOWN;
		return true;

	case WM_MOUSEMOVE :
		RECT area = GetClientArea(msg.hwnd);
		int x = LOWORD(msg.lParam);
		int y = HIWORD(msg.lParam);

		// Do not forward the message if mouse is outside client area.
		if (    (x < area.left) || (x > area.right)
			 || (y < area.top)  || (y > area.bottom) ) {
			return false;
		}

		event.type = Event::MouseMove;
		event.mouseMove.x = x;
		event.mouseMove.y = y;
		return true;
	}

	return false;
}

} // namespace sp
