/*
 * bb-surfer.c
 * Requires libxtst and xwiimote
 * Compile with: cc bb-surfer.c -o bb-surfer -Ofast -s -lX11 -lXtst -lxwiimote -lm
 */

#include <stdlib.h>
#include <unistd.h>
#include <xwiimote.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/extensions/XTest.h>
#include <poll.h>
#include <inttypes.h>
#include <math.h>
#include <string.h>

void drawbar(unsigned length, unsigned star);
void rotate(struct xwii_event_abs abs[]);

enum {
	TOP_RIGHT,
	BOTTOM_RIGHT,
	TOP_LEFT,
	BOTTOM_LEFT
};

int main(int argc, char *argv[])
{
	double lastbalance = 0;
	int dummy, sensitivity, onboard = -1, surfmode, lean;
	Display *display;
	struct xwii_iface *board;
	struct xwii_event event;
	struct pollfd pollfds[1];
	
	sensitivity = surfmode = lean = 0;
	
	while (1)
		switch (getopt(argc, argv, "s:rjl")) {
		case 'j':
			onboard = 0;
			break;
		case 'l':
			lean = 1;
			break;
		case 'r':
			surfmode = 1;
			break;
		case 's':
			sensitivity = atoi(optarg);
			break;
		case '?':
			goto usage;
		case -1:
			goto parsed;
		}
	
parsed:
	if (optind >= argc) {
	usage:
		fprintf(stderr,
			"Usage: %s [options] <joystick path (find using `xwiishow list')>\n"
			"Where options are:\n"
			"\t-s <sensitivity> - default 700\n"
			"\t-r - rotate balance board, POWER button faces left\n"
			"\t-l - lean forward to left click\n"
			"\t-j - make a jumping motion to right click (don't actually jump!)\n",
			argv[0]);
		return 1;
	}
	
	if (!sensitivity)
		sensitivity = 700;
	
	if ((display = XOpenDisplay(NULL)) == NULL) {
		fputs("Failed to open display, is $DISPLAY set?\n", stderr);
		return 2;
	}
	
	if (!XTestQueryExtension(display, &dummy, &dummy, &dummy, &dummy)) {
		fputs("Your display does not support XTEST\n", stderr);
		return 2;
	}
	
	if (xwii_iface_new(&board, argv[optind]) != 0) {
		fprintf(stderr, "Couldn't open wiimote `%s'\n", argv[optind]);
		return 2;
	}
	
	if (xwii_iface_open(board, XWII_IFACE_BALANCE_BOARD) != 0) {
		fprintf(stderr, "Wiimote `%s' is not a balance board\n", argv[optind]);
		return 2;
	}
	
	pollfds[0].fd = xwii_iface_get_fd(board);
	pollfds[0].events = POLLIN;
	setbuf(stdout, NULL);
	fputs("00000 ", stdout);
	drawbar(31, 31/2);
	
	while (poll(pollfds, 1, -1))
		while (xwii_iface_dispatch(board, &event, sizeof(event)) == 0)
			if (event.type == XWII_EVENT_BALANCE_BOARD) {
				if (surfmode)
					rotate(event.v.abs);
				double sumright = event.v.abs[TOP_RIGHT].x + event.v.abs[BOTTOM_RIGHT].x,
				       sum = event.v.abs[TOP_LEFT].x + event.v.abs[BOTTOM_LEFT].x + sumright,
				       balance = sumright / sum;
				
				if (!isnan(balance) && sum > 100) {
					printf("\r%05.0f ", sum);
					drawbar(31, balance*30);
					if (onboard != -1 && sum > 3000)
						onboard = 1;
					else if (onboard) {
						XTestFakeButtonEvent(display, 3, 1, 0);
						XTestFakeButtonEvent(display, 3, 0, 50);
						onboard = 0;
					}
					if (lean)
						XTestFakeButtonEvent(display, 1, (event.v.abs[TOP_RIGHT].x + event.v.abs[TOP_LEFT].x) / sum > 0.7, 0);
					XTestFakeRelativeMotionEvent(display, (balance - lastbalance) * sensitivity, 0, 0);
					XFlush(display);
					lastbalance = balance;
				}
			}
	
	XCloseDisplay(display);
	return 0;
}

void drawbar(unsigned length, unsigned star)
{
	unsigned i;
	
	putchar('[');
	for (i = 0; i < length; ++i)
		putchar(i == star ? '*' : '-');
	putchar(']');
}

void rotate(struct xwii_event_abs abs[])
{
	uint32_t bottomr = abs[BOTTOM_RIGHT].x;
	abs[BOTTOM_RIGHT].x = abs[TOP_RIGHT].x;
	abs[TOP_RIGHT].x = abs[TOP_LEFT].x;
	abs[TOP_LEFT].x = abs[BOTTOM_LEFT].x;
	abs[BOTTOM_LEFT].x = bottomr;
}
