/*
 * transport.c 
 * tranzport interface for klopfer & kluppe 
 * based on transport.c by arthur@artcmusic.com
 */

#ifdef HAVE_USB

#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "tranzport.h"

#define RINGBUFFER_MAX 100

int tranzport_poll(tranzport_t *z)
{
	uint8_t buf[8];
	int val;
	uint32_t newbuttons = 0;

	if (!z) return 0;

	memset(buf, 0, 8);
	val = usb_interrupt_read(z->udev, TRANZPORT_READ_ENDPOINT, (char*)(&buf), 8, z->polltimeout);
	if (val < 0) return val;
	if (val != 8) return -1;

/*       	printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);*/
	
	z->status = buf[1];
	
	newbuttons |= buf[2] << 24;
	newbuttons |= buf[3] << 16;
	newbuttons |= buf[4] << 8;
	newbuttons |= buf[5];
	
	z->buttondiff = z->buttons ^ newbuttons; // XOR
	z->buttons = newbuttons;
	
	z->datawheel = buf[6];

	return 0;
}

static int tranzport_write_core(tranzport_t *z, uint8_t *cmd){
        int val;

        val = usb_interrupt_write(z->udev, TRANZPORT_WRITE_ENDPOINT, (char*)cmd, 8, z->timeout);
        if (val < 0) return val;
        if (val != 8) return -1;
        return 0;
}

static int tranzport_write_queued (tranzport_t *z){ /* try to send lcd data */
	uint8_t cmd[8];
	int count = -1;
	int realcount = 0;

	while (count < 7){
		count++;
		if (z->led_dirty[z->led_ptr]){
		        cmd[0] = 0x00;
        		cmd[1] = 0x00;
        		cmd[2] = z->led_ptr;
        		if (z->leds[z->led_ptr]) cmd[3] = 0x01;
        		else cmd[3] = 0x00;
        		cmd[4] = 0x00;
        		cmd[5] = 0x00;
        		cmd[6] = 0x00;
        		cmd[7] = 0x00;
			if (tranzport_write_core(z, cmd)){
/*				printf ("short write in led (%d,%d)\n",count,realcount);*/
				return realcount;
			}
			z->led_dirty[z->led_ptr] = 0;
                	realcount++;
		}
		z->led_ptr = ++z->led_ptr % 7;
	}

	count = -1;
	while (count < 10){
		count++;
		if (z->lcd_dirty[z->lcd_ptr]){
	        	cmd[0] = 0x00;
        		cmd[1] = 0x01;
        		cmd[2] = z->lcd_ptr;
			cmd[3] = z->lcd[(z->lcd_ptr * 4)];
			cmd[4] = z->lcd[(z->lcd_ptr * 4) + 1];
			cmd[5] = z->lcd[(z->lcd_ptr * 4) + 2];
			cmd[6] = z->lcd[(z->lcd_ptr * 4) + 3];
        		cmd[7] = 0x00;
			if (tranzport_write_core(z, cmd)){
/*				printf ("short write in lcd (%d,%d)\n",count,realcount);*/
				return realcount;
			}
			z->lcd_dirty[z->lcd_ptr] = 0;
			realcount++;
		}
		z->lcd_ptr = ++z->lcd_ptr % 10;
	}
	return realcount;
}

int tranzport_do_io (tranzport_t *z){
	int poll = tranzport_poll(z);
	tranzport_write_queued (z);
	return poll;
}

static tranzport_t *open_tranzport_core(struct usb_device *dev){
	tranzport_t *z;
	int val;

	z = malloc(sizeof(tranzport_t));
	if (!z){
		printf("not enough memory");
		return NULL;
	}
	memset(z, 0, sizeof(tranzport_t));

	z->dev = dev;
	z->udev = usb_open(z->dev);
	if (!z->udev){
		printf("unable to open tranzport");
		return NULL;
	}

	usb_reset (z->udev);

	val = usb_claim_interface(z->udev, 0);
	if (val < 0){
		printf("unable to claim tranzport");
		return NULL;
	}


	z->lcd = calloc(41,sizeof(char));
/*	memset(z->lcd,0,sizeof(char) * 41);*/
	memset(z->lcd_dirty,0,sizeof(int) * 10);
	printf ("reset\n");
	memset(z->leds,0,sizeof(int) * 7);
	memset(z->led_dirty,0,sizeof(int) * 7);

	z->lcd_ptr = 0;
	z->led_ptr = 0;
	z->status = 0;
	z->buttons = 0;
	z->buttondiff = 0;
	z->datawheel = 0;
	z->timeout = 15;
	z->polltimeout = 15;
	
	return z;
}

