#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/termios.h>
#include <signal.h>
#include <unistd.h> 
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>  
#include <stdio.h>   

const char jogpipe[]=".jogdial.pipe";

const long repeat_rate = 100;  // repeat rate (in millisecs)
            
int cm0;
int serial,pipefd;
int mode_count=0,mode=0;
int rot=0,count=0,cd,dsr,cts;

struct timeval mode_t0; // time when MODE was pressed

int states[8];
    
void alarm_handler (int)
{
  int cm;
  ioctl(serial,TIOCMGET,&cm);
  if( cm&TIOCM_RI )     // RI high - MODE is pressed
  {
    if( !(cm0&TIOCM_RI) )  // RI up - just pressed
    {
      mode_count=0;
      mode=1;
      gettimeofday(&mode_t0,NULL);
    }
    else                   // staying high - check for auto-repeat
    {
      struct timeval t1;
      gettimeofday(&t1,NULL);
      long dt = (t1.tv_sec - mode_t0.tv_sec)*1000 +
                (t1.tv_usec - mode_t0.tv_usec)/1000;
      int repeat = (int)(dt/repeat_rate);
      if( mode_count != repeat )
      {
        mode_count=repeat;
        mode=1;
      }
    }
  } 
  else if( cm0&TIOCM_RI ) // RI down - MODE released
  {
    mode_count=-1;
    mode=1;
  }
  int a = cm&TIOCM_CD;
  cd  = (a!=0);
  dsr = ((cm&TIOCM_DSR)!=0); 
  cts = ((cm&TIOCM_CTS)!=0); 
  if( a != (cm0&TIOCM_CD) )
  {
    rot=1;
//    int bit = cm&(a?TIOCM_DSR:TIOCM_CTS);
//    count += (a?bit:!bit) ? -1 : 1;
    count += states[cd*4+dsr*2+cts];
  }
  cm0=cm;
}
    
int main (int argc,char *argv[])
{
// setup states-table
  states[1]=states[4]=states[3]=states[2]= 1;
  states[0]=states[5]=states[6]=states[7]= -1;
// open serial port
  char *serial_port="/dev/ttyS0";
  if( argc>1 )
    serial_port=argv[1];
  serial = open(serial_port,O_RDONLY);
  if( serial<0 ) 
  {
    perror("open serial");
    return 1;
  }
// open output pipe
  int pipefd = open(jogpipe,O_WRONLY);
  if( pipefd<0 ) 
  {
    perror("open pipe");
    return 1;
  }
// setup timer signal handler
  const struct sigaction act = {
    alarm_handler,
    0,
    0,
    0,
    0
  };
  sigaction(SIGALRM,&act,0);
// get initial port value
  ioctl(serial,TIOCMGET,&cm0);
// start timer
  struct itimerval timer = { { 0,1000 }, {0,1000} };
  setitimer(ITIMER_REAL,&timer,NULL);
// loop forever
//  for(;;)
//  {
//    if( ioctl(serial,TIOCMGET,&cm0)<0 )
//    {
//      perror("open");
//      return 1;
//    }
//    printf("mode:%d right:%d left:%d\n",cm0&TIOCM_CD,cm0&TIOCM_RI,cm0&TIOCM_DSR);
//  }
  while( pause() )
  {
    char s[5];
    if( mode ) 
    {
      fprintf(stderr,"mode: %d\n",mode_count);
      if( mode_count<0 )
        write(pipefd,"M--\n",4);
      else
      {
        sprintf(s,"M%-2d\n",mode_count%100);
        write(pipefd,s,4);
      }
      mode=0;
    }
    if( rot ) 
    {
      fprintf(stderr,"count:%d cd:%d dsr:%d cts:%d\n",count,cd,dsr,cts);
      if( count<0 )
        sprintf(s,"L%-2d\n",(-count)%100);
      else
        sprintf(s,"R%-2d\n",count%100);
      write(pipefd,s,4);
      count=rot=0;
    }
  }
}
