/*  Comm keyboard routines */

#define  KEYBOARD 1
#include "globals.h"

extern  int capture;

#define ERR    "\nKEY MACRO error."
#define KEYERR -1
#define NOMEM  -2
#define OFFSET FN1                  /* convert to cardinal number */

static UBYTE *string;               /* temp string pointer */
static UBYTE workbuff[ SECSIZ ];    /* keyboard macro work area */

#define ENTRIES  112                /* keys on keyboard */

UBYTE  toasc(code,qual)
USHORT qual,code;
{
   static USHORT  caps = FALSE;
   UBYTE  c;
   static UBYTE nosh[ ENTRIES ] = {
                    '`','1','2','3','4','5','6','7',
                    '8','9','0','-','=',SLH,UND,'0',
                    'q','w','e','r','t','y','u','i',
                    'o','p','[',']',UND,'1','2','3',
                    'a','s','d','f','g','h','j','k',
                    'l',';',APS,UND,UND,'4','5','6',
                    UND,'z','x','c','v','b','n','m',
                    ',','.','/',UND,'.','7','8','9',
                    ' ',BKS,TAB,ENT,RET,ESC,DEL,UND,
                    UND,UND,'-',UND,CUP,CDN,CFW,CBK,
                    FN1,FN2,FN3,FN4,FN5,FN6,FN7,FN8,
                    FN9,F10,UND,UND,UND,UND,UND,HLP,
                    LSH,RSH,CAP,CTL,LAL,RAL,LAM,RAM,
                    LMB,RMB,MMB,UND,UND,UND,UND,UND
                  };

   static UBYTE shift[ ENTRIES ] = {
                    '~','!','@','#','$','%','^','&',
                    '*','(',')','_','+','|',UND,'0',
                    'Q','W','E','R','T','Y','U','I',
                    'O','P','{','}',UND,'1','2','3',
                    'A','S','D','F','G','H','J','K',
                    'L',':','"',UND,UND,'4','5','6',
                    UND,'Z','X','C','V','B','N','M',
                    '<','>','?',UND,'.','7','8','9',
                    ' ',BKS,TAB,ENT,RET,ESC,DEL,UND,
                    UND,UND,'-',UND,CUP,CDN,CFW,CBK,
                    SF1,SF2,SF3,SF4,SF5,SF6,SF7,SF8,
                    SF9,S10,UND,UND,UND,UND,UND,SHH,
                    LSH,RSH,CAP,CTL,LAL,RAL,LAM,RAM,
                    LMB,RMB,MMB,UND,UND,UND,UND,UND
                  };

   switch(code)
    {
      case  98:   caps   = TRUE;  break;      /* caps lock pressed  */
      case 226:   caps   = FALSE; break;      /* caps lock released */
    }


   if(code < ENTRIES)
    {
       c = nosh[ code ];                       /* default lower case */
       if(qual & 0x40)                         /* left amiga */
       {
         switch(c)
         {
            case 'p':   return TOGPRT;         /* Toggle printer */
            case 'c':   return TOGCAP;         /* Toggle capture buffer */
            case 's':   return TOGSCR;
         }
       }
       if(qual & 3)
            return (shift[ code ]);            /* shift key down     */
       if( isalpha(c) )                        /* control or CAPS?   */
         {
          if(qual & 8)
            return(UBYTE)(shift[ code ] - '@');/* control down ?     */
                                               /* make it a control  */
          if ( caps )                          /* caps lock down     */
            return ( shift[ code ]);           /* make it upper case */
         }
       return (c);
    }
   return (UND);                               /* for release code   */
} /* end of routine */

/*
   Expand keyboard macros.  Control characters are sent by prefixing the
   ASCII equiv. with ^.  Ex, control C is ^C, control X is ^X.
   Also translates C type slash characters like \t, \r and \n.
   In this implimentation, \n and \r generate the RETURN code (0xD).
   Also, \w produces a 500 millisecond delay and \l produces a line break.
*/
void expand_macro(key)
char key;
{
   key -= OFFSET;                        /* F1 becomes offset zero */
   string = keymacro[ key ];             /* point to definition */
   if(string == NULL) return;            /* return if not defined */
   sendout( string );
}

/* do the actual expansion.  Send the characters to the modem */

sendout(str)
UBYTE *str;
{
   static UBYTE macro[] = "00000000000000000000";
   unsigned  key;
   UBYTE     c;

   while( c = *str++ )                    /* do until end of string */
    {
      if( c == '^') {                     /* control character ^? */
         if((c = *str++) == '^')
            break;
         else c = toupper(c)- 0x40;       /* convert only valid chars */
      }
      else if( c == '\\')                /* have an escape character? */
         switch( c = tolower(*str++))    /* yes, get next character */
         {
            case 'r': c = '\r'; break;   /* C equivilents */
            case 'n': c = '\r'; break;
            case 't': c = '\t'; break;
            case 'f': c = '\f'; break;
            case 'b': c = '\b'; break;
                                         /* now my own */
/*
            case 'p':
                      c = *str++;
                      if( c == '^')
                          c = toupper(*str++) - 0x40;
                      do {
                            key = readchar(10,0);
                            if( key == TIMEOUT ) break;
                      } while ( (tolower(key) != tolower(c)) );
                      continue;
*/
            case 'l':                    /* line break */
                      SendBreak(); continue;
            case 'w':                    /* delay 1/2 second */
                      Delay(25L);continue;
            case 'm':                    /* expand another macro */
                      key = atoi(str);
                      while(isdigit(*str))
                           str++;
                      if( (key > 0) && (key <= KEYMACS) )
                      {
                        if(macro[ key -1 ] != '0')
                        {
                           emits_rx("\nKEY MACRO RECURSION not allowed -- or desired!\n");
                           continue;
                        }
                        macro[ key-1 ] = '1';
                        expand_macro( key - 1 + OFFSET );
                        macro[ key-1 ] = '0';
                      }
                      continue;

            case '\\':break;

            default:                     /* was really a \ character alone */
               str--;                    /* adjust string pointer */
               c = '\\';                 /* and send the \ */
          }
      if(halfduplex)
        emit_rx( c );
      if(split_screen)
        emit_tx( c );                    /* echo it to the user */
      if (capture && capton)             /* capturing data to disk file? */
         if (isprint(c)) buffer_it(c);
      sendchar( c );
    }
}


