/* mouse.c */

#include <sys/time.h>
#include <dev/wscons/wsconsio.h>

#include <fcntl.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include "vm.h"

#define MOUSEDEVICE "/dev/wsmouse"

char *events[] = {
	"undefined",
	"WSCONS_EVENT_KEY_UP",
	"WSCONS_EVENT_KEY_DOWN",
	"WSCONS_EVENT_ALL_KEYS_UP",
	"WSCONS_EVENT_MOUSE_UP",
	"WSCONS_EVENT_MOUSE_DOWN",
	"WSCONS_EVENT_MOUSE_DELTA_X",
	"WSCONS_EVENT_MOUSE_DELTA_Y",
	"WSCONS_EVENT_MOUSE_ABSOLUTE_X",
	"WSCONS_EVENT_MOUSE_ABSOLUTE_Y",
	"WSCONS_EVENT_MOUSE_DELTA_Z",
	"WSCONS_EVENT_MOUSE_ABSOLUTE_Z",
	"WSCONS_EVENT_SCREEN_SWITCH",
	"WSCONS_EVENT_ASCII",
	"WSCONS_EVENT_MOUSE_DELTA_W",
	"WSCONS_EVENT_MOUSE_ABSOLUTE_W"
};

#define WSCONS_EVENT_DELTA_X 6
#define WSCONS_EVENT_DELTA_Y 7
#define WSCONS_EVENT_DELTA_Z 10

#define MOUSE_SCALE 4

#define MOUSE_X_MIN (-127 * MOUSE_SCALE)
#define MOUSE_X_MAX (127 * MOUSE_SCALE)
#define MOUSE_Y_MIN (-127 * MOUSE_SCALE)
#define MOUSE_Y_MAX (127 * MOUSE_SCALE)
#define MOUSE_Z_MIN -127
#define MOUSE_Z_MAX 127

int mouse_fd;
int mouse_bytes;
struct wscons_event mouse_buf;
int mouse_initialised = 0;
int mouse_x, mouse_y, mouse_z;

int mouse_init(void)
{
	mouse_fd = open(MOUSEDEVICE, O_RDONLY);
	if (mouse_fd < 0)
		err(1, "can't open mouse device");

	vm_register_signal_fd(mouse_fd, VM_MOUSEQ);
	mouse_initialised = 1;
	mouse_x = 0;
	mouse_y = 0;
	mouse_z = 0;

	return 1;
}

void mouse_close(void)
{
	close(mouse_fd);
	mouse_initialised = 0;
}

int mouse_filter(void)
{
	switch (mouse_buf.type) {
		case WSCONS_EVENT_MOUSE_DELTA_X:
			mouse_x += mouse_buf.value;
			if (mouse_x > MOUSE_X_MAX)
				mouse_x = MOUSE_X_MAX;
			if (mouse_x < MOUSE_X_MIN)
				mouse_x = MOUSE_X_MIN;
			return 1;
		case WSCONS_EVENT_MOUSE_DELTA_Y:
			mouse_y += mouse_buf.value;
			if (mouse_y > MOUSE_Y_MAX)
				mouse_y = MOUSE_Y_MAX;
			if (mouse_y < MOUSE_Y_MIN)
				mouse_y = MOUSE_Y_MIN;
			return 1;
		case WSCONS_EVENT_MOUSE_DELTA_Z:
			mouse_z += mouse_buf.value;
			if (mouse_z > MOUSE_Z_MAX)
				mouse_z = MOUSE_Z_MAX;
			if (mouse_z < MOUSE_Z_MIN)
				mouse_z = MOUSE_Z_MIN;
			return 1;
	}
	return 0;
}

void mouse_getpos(int *x, int *y, int *z)
{
	*x = mouse_x / MOUSE_SCALE;
	*y = mouse_y / MOUSE_SCALE;
	*z = mouse_z;
}

int mouse_read(void)
{
	if (!mouse_initialised)
		return 0;

	while (1) {
		int rv;
		int left = sizeof(mouse_buf) - mouse_bytes;
		rv = read(mouse_fd, ((char *)&mouse_buf) + mouse_bytes, left);
		if (rv == -1) {
			if (errno == EAGAIN)
				return 0;
			printf("Error reading from mouse\n");
			close(mouse_fd);
			mouse_initialised = 0;
		}
		if (rv == 0)
			return 0;
		mouse_bytes += rv;
		if (mouse_bytes == sizeof(mouse_buf)) {
			mouse_bytes = 0;
			if (mouse_filter())
				return 1;
		}
	}
}
