#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include <termios.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/time.h>
#include "cwKeyboard.h"
namespace cw
{
  struct termios new_settings;
  struct termios stored_settings;

  void set_keypress(void) 
  {
    struct termios new_settings;

    tcgetattr(0,&stored_settings);
    new_settings             = stored_settings;
    new_settings.c_lflag    &= (~ICANON);
    new_settings.c_lflag    &= (~ECHO);
    new_settings.c_cc[VTIME] = 0;

    //int i;
    //for(i=0; i<NCCS; ++i)
    //  printf("%i ",new_settings.c_cc[i]);
    //printf("\n");
      

    tcgetattr(0,&stored_settings);

    new_settings.c_cc[VMIN] = 1;
    tcsetattr(0,TCSANOW,&new_settings);

  }

  void reset_keypress(void) 
  {
    tcsetattr(0,TCSANOW,&stored_settings);
  }

#define CM_KB_TBL_CNT (10)

  unsigned _cmKbTbl[][CM_KB_TBL_CNT] = 
  {

    //                           alt ctl code
    { 3, 27, 91, 68,   0,   0,   0,  0, 0, kLeftArrowKId },
    { 3, 27, 91, 67,   0,   0,   0,  0, 0, kRightArrowKId },
    { 3, 27, 91, 65,   0,   0,   0,  0, 0, kUpArrowKId },
    { 3, 27, 91, 66,   0,   0,   0,  0, 0, kDownArrowKId },
    { 3, 27, 79, 72,   0,   0,   0,  0, 0, kHomeKId },
    { 3, 27, 79, 70,   0,   0,   0,  0, 0, kEndKId },
    { 4, 27, 91, 53, 126,   0,   0,  0, 0, kPgUpKId },
    { 4, 27, 91, 54, 126,   0,   0,  0, 0, kPgDownKId },
    { 4, 27, 91, 50, 126,   0,   0,  0, 0, kInsertKId },
    { 4, 27, 91, 51, 126,   0,   0,  0, 0, kDeleteKId },
    { 6, 27, 91, 49,  59,  53,  68,  0, 1, kLeftArrowKId },
    { 6, 27, 91, 49,  59,  53,  67,  0, 1, kRightArrowKId },
    { 6, 27, 91, 49,  59,  53,  65,  0, 1, kUpArrowKId },
    { 6, 27, 91, 49,  59,  53,  66,  0, 1, kDownArrowKId },
    { 6, 27, 91, 53,  59,  53, 126,  0, 1, kPgUpKId }, 
    { 6, 27, 91, 54,  59,  53, 126,  0, 1, kPgDownKId },
    { 4, 27, 91, 51,  59,  53, 126,  0, 1, kDeleteKId }, 
    { 0,  0,  0,  0,   0,   0,   0,  0, 0, kInvalidKId }
  };
}


void cw::keyPress( cmKbRecd* p )
{
  const int bufN = 16;
  char      buf[bufN];
  int       n,j, k;
  int       rc;
  char      c;

  if( p != NULL )
  {
    p->code  = kInvalidKId;
    p->ch    = 0;
    p->ctlFl = false;
    p->altFl = false;
  }
  
  set_keypress();

  // block for the first character
  if((rc = read(0, &c, 1 )) == 1)
    buf[0]=c;

  // loop in non-blocking for successive characters
  new_settings.c_cc[VMIN] = 0;
  tcsetattr(0,TCSANOW,&new_settings);

  for(n=1; n<bufN; ++n)
    if(read(0,&c,1) == 1 )
      buf[n] = c;
    else
      break;

  new_settings.c_cc[VMIN] = 1;
  tcsetattr(0,TCSANOW,&new_settings);

  /*
  for(j=0; j<n; ++j)
    printf("{%c (%i)} ",buf[j],buf[j]);
  printf(" :%i\f\n",n);
  fflush(stdout);
  */

  if( p != NULL )
  {
    // translate the keypress
    if( n == 1)
    {
      p->code  = kAsciiKId;
      p->ch    = buf[0];
      p->ctlFl = buf[0] <= 31;    
    }
    else
    {
      for(j=0; _cmKbTbl[j][0] != 0; ++j)
        if( _cmKbTbl[j][0] == (unsigned)n )
        {
          for(k=1; k<=n; ++k)
            if( _cmKbTbl[j][k] != (unsigned)buf[k-1] )
              break;

          // if the key was found
          if( k==n+1 )
          {
            p->code  = _cmKbTbl[j][ CM_KB_TBL_CNT - 1 ];
            p->ctlFl = _cmKbTbl[j][ CM_KB_TBL_CNT - 2 ];
            break;
          }
        }
    }
  }
  reset_keypress();
}


