/*
 * based on xcshuttle by David Arendt
*/
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <linux/input.h>
#include "shuttle.h"


int shuttle_open(shuttle_data_t* shuttle){
	if (shuttle != NULL){
		int i = 0;
  		char name[256] = "unknown";
		char *device;
		char shuttlename[256] = "Contour Design ShuttleXpress";
	
		device = malloc(18 * sizeof(char));
		if (shuttle->fd) close(shuttle->fd);
		shuttle->fd = 0;
		
		for (i = 0;(i < 10) && !shuttle->fd;i++){
			snprintf(device,18,"/dev/input/event%d",i);
			shuttle->fd = open(device, O_RDONLY);
			if (shuttle->fd > 0){
				if (ioctl(shuttle->fd, EVIOCGNAME(sizeof(name)), name) < 0){
					shuttle->fd = close (shuttle->fd);
				}else{
					if (strncmp(name,shuttlename,22) != 0){
						shuttle->fd = close (shuttle->fd);
					}else{
/*						printf ("Found %s at %s\n",name,device);*/
					}
				}
			}else{shuttle->fd = 0;}
		}
		return shuttle->fd;
	}
	return 0;
}

shuttle_data_t* shuttle_new(){
        shuttle_data_t* shuttle;

        shuttle = (shuttle_data_t*) malloc (sizeof(shuttle_data_t));

	shuttle->fd = 0;
	shuttle->value = 0;
	shuttle->lastvalue = 0;
	shuttle->code = 0;
	shuttle->lastcode = 0;
	shuttle->type = 0;
	shuttle->buttonstates = 0;
	shuttle->jogstate = 0;
	shuttle->shuttlestate = 0;
	shuttle->shuttledelta = 0.0;
	shuttle->timediff = 0;
	shuttle->lastevent.tv_sec = 0;
	shuttle->lastevent.tv_usec = 0;

        return shuttle;
}

int shuttle_close (shuttle_data_t* shuttle){
	if (shuttle != NULL){
		if (shuttle->fd > 0){
	 		shuttle->fd = close(shuttle->fd);
			printf ("Shuttle device closed\n");
		}
		return shuttle->fd;
	}else return -1;
}

int shuttle_poll (shuttle_data_t* shuttle){
	struct timeval tv;
	struct input_event ev;
	char name[256] = "unknown";
	int retval;
	int type;
	int code;
	int value;
	int ok;
	fd_set rfds;

	if (shuttle != NULL){
		if (shuttle->fd > 0){
			tv.tv_sec = 0;
			tv.tv_usec = 0;
			
			if (ioctl(shuttle->fd, EVIOCGNAME(sizeof(name)), name) < 0)  return shuttle_close (shuttle);

			FD_ZERO(&rfds);
           		FD_SET(shuttle->fd, &rfds);

			retval = select(shuttle->fd + 1, &rfds, NULL, NULL, &tv);

/*			printf ("retval:%d, %d\n",retval,shuttle->fd);*/

			if (retval < 0){ 
				return shuttle_close (shuttle);
			}else if (retval > 0){	
                		ok = read(shuttle->fd, &ev, sizeof(ev));

				if (ok != sizeof(ev)) return shuttle_close (shuttle);

				type  = ev.type;
                		if ((type != SHUTTLE_TYPE_KEY) && (type != SHUTTLE_TYPE_JOGSHUTTLE)){ return 0;}

				switch (ev.code){
					case 8   : code = SHUTTLE_JOG;       break;
					case 7   : code = SHUTTLE_SHUTTLE;   break;
					case 260 : code = SHUTTLE_LEFTMOST;  break;
					case 261 : code = SHUTTLE_LEFT;      break;
					case 262 : code = SHUTTLE_MIDDLE;    break;
					case 263 : code = SHUTTLE_RIGHT;     break;
					case 264 : code = SHUTTLE_RIGHTMOST; break;
					default  : code = 0; printf ("unknown code\n"); break;
				}
				value = ev.value;

				if (code == SHUTTLE_JOG){
					/* joq of shuttle xpress doesn't have 0 */
					if (value < 0) value++;
					else value--;
				}

				if (type == SHUTTLE_TYPE_KEY){
					if (value)  shuttle->buttonstates |= code;
                                	else  shuttle->buttonstates ^= code;
/*					printf ("buttonstates:%d\n",shuttle->buttonstates);*/
				}

				if ((value != shuttle->shuttlestate) || (code != SHUTTLE_SHUTTLE)){
					shuttle->lastvalue = shuttle->value;
					shuttle->lastcode = shuttle->code;
					shuttle->value = value;
					shuttle->code = code;
					shuttle->type = type;
/*					gettimeofday(&tv, NULL);*/
					tv = ev.time;

					if (shuttle->lastevent.tv_sec || shuttle->lastevent.tv_usec){
						shuttle->timediff = (tv.tv_sec - shuttle->lastevent.tv_sec) * 1000;
						shuttle->timediff = tv.tv_usec - shuttle->lastevent.tv_usec;
					}

					if (code == SHUTTLE_JOG) {shuttle->jogstate = value;}
                                        if (code == SHUTTLE_SHUTTLE){
						if ((value - shuttle->shuttlestate) < -200){
							shuttle->shuttlestate -= 253;
						} 
						if ((value - shuttle->shuttlestate) > 200){
                                                        shuttle->shuttlestate += 253;
                                                }

						if (shuttle->timediff){
							shuttle->shuttledelta = (float)(value - shuttle->shuttlestate) 
									* 200000. / (float)shuttle->timediff;	
						}
						shuttle->shuttlestate = value;
					}

					shuttle->lastevent.tv_sec = tv.tv_sec;
					shuttle->lastevent.tv_usec = tv.tv_usec;
		
					return code;
				}
			}
		}
		return -1;
	}
	return 0;
}