int tranzport_set_nativemode(tranzport_t *z){
	uint8_t nativemodemessage[8];

	nativemodemessage[0] = 0xf0;
	nativemodemessage[1] = 0x00;
	nativemodemessage[2] = 0x01;
	nativemodemessage[3] = 0x40;
	nativemodemessage[4] = 0x10;
	nativemodemessage[5] = 0x01;
	nativemodemessage[6] = 0x00;
	nativemodemessage[7] = 0xf7;
	return tranzport_write_core(z, nativemodemessage);
}

tranzport_t *tranzport_new()
{
	struct usb_bus *bus;
	struct usb_device *dev;

	usb_init();
	usb_find_busses();
	usb_find_devices();

	for(bus=usb_busses; bus; bus=bus->next) {
		for(dev=bus->devices; dev; dev=dev->next) {
			if (dev->descriptor.idVendor != TRANZPORT_VENDORID)
				continue;
			if (dev->descriptor.idProduct != TRANZPORT_PRODUCTID)
				continue;

//			printf ("found\n");
			return open_tranzport_core(dev);
		}
	}

	printf("can't find tranzport");
	return NULL;
}

void tranzport_set_timeout(tranzport_t *z, int timeout){
	if ((timeout < 100) && (timeout > 0))
		z->timeout = timeout;
}

void tranzport_set_polltimeout(tranzport_t *z, int timeout){
	if ((timeout < 100) && (timeout > 0))
		z->polltimeout = timeout;
}


uint8_t tranzport_get_status(tranzport_t *z){
	return z->status;
}

void close_tranzport(tranzport_t *z)
{
	int val;

	val = usb_release_interface(z->udev, 0);
	if (val < 0)
		printf("unable to release tranzport");

	val = usb_close(z->udev);
	if (val < 0)
		printf("unable to close tranzport");

	free(z);
}

/* ***************** LEDs *************************************/

int tranzport_set_led(tranzport_t *z, uint8_t led, int status){
	
	if (led > 7) return -1;
	z->leds[led] = status;
	z->led_dirty[led] = 1;
	return 0;
}

int tranzport_get_led(tranzport_t *z, uint8_t led){
	if (led > 7) return -1;
	if (!z) return -1;
	return z->leds[led];
}

void tranzport_clear_leds(tranzport_t *z){
	tranzport_set_led(z, TRANZPORT_LIGHT_RECORD, 0);
	tranzport_set_led(z, TRANZPORT_LIGHT_TRACKREC, 0);
	tranzport_set_led(z, TRANZPORT_LIGHT_TRACKMUTE, 0);
	tranzport_set_led(z, TRANZPORT_LIGHT_TRACKSOLO, 0);
	tranzport_set_led(z, TRANZPORT_LIGHT_ANYSOLO, 0);
	tranzport_set_led(z, TRANZPORT_LIGHT_LOOP, 0);
	tranzport_set_led(z, TRANZPORT_LIGHT_PUNCH, 0);
}

int tranzport_pressed(tranzport_t *z, int button){
	if ((z->buttons & button) && (z->buttondiff & button)) return 1;
	else return 0;
}

int tranzport_buttonstate (tranzport_t *z, int button){
	if (z->buttons & button) return 1;
	else return 0;
}

int tranzport_get_wheel(tranzport_t *z){
	int w = z->datawheel;

	if (w > 128) w -= 256;
	z->datawheel = 0;
	return w;
}

/************************* LCD FUNCTIONS: ****************************/

void tranzport_lcd_readcell(tranzport_t *z, uint8_t cell, char *text){
	strncpy(text,(char*)(z->lcd + (cell * 4)),4);
}

int tranzport_clear_lcd(tranzport_t *z){

	memset(z->lcd,0x20,20);
	memset(z->lcd_dirty,1,10);
	return 0;
}

int tranzport_write_lcd (tranzport_t *z, const char* txt, int x, int y){
	int i = 0;
	int j;
	int length = strlen(txt);

	if (y > 1) y = 1;
	if (y < 0) y = 0;
	if (x < 0) x = 0;
	if ((length + x) > 20) length = 20 - x;

	for (i = 0; i < length;i++){
		j = x + i + (y * 20);
		if ((j < 40) && (j >= 0)){
/*			printf ("vor copy\n");*/
			z->lcd[j] = txt[i];
			z->lcd_dirty[(int)(j / 4)] = 1;
/*			printf ("j:%d,txt[i]:%c\n",j,(int)txt[i]);*/
		}
	}
	return 0;
}

#endif /* HAVE_USB */