// Based on: // From: http://www.flipcode.com/archives/_kbhit_for_Linux.shtml

int cw::isKeyWaiting()
{
  static const int STDIN       = 0;
  static bool      initialized = false;
  struct timeval   timeout;
  fd_set           rdset;

  if( !initialized )
  {
    // Use termios to turn off line buffering
    struct termios term;
    tcgetattr(STDIN, &term);
    term.c_lflag &= ~ICANON;
    tcsetattr(STDIN, TCSANOW, &term);
    setbuf(stdin, NULL);
    initialized = true;
  }

  if(0)
  {

    FD_ZERO(&rdset);
    FD_SET(STDIN, &rdset);
    timeout.tv_sec  = 0;
    timeout.tv_usec = 0;

    // time out immediately if STDIN is not ready.
    return select(STDIN + 1, &rdset, NULL, NULL, &timeout);
  }
  else
  {
    int bytesWaiting;
    ioctl(STDIN, FIONREAD, &bytesWaiting);
    return bytesWaiting;
  }

}


void cw::kbTest1()
{
  set_keypress();
  
  int c = 0;
  int r;
  printf("'q' to quit\n");
  while( c != 'q' )
  {

    printf("0>"); fflush(stdout);
    r = read(0, &c, 1 );
    printf("0: %c (%i)\r\n",(char)c,c);


    new_settings.c_cc[VMIN] = 0;
    tcsetattr(0,TCSANOW,&new_settings);

    if( r == 1 && c == 27 )
    {
      r = read(0, &c, 1 );
      printf("1: %c (%i)\n",(char)c,c);

      if( r == 1 && c == '[' )
      {
        r = read(0, &c, 1 );
        printf("2: %c (%i)\n",(char)c,c);

      }

    }

    new_settings.c_cc[VMIN] = 1;
    tcsetattr(0,TCSANOW,&new_settings);

  }

  reset_keypress();
}

void cw::kbTest2()
{
  set_keypress();

  fd_set         rfds;
  struct timeval tv;
  int            retval;
  int            c=0;
    
  printf("'q' to quit\n");
  
  while( c != 'q' )
  {
    int i = 0;

    printf(">");

    do
    {      
      // Watch stdin (fd 0) to see when it has input.
      FD_ZERO(&rfds);
      FD_SET(0, &rfds);

      // don't wait
      tv.tv_sec =  0;
      tv.tv_usec = 0;

      retval = select(1, &rfds, NULL, NULL, i==0 ? NULL : &tv);
      // Don't rely on the value of tv now - it may have been overwritten by select

      // if an error occurred
      if (retval == -1)
        perror("select()");
      else 
      {
        // if data is waiting
        if (retval)
        {
          c = getchar();
          printf("%i %c (%i) ",i,(char)c,c);
          ++i;

        }
        else
        {
          printf("\n");
          break; // no data available
        }
      }

    } while( 1 );
  }

  reset_keypress();
}

void cw::kbTest3()
{
  set_keypress();

  int i =0;

  printf("<enter> to quit");
  
  while(1)
  {
    cw::sleepMs(500); // sleep milliseconds

    printf("%i\n",i);
    
    i += 1;

    if( isKeyWaiting() )
      break;

  }

  reset_keypress();
  
}