#include "irec.h"

#define Q_MASK (IEQUALIFIER_CONTROL | IEQUALIFIER_LALT | \
                IEQUALIFIER_RALT   | IEQUALIFIER_LCOMMAND | IEQUALIFIER_RCOMMAND | \
                IEQUALIFIER_NUMERICPAD )

#define Q_MASK_2 (IECODE_LBUTTON | IECODE_RBUTTON | IECODE_MBUTTON)

#define CA_MASK  (IEQUALIFIER_CONTROL | IEQUALIFIER_LSHIFT)
#define CA_MASK2 (IEQUALIFIER_CONTROL | IEQUALIFIER_RSHIFT)


struct Interrupt *IHandler;
struct IOStdReq  *HandlerIO;
struct MsgPort   *InputPort;
BYTE              device;
struct InputEvent record_anchor;
struct InputEvent *record;
struct Task       *ourtask;
LONG               SigBit;
BOOL               break_on;

static void  free_recordline(void);
static void  write_file(UBYTE *filename);
static ULONG make_control(struct InputEvent *ie,UBYTE *buffer,UBYTE *t);
void * __asm __saveds input_handler(register __a0 struct InputEvent *ev);

ULONG numev;

void rec_start_f(ULONG n)
{
 if(!recording)
 {
  recording = TRUE;
  numev = n;
  if(n && max_on)break_on = TRUE;
  else break_on = FALSE;
  HandlerIO->io_Data = (APTR)IHandler;
  HandlerIO->io_Command = IND_ADDHANDLER;
  BeginIO((struct IORequest *)HandlerIO);
  record_anchor.ie_NextEvent = NULL;
  record = &record_anchor;
 }
}

void rec_stop_f(UBYTE *name)
{
 if(!name)name = rec_file;
 if(recording)
 {
  AbortIO((struct IORequest *)HandlerIO);
  WaitIO((struct IORequest  *)HandlerIO);
  HandlerIO->io_Data = (APTR)IHandler;
  HandlerIO->io_Command = IND_REMHANDLER;
  DoIO((struct IORequest *)HandlerIO);
  recording = FALSE;
  write_file(name);  
  free_recordline();
 }
}

LONG init_recorder()
{
 if(InputPort = CreateMsgPort())
 {
  SigBit =  AllocSignal(-1);
  if(SigBit != -1 )
  {
   ourtask = FindTask(0);
   if(IHandler = AllocVec(sizeof(struct Interrupt),MEMF_PUBLIC | MEMF_CLEAR))
   {
    if(HandlerIO = (struct IOStdReq *)CreateExtIO(InputPort,sizeof(struct IOStdReq)))
    {
     if(!(device = OpenDevice("input.device",NULL,(struct IORequest *)HandlerIO,0L)))
     {
      IHandler->is_Code = input_handler;
      /*IHandler->is_Data = mem_pool;*/
      IHandler->is_Node.ln_Pri  = h_priority;
      IHandler->is_Node.ln_Name = "irec.input";
      return(TRUE);
     }
    }
   }
  }  
 }
 return (FALSE);
}

void exit_recorder()
{
 if(recording)
 {
  AbortIO((struct IORequest *)HandlerIO);
  WaitIO((struct IORequest  *)HandlerIO);
  HandlerIO->io_Data = (APTR)IHandler;
  HandlerIO->io_Command = IND_REMHANDLER;
  DoIO((struct IORequest *)HandlerIO);
 }
 if(HandlerIO)
 {
  if(!device)CloseDevice((struct IORequest *)HandlerIO);
  DeleteExtIO((struct IORequest *)HandlerIO);
 }
 if(InputPort)DeletePort(InputPort);
 if(SigBit != -1)FreeSignal(SigBit);
}

static void free_recordline()
{
 struct InputEvent *x;
 struct InputEvent *t = record_anchor.ie_NextEvent;
 while(t)
 {
  x = t->ie_NextEvent;
  FreeVec(t);
  t = x;
 }
 record_anchor.ie_NextEvent = NULL;
}


/***********************************************************/

