/*******************************************************
 program for reading info form redlab measurments controller (usb hid device), joiystick and feed the through Csound c++ APi to soundgame chebyshev
 redöab 1024LS -> Csound API -> interakr-proov.csd
********************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <asm/types.h> // u8 etc
#include <curses.h>

#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <math.h>

#include <linux/input.h>
#include <linux/joystick.h>

#define X_AXIS 0
#define Y_AXIS 1

#include "hidapi.h"
// CSound headers
#include "csound/csound.hpp"
#include "csound/csPerfThread.hpp"

// constants for the USB controlled from 1024.h
#define DIO_PORTA (0x01)
#define DIO_PORTB (0x04)
#define DIO_PORTC_LOW (0x08)
#define DIO_PORTC_HI  (0x02)

/* Commands and Codes for USB 1024-LS HID reports */

#define DIN         (0x00)     // Read digital port
#define DOUT        (0x01)     // Write digital port



// code of readport: taken and slightly modifed from: ftp://lx10.tx.ncsu.edu/pub/Linux/drivers/USB/ developed by Warren Jasper <wjasper@tx.ncsu.edu>

// hidapi functions deriving from http://www.signal11.us/oss/hidapi/

int readport(hid_device* hid, __u8 port, __u8* din_value)  // returns number of bytes read or error code
{
  __u8 cmd[9];
  int retval;
    
  cmd[0] = 0 ; // report number always 0
  cmd[1] = DIN;
  cmd[2] = port;
  retval=hid_write(hid, cmd, 9); // report number + 8 bytes report
  if (retval < 0)
	return retval; 
  
  hid_set_nonblocking(hid, 0); // 1- nonblocking, 0 - wait until there is data to read NB! Can be dangerous: can hang! TODO: rewrite as nonblocking

  // read 1 byte
  retval = hid_read(hid, din_value, 1);
  if ( (retval == 0) || (retval < 0) )
    return retval; // retunr the error code or 0
  
  if (port == DIO_PORTC_HI)  *din_value >>= 4;  // TODO: test! both give the same result
  if (port == DIO_PORTC_LOW) *din_value &= 0xf;
  return retval; // number of bytes read
}



int main(int argc, char* argv[])
{
	int res, cs_res, ch=0, send_js_data=0,i=0;
	char readswitches=0, readjoystick=0;
	//int userInput=200;
	
	__u8 portA, portB,oldA=0, oldB=0; 
	__u8 jsbuttons=0; // map of joystick buttons pressed
	#define MAX_STR 255
	hid_device *handle;
	Csound cs;
	
	// joystick variables
	int fd; 
	char *fn="/dev/input/js0";
	struct js_position {
	  int x,y;
	  char sect;
	} jspos = {1,2,3};
	struct js_event js;
	
	
	printf("Usage: redlab [-s] [-j]\n-s -- read from switches\n-j -- read from joystick\n\n");
	// check the command line argumen
	for (i=1;i<argc;i++) {
	   if ( !strcmp("-s",argv[i]) )  
	      readswitches=1;
	   else if ( !strcmp("-j",argv[i]) )
	     readjoystick=1;
	}
	printf("Joystick: %d, Switches: %d\n",readjoystick,readswitches );
	
	
	// OPEN usb-1024LS -----------------------------------      
	// Open the device using the VID, PID,
	// and optionally the Serial number.
	if ( readswitches) { 
	  handle = hid_open(0x9db, 0x76, NULL);
	   if (!handle) {
	    printf(" Could not open hid device\n");
	    return 1;
	  } 
	  else 
	      printf("Device successfully opened\n");
	}
	
	// OPEN JOYSTICK
	if (readjoystick) {
	  if ((fd = open(fn, O_RDONLY)) < 0) {
	    perror("JOYSTICK: ");
	    return 1;
	  }
	  fcntl(fd, F_SETFL, O_NONBLOCK); // set joystick to nonblocking mode  
	}

	// Csound API section ----------------------------------------
	
	csoundInitialize(NULL,NULL,0);
	cs_res = cs.Compile("chebyshev.csd");
	if(cs_res) {
	  return -1;
	  printf("Csound error: %d\n",cs_res);
	}
	
	CsoundPerformanceThread perfThread(cs.GetCsound());
	
	perfThread.Play(); 	
	printf("Peatamiseks vajutage s klahvile\n");
	initscr(); // open curses mode for getch()
	nodelay(stdscr, TRUE); // don't wait for keypress, go on
	while(perfThread.GetStatus() == 0 && (ch=getch()!='s') )
	{  
	  
	  //JOYSTICK:
	  if (readjoystick) {
	     while (read(fd, &js, sizeof(struct js_event)) == sizeof(struct js_event))  {
		if (js.type==JS_EVENT_AXIS) {
		  if (js.number==0) jspos.x=js.value;
		  if (js.number==1) jspos.y=js.value;
		  
		  if ((jspos.x<0) && (jspos.y<=0) ) jspos.sect=1;
		  else if ((jspos.x>=0) && (jspos.y<0) ) jspos.sect=2; 
		  else if ((jspos.x<0) && (jspos.y>=0)) jspos.sect=3;
		  else if ((jspos.x>0) && (jspos.y>=0)) jspos.sect=4;
		  else jspos.sect=0;
		  
		  cs.SetChannel("js_x",(MYFLT)abs(jspos.x)/32767 ); // scale the axis from 0..32767 to 0..9 - the octaves from the lowest frequency, here 20 Hz. Pass the frequency to CSound
		  cs.SetChannel("js_y",(MYFLT)abs(jspos.x)/32767 );
		  cs.SetChannel("js_sect",(MYFLT)jspos.sect);
		
		  
		} else if (js.type==JS_EVENT_BUTTON ) {
		   switch (js.number) {
		     case 0:
		       cs.SetChannel("js_bt0",(MYFLT)js.value);
		       break;
		     case 2: // upper button
		       cs.SetChannel("js_bt1",(MYFLT)js.value);
		       break;
		   }
		   
	  }

	  if (errno != EAGAIN) {
		  perror("\njstest: error reading");
		  return 1;
	  }
	  
	  usleep(10000);
	  	    
	  }
	  
	  // read data from ports
	  if (readswitches) {
	    res=readport(handle, DIO_PORTA, &portA);
	    if (res>0) {
		if (portA!=oldA) //if the state has changed, send it to csound
		  cs.SetChannel("porta",(MYFLT)portA);
		 oldA=portA; 	  
	    }
	    else 
		printf(" Error: %d\n",res);
	    
	    res=readport(handle, DIO_PORTB, &portB);
	    if (res>0) {
		if (portB!=oldB) 
		  cs.SetChannel("portb",(MYFLT)portB);
		 oldB=portB;
		
	    }
	    else
		printf(" Error: %d\n",res);
	  }
	  // JOYSTICK 
	  
	 }
	
	endwin(); // end curses mode
	perfThread.Stop();
	perfThread.Join();
	
   

	return 0;
}
