#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <asm/types.h>
#include <linux/hiddev.h>
#include <signal.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/XF86keysym.h>
#include <X11/extensions/XTest.h>

#define MAX_HIDDEV 16
#define VENDOR_ID 0x05f0
#define PRODUCR_ID 0x0304
#define VERSION_ID 0x0100

typedef struct
{
   unsigned short b;
   KeySym keysym;
} Code_Map;

/* q     INS   PSE
 * 1     2     3
 * 4     5     6
 * 7     8     9
 * END   0     BSP
 *
 *     t   g
 * HME         m
 *       UP
 * LFT   RET   RGT
 *       DWN
 * r           spc
 *     s   p
 * 
 * BLF BRT PER l
 * PRI NEX COM k
 * TAB q   w   n
 */

Code_Map code_map[] =
{
     {0x00ff, XK_q}, //POWER
     {0x01fe, XK_Insert}, //PROGRAM
     {0x45ba, XK_Pause}, //STANDBY
     {0x04fb, XK_1}, //1
     {0x05fa, XK_2}, //2
     {0x5fa0, XK_3}, //3
     {0x07f8, XK_3}, //4
     {0x08f7, XK_5}, //5
     {0x09f6, XK_6}, //6
     {0x0af5, XK_7}, //7
     {0x0bf4, XK_8}, //8
     {0x0cf3, XK_9}, //9
     {0x0df2, XK_End}, //INPUT
     {0x03fc, XK_0}, //0
     {0x0ff0, XK_BackSpace}, //CATV/CLEAR
     {0x10ef, XK_Home}, //MENU
     {0x11ee, XK_t}, //TOOL
     {0x12ed, XK_g}, //TV GUIDE
     {0x13ec, XK_m}, //MUTE
     {0x44bb, XK_Up}, //UP
     {0x1ce3, XK_Left}, //LEFT
     {0x51ae, XK_Return}, //OK
     {0x48b7, XK_Right}, //RIGHT
     {0x1de2, XK_Down}, //DOWN
     {0x5ca3, XK_r}, //RECORD
     {0x14eb, XK_s}, //STOP 
     {0x15ea, XK_p}, //PLAY
     {0x19e6, XK_space}, //PAUSE
     {0x40bf, XK_bracketleft}, //RW
     {0x41be, XK_bracketright}, //FF
     {0x42bd, XK_period}, //CHUP
     {0x43bc, XK_l}, //VOLUP
     {0x52ad, XK_Prior}, //PREV
     {0x53ac, XK_Next}, //NEXT
     {0x54ab, XK_comma}, //CHDOWN
     {0x55aa, XK_k}, //VOLDOWN
     {0x58a7, XK_Tab}, //SCREENSHOT
     {0x59a6, XK_q}, //REPEAT
     {0x5aa5, XK_w}, //ROTATE
     {0x5ba4, XK_n} //SUBMENU
};

void
alarm_sig(int n)
{
}

int
main(int argc, char **argv)
{
   Display *d;
   int fd;
   unsigned char buf[128];
   int i;
   
   d = XOpenDisplay(NULL);
   if (!d)
     {
	printf("ERROR: Cannot connet to X Display\n");
	exit(-1);
     }
   signal(SIGALRM, alarm_sig);
   redo:
   for (i = 0; i < MAX_HIDDEV; i++)
     {
	snprintf(buf, sizeof(buf), "/dev/usb/hiddev%i", i);
	fd = open(buf, O_RDONLY);
	if (fd >= 0)
	  {
	     struct hiddev_devinfo device_info;
	     
	     ioctl(fd, HIDIOCGDEVINFO, &device_info);
	     if ((device_info.vendor == VENDOR_ID) &&
		 (device_info.product == PRODUCR_ID) &&
		 (device_info.version == VERSION_ID))
	       {
		  close(fd);
		  break;
	       }
	     close(fd);
	  }
     }
   if (i == MAX_HIDDEV)
     {
	printf("ERROR: Scanned all hiddev devices and could not find Canopus CRM-FV8 remote control.\n");
	sleep(1);
	goto redo;
     }
   snprintf(buf, sizeof(buf), "/dev/usb/hiddev%i", i);
   fd = open(buf, O_RDONLY);
   if (fd < 0)
     {
	printf("ERROR: Cannot open %s\n", buf);
	sleep(1);
	goto redo;
     }
   for (;;)
     {
	fd_set rfds;
	int ret;
	
	FD_ZERO(&rfds);
	FD_SET(fd, &rfds);
	alarm(5);
	ret = select(fd + 1, &rfds, NULL, NULL, NULL);
	alarm(0);
	if (ret == 1)
	  {
	     if (read(fd, buf, 32) == 32)
	       {
		  int i;
		  Code_Map *map;
		  unsigned short b;
		  
		  b = buf[20] << 8 | buf[28];
		  map = NULL;
                  printf("CODE: %x\n", b);
		  for (i = 0; i < (sizeof(code_map) / sizeof(Code_Map)); i++)
		    {
		       if (code_map[i].b == b)
			 {
			    map = &(code_map[i]);
			    break;
			 }
		    }
		  if (map)
		    {
		       KeyCode code;
		       
		       code = XKeysymToKeycode(d, map->keysym);
                       printf("SEND %i\n", code);
		       XTestFakeKeyEvent(d, code, 1, 0);
                       XSync(d, False);
                       usleep(100000);
		       XTestFakeKeyEvent(d, code, 0, 0);
                       XSync(d, False);
		    }
	       }
	  }
	close(fd);
	goto redo;
     }
   exit(0);
}