void * __asm __saveds input_handler(register __a0 struct InputEvent *ev)
{
 struct InputEvent *t;
 if((ev->ie_Class == IECLASS_RAWKEY)||((ev->ie_Class == IECLASS_RAWMOUSE)&&(ev->ie_Code != IECODE_NOBUTTON)))
 {
  if(t = AllocVec(sizeof(struct InputEvent),MEMF_PUBLIC | MEMF_CLEAR))
  {
   CopyMem(ev,t,sizeof(struct InputEvent));
   t->ie_NextEvent = NULL;
   if((ev->ie_Class == IECLASS_RAWMOUSE)&&(ev->ie_Code & Q_MASK_2))
   {
    t->ie_X = IntuitionBase->ActiveScreen->MouseX;
    t->ie_Y = IntuitionBase->ActiveScreen->MouseY;
   }
   record->ie_NextEvent = t;
   record = t;
   if(break_on)
   {
    if(ev->ie_Code & IECODE_UP_PREFIX)
    {
     --numev;if(!numev) Signal(ourtask, 1L << SigBit);
    }
   }
  }
 }
 return ((void *)ev);
}

static void write_file(UBYTE *name)
{
 FILE  *rec;
 struct InputEvent *e  = NULL;
 struct InputEvent  e1;
 struct InputEvent *le = NULL;
 UBYTE buffer[STRMAXLEN]; 
 char t[20];
 if(rec=fopen(name,"w"))
 {
  e = record_anchor.ie_NextEvent;
  while(e)
  {
   BOOL spc = FALSE;
   long bytes;
   UBYTE *dl = NULL;
   *buffer = 0;
   if((i_mb) &&(e->ie_Class == IECLASS_RAWMOUSE))
   {
    char ud = 'd';
    if(le)
    {
     ULONG s = ((((e->ie_TimeStamp.tv_secs) - (le->ie_TimeStamp.tv_secs))-1) * 50) +
               (((e->ie_TimeStamp.tv_micro) + (1000000 - le->ie_TimeStamp.tv_micro)) / 20000);
     if((i_dl) && (s >= min_delay))fprintf(rec,"\\d%04ld",s);
    }
    le = e;
    if(e->ie_Code & IECODE_UP_PREFIX)
    {
     ud = 'u';
     e->ie_Code -= IECODE_UP_PREFIX;
    }
    switch(e->ie_Code)
    {
     case IECODE_LBUTTON : fprintf(rec,"\\(lm%c:%d,%d)",ud,e->ie_X,e->ie_Y);
                           break;
     case IECODE_RBUTTON : fprintf(rec,"\\(rm%c:%d,%d)",ud,e->ie_X,e->ie_Y);
                           break;
     case IECODE_MBUTTON : fprintf(rec,"\\(mm&c:%d,%d)",ud,e->ie_X,e->ie_Y);
                           break;
    }
   }
   else
   {
   BOOL hold = FALSE;
   bytes = MapRawKey(e,buffer,STRMAXLEN,NULL);
   CopyMem(e,&e1,sizeof(struct InputEvent));
   if(e1.ie_Qualifier & IEQUALIFIER_RALT)e1.ie_Qualifier    -= IEQUALIFIER_RALT,hold = TRUE;
   if(e1.ie_Qualifier & IEQUALIFIER_LALT)e1.ie_Qualifier    -= IEQUALIFIER_LALT,hold = TRUE;
   if(e1.ie_Qualifier & IEQUALIFIER_CONTROL)e1.ie_Qualifier -= IEQUALIFIER_CONTROL,hold = TRUE;
   if(hold)bytes = MapRawKey(&e1,buffer,STRMAXLEN,NULL);
   if(bytes)
   {
    *(buffer + bytes) = 0;
    if(bytes == 1)
    {
     /* hier war mal was 8( */
     {
      *t = *buffer;*(t + 1) = 0;
      *buffer = 0;
      if(*t == 0x7f){spc = TRUE; strcpy(t,"del");dl = t;}
      if(*t < ' ')
      {
       spc = TRUE;
       switch(*t)
       {
        case '\r' : strcpy(t,"return");
                    break;
        case '\n' : strcpy(t,"enter");
                    break;
        case '\t' : strcpy(t,"tab");
                    break;
        case 0x1b : strcpy(t,"esc");
                    break;
        case 0x08 : strcpy(t,"backspace");
                    break;
        default   : break;
       }
      }
      if(make_control(e,buffer,dl)){ strcat(buffer,t); strcat(buffer,">");}
      else 
      {
       if(spc){strcat(buffer,"<");strcat(buffer,t);strcat(buffer,">");}
       else strcat(buffer,t);
      }
      if(le)
      {
       ULONG s = ((((e->ie_TimeStamp.tv_secs) - (le->ie_TimeStamp.tv_secs))-1) * 50) +
                 (((e->ie_TimeStamp.tv_micro) + (1000000 - le->ie_TimeStamp.tv_micro)) / 20000);
       if((i_dl) && (s >= min_delay))fprintf(rec,"\\d%04ld",s);
      }
      fputs(buffer,rec);
      le = e;      
     }
    }
    else
    {
     if(*buffer == 0x9b)
     { 
      spc = TRUE;
      switch(*(buffer + 1))
      {
       case '0'     : strcpy(t,"F1"); break;
       case '1'     : switch(*(buffer + 2))
                      {
                       case '0' : strcpy(t,"shift F1"); break;
                       case '1' : strcpy(t,"shift F2"); break;
                       case '2' : strcpy(t,"shift F3"); break;
                       case '3' : strcpy(t,"shift F4"); break;
                       case '4' : strcpy(t,"shift F5"); break;
                       case '5' : strcpy(t,"shift F6"); break;
                       case '6' : strcpy(t,"shift F7"); break;
                       case '7' : strcpy(t,"shift F8"); break;
                       case '8' : strcpy(t,"shift F9"); break;
                       case '9' : strcpy(t,"shift F10"); break;
                       case '~' : strcpy(t,"F2"); break;
                      }
                      break;
       case '2'     : strcpy(t,"F3"); break;
       case '3'     : strcpy(t,"F4"); break;
       case '4'     : strcpy(t,"F5"); break;
       case '5'     : strcpy(t,"F6"); break;
       case '6'     : strcpy(t,"F7"); break;
       case '7'     : strcpy(t,"F8"); break;
       case '8'     : strcpy(t,"F9"); break;
       case '9'     : strcpy(t,"F10"); break;
       case '?'     : strcpy(t,"help"); break;
       case 'A'     : strcpy(t,"up"); break;
       case 'B'     : strcpy(t,"down"); break;
       case 'D'     : strcpy(t,"left"); break;
       case 'C'     : strcpy(t,"right"); break;
       case 'T'     : strcpy(t,"shift up"); break;
       case 'S'     : strcpy(t,"shift down"); break;
       case ' '     : switch(*(buffer + 2))
                      {
                       case 'A' : strcpy(t,"shift left");  break;
                       case '@' : strcpy(t,"shift right"); break;
                      }
                      break;
      }
     }
     *buffer = 0;
     if(make_control(e,buffer,t)){ strcat(buffer,t); strcat(buffer,">");}
     else 
     {
      if(spc){strcat(buffer,"<");strcat(buffer,t);strcat(buffer,">");}
      else strcat(buffer,t);
     }
     if(le)
     {
      ULONG s = ((((e->ie_TimeStamp.tv_secs) - (le->ie_TimeStamp.tv_secs))-1) * 50) +
                (((e->ie_TimeStamp.tv_micro) + (1000000 - le->ie_TimeStamp.tv_micro)) / 20000);
       if((i_dl) && (s >= min_delay))fprintf(rec,"\\d%04ld",s);
     }
     fputs(buffer,rec);
     le = e;
    }
   }
   } /* RAWMOUSE */
   e = e->ie_NextEvent;
   if(e == record)break;
  }
  fclose(rec); 
 }
}

