
#include <Spectre/System/Event.h>
#include <Spectre/System/Log.h>
#include "Xlib.h"
#include "X11Display.h"
#include "X11Mouse.h"

#define XBUTTON1BIT (1<<0)
#define XBUTTON2BIT (1<<1)

namespace sp {

namespace _priv {

	// Variable to handle extra button state (not queryable in xlib)
	unsigned int xstate = 0;
}

X11Mouse::X11Mouse() :
m_btn_state(0)
{
}

void X11Mouse::init()
{
	updateFocusedWindow();
}

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

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

bool X11Mouse::isButtonDown(Mouse::Button button) const
{
	// Only signal that a button is down
	// if we have focus on a window.
	if (!m_win) {
		return false;
	}

	switch(button) {
	case Mouse::Button::Left :
		return m_btn_state & Button1Mask;
	case Mouse::Button::Right :
		return m_btn_state & Button3Mask;
    case Mouse::Button::Middle :
		return m_btn_state & Button2Mask;
	case Mouse::Button::XButton1 :
		return _priv::xstate & XBUTTON1BIT;
	case Mouse::Button::XButton2 :
		return _priv::xstate & XBUTTON2BIT;
	default :
		return false;
	}
	return false;
}

void X11Mouse::update(InputModule *input)
{
	::Display* disp = Xlib::getDisplay();
	::Window root, child;
	int rx, ry, x = 0, y = 0;

	updateFocusedWindow();

	// Query position and button state.
	XQueryPointer(disp,
		m_win ? m_win : ::XDefaultRootWindow(disp),
		&root, &child,
		&rx, &ry,
		&x, &y, &m_btn_state);

	// Update abs position (relative to root).
	m_abs_position.x = rx;
	m_abs_position.y = ry;

	// Update window position.
	if (m_win) {
		m_position.x = x;
		m_position.y = y;
	}
}

void X11Mouse::updateFocusedWindow()
{
	X11Display *focus = X11Display::getFocused();
	m_win = focus ? (::Window) focus->getHandle() : 0;
}

#define SETXSTATE(bit) \
	_priv::xstate = (xevent->type == ButtonPress) \
		? _priv::xstate | (bit) \
		: _priv::xstate & ~(bit)

bool X11Mouse::handleMessage(XEvent* xevent, Event& event)
{
	if (xevent->type == MotionNotify) {
		event.type = Event::MouseMove;
		event.mouseMove.x = xevent->xmotion.x;
		event.mouseMove.y = xevent->xmotion.y;
		return true;
	}

	if (xevent->type == ButtonPress || xevent->type == ButtonRelease) {

		Mouse::Button trans = Mouse::Button::Unknown;

		switch(xevent->xbutton.button) {
		case Button1 : trans = Mouse::Button::Left; break;
		case Button2 : trans = Mouse::Button::Middle; break;
		case Button3 : trans = Mouse::Button::Right; break;
		// Xlib do not support querying of button 8 and 9.
		// so we have to fake it.
		case 8 : trans = Mouse::Button::XButton1; SETXSTATE(XBUTTON1BIT); break;
		case 9 : trans = Mouse::Button::XButton2; SETXSTATE(XBUTTON2BIT); break;
		}

		if (trans != Mouse::Button::Unknown) {
			event.type = Event::MouseButton;
			event.mouseButton.pressed = xevent->type == ButtonPress;
			event.mouseButton.button = trans;
			return true;
		}
	}

	return false;
}

} // namespace sp