/*  open keyboard macro file.  Return 0 if not found, else return # of
     macros in the library.
    allocate memory for the macros and copy them to memory.
*/
int Init_keymacros()
{
   USHORT i;
   for(i = 0; i < KEYMACS; i++)        /* init array of pointers */
      keymacro[i] = NULL;
   Load_keymacros(commkeys);
}

Load_keymacros(name)
UBYTE *name;
{
   extern UBYTE *malloc();
   int index, i;
   FILE *inp;
   UBYTE temp[42], *file, p[42];

   if(*name == NULL) return;           /* no name given */
   strcpy(p,name);

   i = strlen(p);
   while(--i)                          /* remove trailing blanks from */
     if(p[i] != ' ') break;            /* filename */

   if(i == 0) return;                  /* blank name given */
   p[++i] = NULL;

   index = 0;

   strcpy(temp,install.DefDir);
   strcat(temp,p);

   file = name;                        /* try to open macro file */
   if(( inp = fopen(p,"r")) == NULL)   /* check default directory */
   {
     file = temp;
     if((inp = fopen(temp,"r")) == NULL) /* then SYS: directory */
        return NULL;
   }
   strcpy(commkeys,file); 
   Free_keymacros();                   /* free any memory allocated */

   while (fgets(workbuff,SECSIZ,inp))           /* do until end of file */
   {
        if(strlen(workbuff) < 3) continue;      /* ignore short lines   */
        workbuff[ strlen(workbuff)-1 ] = NULL;  /* kill trailing \n     */
        if((index = Add_keymacro()) == NOMEM)   /* no memory left       */
        {
          emits_rx("\nOut of memory\n");
          break;
        }
   }
   fclose(inp);
   return index;
}

/* Save key macros to named file */
Save_keymacros(name)
UBYTE *name;
{
   FILE   *out;
   USHORT i;
   int    error;

   if((out = fopen(name,"w")) == NULL)
   {
      sprintf(sbuff,"\nCan't open file %s\n",name);
      emits_rx(sbuff);
      return NULL;
   }
   for(i = 0; i < KEYMACS; i++)
      if( keymacro[ i ] )
         fprintf(out,"%c%d%s\n",
                       (i > 9) ? 'S' : 'F',
                       (i > 9) ? i-9 : i+1,
                       keymacro[ i ]);
   error = ferror(out);
   fclose( out );
   if( error )
   {
      emits_rx("Error during file write\n");
      return NULL;
   }
   return OK;
}

/*
   Return memory allocated for all key macros
*/
Free_keymacros()
{
   int i;
   for( i=0; i < KEYMACS; i++)
     free_macro(i);
}

/*
   Return memory allocated for single macro
*/
free_macro(num)
int num;
{
   if(keymacro[ num ])
   {
      free(keymacro[ num ]);
      keymacro[ num ] = NULL;
   }
}
/*
   Bring up the ASCII input window for macro entry
*/
Edit_keymacro()
{
   workbuff[0] = NULL;
   getstring(" USE","new key macro",workbuff,SECSIZ);
}

/*
   Parse macro string and add it to macro list
*/
Add_keymacro()
{
  int index, shift;

  if(*workbuff == NULL) return NULL;
  string = &workbuff[2];
  
  if( (toupper(*workbuff) != 'F' && toupper(*workbuff) != 'S') ||
             !isdigit(workbuff[1]))
   {                    /* line doesn't start with Fn or Sn */
     emits_rx(ERR);
     sprintf(sbuff,"\nExpected Fnn or Snn but read:  %10s\n",workbuff);
     emits_rx(sbuff);
     return KEYERR;
   }
  shift = (toupper(*workbuff) == 'F') ? 0 : 10;

  index = workbuff[1] - '1';       /* index is 0 to 9 */
  if(workbuff[1] == '1')
    if(workbuff[2] == '0')
    {                              /* F10 is ok */
      string++;                    /* skip past extra 0 */
      index = 9;                   /* make index 10 - 1 */
    }

  index += shift;                  /* account for shift Fnn key */
  free_macro(index);               /* if already allocated, deallocate it */

  if(*string == NULL) return NULL;
  keymacro[index] = malloc(strlen(string));
  if(keymacro[index])             /* if allocation succeeded, copy string */
    strcpy(keymacro[index],string);
  else                            /* else, can't do it */
   {
     sprintf(sbuff,"\nCan't allocate memory for key macro F%d\n", index+1);
     emits_rx(sbuff);
     return NOMEM;
   }
  return index;
}