static ULONG make_control(struct InputEvent *ie,UBYTE *buffer,UBYTE *t)
{
 ULONG rc = FALSE;
 if(ie->ie_Qualifier & Q_MASK)
 {
  rc = TRUE;
  strcat(buffer,"<");
  if(ie->ie_Qualifier & IEQUALIFIER_LEFTBUTTON)strcat(buffer,"leftbutton ");
  if(ie->ie_Qualifier & IEQUALIFIER_RBUTTON)strcat(buffer,"rbutton ");
  if(ie->ie_Qualifier & IEQUALIFIER_MIDBUTTON)strcat(buffer,"midbutton ");
  if(ie->ie_Qualifier & IEQUALIFIER_CONTROL)strcat(buffer,"control ");
  if(ie->ie_Qualifier & IEQUALIFIER_RALT)strcat(buffer,"ralt ");
  if(ie->ie_Qualifier & IEQUALIFIER_LALT)strcat(buffer,"lalt ");
  if(ie->ie_Qualifier & IEQUALIFIER_RCOMMAND)strcat(buffer,"rcommand ");
  if(ie->ie_Qualifier & IEQUALIFIER_LCOMMAND)strcat(buffer,"lcommand ");
  if(ie->ie_Qualifier & IEQUALIFIER_NUMERICPAD)strcat(buffer,"numericpad ");
  if(!t)if(ie->ie_Qualifier & IEQUALIFIER_LSHIFT)strcat(buffer,"lshift ");
  if(!t)if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)strcat(buffer,"rshift ");
 }
 return (rc);
}
