#include <stdio.h>
#include <signal.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/kd.h>
#include <sys/ioctl.h>
#include "cuecat_lib.h"


/* Scancode and scancode masks */
#define ESC_SCANCODE 0x01
#define CR_SCANCODE  0x1c
#define KEY_RELEASE  0x80

/* Global vars */
int fd;
int previous_kbd_mode;
struct termios old_term_settings;
struct termios new_term_settings;

/* Function prototypes */
void terminate_program(int x);
void restore_console(void);
int getfd(void);
static int open_console(char *fnam);
static int is_a_console(int fd);



int main(void)
{
  unsigned char scancode;
  char cuecat_id[CUECAT_DEVICE_ID_LEN];			/* Cuecat device ID */
  char barcode_type[CUECAT_BARCODE_TYPE_LEN+1];
  char barcode[256];
  int user_abort=0;

  /* Get a console descriptor */
  fd=getfd();

  /* Get the current state of the keyboard */
  if (ioctl(fd, KDGKBMODE, &previous_kbd_mode))
  {
    perror("KDGKBMODE");
    exit(1);
  }

  /* Display the current state of the keyboard */
  printf("The keyboard was in ");
  switch(previous_kbd_mode)
  {
    case K_RAW:       printf("raw")      ;break;
    case K_XLATE:     printf("xlate")    ;break;
    case K_MEDIUMRAW: printf("mediumraw");break;
    case K_UNICODE:   printf("unicode")  ;break;
    default:          printf("unknown")  ;break;
  }
  printf(" state.\n\n");

  /* Give a quick word of advise */
  printf("Press ESC to end the program ...\n\n");

  /* Setup the signal handler to trap any signal that might kill us before
     we can restore the keyboard */
  signal(SIGHUP,terminate_program);
  signal(SIGINT,terminate_program);
  signal(SIGQUIT,terminate_program);
  signal(SIGILL,terminate_program);
  signal(SIGTRAP,terminate_program);
  signal(SIGABRT,terminate_program);
  signal(SIGIOT,terminate_program);
  signal(SIGFPE,terminate_program);
  signal(SIGKILL,terminate_program);
  signal(SIGUSR1,terminate_program);
  signal(SIGSEGV,terminate_program);
  signal(SIGUSR2,terminate_program);
  signal(SIGPIPE,terminate_program);
  signal(SIGTERM,terminate_program);
#ifdef SIGSTKFLT
  signal(SIGSTKFLT,terminate_program);
#endif
  signal(SIGCHLD,terminate_program);
  signal(SIGCONT,terminate_program);
  signal(SIGSTOP,terminate_program);
  signal(SIGTSTP,terminate_program);
  signal(SIGTTIN,terminate_program);
  signal(SIGTTOU,terminate_program);

  /* Set the keyboard in raw scancode mode */
  if (tcgetattr(fd,&old_term_settings)==-1)
    perror("tcgetattr");
  if (tcgetattr(fd,&new_term_settings)==-1)
    perror("tcgetattr");

  new_term_settings.c_lflag &= ~ (ICANON | ECHO | ISIG);
  new_term_settings.c_iflag = 0;
  new_term_settings.c_cc[VTIME] = 1;	/* 0.1 sec intercharacter timeout */

  if (tcsetattr(fd,TCSAFLUSH,&new_term_settings)==-1)
    perror("tcsetattr");
  if (ioctl(fd,KDSKBMODE,K_RAW))
  {
    perror("KDSKBMODE");
    exit(1);
  }

  /* Read scancodes and attempt to convert them until we get an ESCape code */
  do
  {
    /* Read a new scancode */
    if(read(fd,&scancode,1)!=1)
    {
      fprintf(stderr,"Error reading a scancode ...\n");
      restore_console();
      exit(1);
    }
    if((scancode==ESC_SCANCODE) | (scancode==(ESC_SCANCODE | KEY_RELEASE)))
      user_abort=1;

    /* Try to convert the current scancode sequence into barcode information */
    if(cuecat_decode_barcode(scancode,cuecat_id,barcode_type,barcode)==
       BARCODE_DECODED)
    {
      printf("Barcode read by cuecat #%s :\n",cuecat_id);
      printf("Barcode type : %s\n",barcode_type);
      printf("Barcode : %s\n\n",barcode);
    }
  }
  while(!user_abort);

  /* Put the console back like we found it */
  restore_console();
  return(0);
}



/* Signal handler, to stop the program gracefully and avoid screwing up the
   console */
void terminate_program(int x)
{
  fprintf(stderr,"Caught signal %d, restoring terminal ...\n", x);
  restore_console();
  exit(1);
}



/* Restore the keyboard and console as they where when we started */
void restore_console()
{
  if (ioctl(fd, KDSKBMODE,previous_kbd_mode))
  {
    perror("KDSKBMODE");
    exit(1);
  }
  if (tcsetattr(fd, 0, &old_term_settings)==-1)
    perror("tcsetattr");
  close(fd);
}



/* Get a console descriptor */
int getfd()
{
  int fd;

  fd = open_console("/dev/tty");
  if (fd >= 0)
    return fd;

  fd = open_console("/dev/tty0");
  if (fd >= 0)
    return fd;

  fd = open_console("/dev/console");
  if (fd >= 0)
    return fd;

  for (fd = 0; fd < 3; fd++)
    if (is_a_console(fd))
      return fd;

  fprintf(stderr,"Couldnt get a file descriptor referring to the console\n");

  exit(1);		/* total failure */
}



/* Open a console */
static int open_console(char *fnam)
{
  int fd;

  fd = open(fnam, O_RDONLY);
  if (fd < 0 && errno == EACCES)
    fd = open(fnam, O_WRONLY);
  if (fd < 0 || ! is_a_console(fd))
    return -1;
  return fd;
}



/* Determine if a file descriptor is a console */
static int is_a_console(int fd)
{
  char arg;

  arg = 0;
  return (ioctl(fd, KDGKBTYPE, &arg) == 0
          && ((arg == KB_101) || (arg == KB_84)));
}

