// $ sudo apt-get install libusb-1.0-0-dev
// gcc -Wall -g -O2 -I/usr/local/include/ -o interface_joy2key interface_joy2key-0.0.1.c -lusb-1.0
// http://www.dreamincode.net/forums/topic/148707-introduction-to-using-libusb-10/

#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>

#define EP_INTR			(1 | LIBUSB_ENDPOINT_IN)
#define INTR_LENGTH		27

char SOFTWARE[] = "interface_joy2key";
char DATE[] = "Febrer";
char YEAR[] = "2014";
char AUTHOR[] = "Joan Quintana Compte (joanillo)";
char LICENSE[] = "GPL v.3";
char WEB[] = "joanqc@gmail.com - www.joanillo.org";
char VERSION[] = "v1.00";

static int do_exit = 0;
static unsigned char irqbuf[INTR_LENGTH];
static struct libusb_device_handle *devh = NULL;
static struct libusb_transfer *irq_transfer = NULL;

static int state = 0;
//static int drum_state[5];

static int find_joystick_device()
{
	devh = libusb_open_device_with_vid_pid(NULL, 0x1781, 0x0a96); //ratolí Logitech (id_vendor, id_product)
	return devh ? 0 : -EIO;
}

static void cb_irq(struct libusb_transfer *transfer)
{

	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
		fprintf(stderr, "irq transfer status %d? %d\n", transfer->status, LIBUSB_TRANSFER_ERROR);
		do_exit = 2;
		libusb_free_transfer(transfer);
		irq_transfer = NULL;
		return;
	}

	unsigned char *buf = irqbuf;

	if (buf[0]!=0) printf("aaa %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19], buf[20], buf[21], buf[22], buf[23], buf[24], buf[25], buf[26]);

	printf("%d\n", buf[0]);
}

static int init_capture(void)
{
	int r;

	r = libusb_submit_transfer(irq_transfer);
	if (r < 0)
		return r;

	/* start state machine */
	state = 6; //STATE_AWAIT_IRQ_FINGER_REMOVED;
	return state; //next_state();
}

static int do_init(void)
{
	//unsigned char status;
	return 0;
}

static int alloc_transfers(void)
{
	/*img_transfer = libusb_alloc_transfer(0);
	if (!img_transfer)
		return -ENOMEM;*/
	
	irq_transfer = libusb_alloc_transfer(0);
	if (!irq_transfer)
		return -ENOMEM;

	/*libusb_fill_bulk_transfer(img_transfer, devh, EP_DATA, imgbuf,
		sizeof(imgbuf), cb_img, NULL, 0);*/
	libusb_fill_interrupt_transfer(irq_transfer, devh, EP_INTR, irqbuf,
		sizeof(irqbuf), cb_irq, NULL, 0);
	
	return 0;
}

static void sighandler(int signum)
{
	do_exit = 1;
}

int main(int argc, char **argv)
{
	printf("\n%s %s\n",SOFTWARE,VERSION);
	printf("\nCreated by %s.\n",AUTHOR);
	printf("Licensed under %s.\n\n",LICENSE);
	
	struct sigaction sigact;
	int r = 1;

	r = libusb_init(NULL);
	if (r < 0) {
		fprintf(stderr, "failed to initialise libusb\n");
		exit(1);
	}

	r = find_joystick_device();
	if (r < 0) {
		fprintf(stderr, "Could not find/open device\n");
		libusb_close(devh);
		libusb_exit(NULL);
		return -r;
	}

	if (libusb_kernel_driver_active(devh, 0)) {
		r = libusb_detach_kernel_driver(devh, 0);
		if (r < 0) {
			printf("did not detach.\n");
		}
	}

	r = libusb_claim_interface(devh, 0);
	if (r < 0) {
		fprintf(stderr, "usb_claim_interface error %d %d\n", r, LIBUSB_ERROR_BUSY);
		libusb_close(devh);
		libusb_exit(NULL);
		return -r;
	}
	printf("claimed interface\n");

	r = do_init();
	if (r < 0) {
		// Deinit & Release
		libusb_free_transfer(irq_transfer);
		libusb_release_interface(devh, 0);
		libusb_close(devh);
		libusb_exit(NULL);
		printf("do_init failed.\n");
		return -r;
	}

	r = alloc_transfers();
	if (r < 0) {
		// Deinit & Release
		libusb_free_transfer(irq_transfer);
		libusb_release_interface(devh, 0);
		libusb_close(devh);
		libusb_exit(NULL);
		printf("alloc_transfers failed.\n");
		return -r;
	}

	r = init_capture();
	if (r < 0) {
		// Deinit & Release
		libusb_free_transfer(irq_transfer);
		libusb_release_interface(devh, 0);
		libusb_close(devh);
		libusb_exit(NULL);
		printf("init_capture failed.\n");
		return -r;
	}

	sigact.sa_handler = sighandler;
	sigemptyset(&sigact.sa_mask);
	sigact.sa_flags = SA_RESTART;
	r = sigaction(SIGINT, &sigact, NULL);
	r = sigaction(SIGTERM, &sigact, NULL);
	r = sigaction(SIGQUIT, &sigact, NULL);

	// Drum state is all up.
	//memset(drum_state, 0, 6);

	while (!do_exit) {
		r = libusb_handle_events(NULL);
		if (r < 0) {
			break;
		}
	}

	//printf("shutting down...\n");
	
	if (irq_transfer) {
		r = libusb_cancel_transfer(irq_transfer);
		if (r < 0) {
			// Deinit & Release
			libusb_free_transfer(irq_transfer);
			libusb_release_interface(devh, 0);
			libusb_close(devh);
			libusb_exit(NULL);
			printf("libusb_cancel_transfer failed.\n");
			return -r;
		}
	}

	// || img_transfer
	while (irq_transfer)
		if (libusb_handle_events(NULL) < 0)
			break;
	
	if (do_exit == 1)
		r = 0;
	else
		r = 1;

//out_deinit:
	//libusb_free_transfer(img_transfer);
	libusb_free_transfer(irq_transfer);
	/*set_mode(0);
	set_hwstat(0x80);*/
//out_release:
	libusb_release_interface(devh, 0);
//out:
	libusb_close(devh);
	libusb_exit(NULL);
	//return 23;
	//return r >= 0 ? r : -r;

	return -1;
}
