/*{{{  #includes*/
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <local/bool.h>

#define PUTRC
#include "../h/rcformat.h"

#define KEYTAB_C

#include "keybind.h"
/*}}}  */

/*{{{  types*/
/*{{{  type KEYTABLES*/
typedef struct
 { char *name;
   bool def;
   char *terms[termmax];
   KEY *top_level[termmax];
   int bind_mark[termmax];
   int count[termmax];
 } KEYTABLES;
/*}}}  */
/*{{{  typedef mouse_supports*/
typedef struct
 { char* name;
   char* code;
   int code_lg;
 } mouse_supports;
/*}}}  */
/*}}}  */
/*{{{  variables*/
private unsigned char abort_key= 0;
private bool ab_set=FALSE;
private int m_kbd_used=0;
private bool key_used=FALSE;
private KEYTABLES keytabs[KBD_COUNT];
private int bind_off=std_kbd;

/* this name cannot be given with OCL :-)  */
private char *default_term="((((((foo))))))";
private mouse_supports m_list[]=
 /*{{{  init values*/
 { "scann",0,0,
#   ifdef XTERM
      XTERM_MOUSE_NAME,XTERM_MOUSE_CODE,XTERM_MOUSE_LG,
#   endif
#   ifdef MGR
      MGR_MOUSE_NAME,MGR_MOUSE_CODE,MGR_MOUSE_LG,
#   endif
#   ifdef OS_MOUSE_TAG
      OS_MOUSE_NAME,OS_MOUSE_CODE,OS_MOUSE_LG,
#   endif
   NO_MOUSE,0,0,
   0,0,0
 };
 /*}}}  */
public char *m_name=DEFAULT_MOUSE;
/*}}}  */

/*{{{  init_mouse*/
public bool init_mouse(void)
{ mouse_supports *x=m_list;
  int m_number;

  m_number=0;
  do
   { if (!strcmp(x->name,m_name)) return(FALSE);
     m_number++;
   }
  while ((x++)->name);
  return(TRUE);
}
/*}}}  */
/*{{{  show_mice*/
public void show_mice(FILE *f)
{ mouse_supports *x=m_list;
  bool first=TRUE;

  fprintf(f,"%s(",m_name);
  while (x->name) { fprintf(f,first?"%s":",%s",(x++)->name);first=FALSE; }
  fprintf(f,")");
}
/*}}}  */
/*{{{  key_alloc*/
private KEY *key_alloc(void)
{
  KEY *x=(KEY*)malloc(sizeof(KEY));

  /*{{{  no memory?*/
  if (!x) {
    printf(M_NOMEMORY);
    return(0);
  }
  /*}}}  */
  x->next=x->n_level=0;
  x->code=x->key=0;
  return(x);
}
/*}}}  */
/*{{{  key_copy*/
private KEY *key_copy(KEY *p)
{ KEY *n;

  if (!p) return(0);
  if (!(n=key_alloc())) return(0);
  *n = *p;
  if (n->next && !(n->next=key_copy(p->next))) return(0);
  if (n->n_level && !(n->n_level=key_copy(p->n_level))) return(0);

  return(n);
}
/*}}}  */
/*{{{  add_keysequence*/
/*{{{  decode*/
private char *decode(TOKEN keynumber)
{ KEYNAME *seek = bindings;
  int off;
  static char name[name_lg];

  /*{{{  store offset to first fix-macro*/
  if (keynumber>=O_CALL_FIX)
   { off=keynumber-O_CALL_FIX+1;
     keynumber=O_CALL_FIX;
   }
  else
     off=0;
  /*}}}  */
  while (seek->name)
    if ((seek->num)==(TOKEN) keynumber)
      /*{{{  return the name*/
      if (off)
       { sprintf(name,"%d=%s %d",keynumber,seek->name,off);
         return(name);
       }
      else
       { sprintf(name,"%d=%s",keynumber,seek->name);
         return(name);
       }
      /*}}}  */
    else
      seek++;
  sprintf(name,F_NOKEYNUMBER,keynumber);
  return(name);
}
/*}}}  */
/*{{{  add_key*/
private KEY *add_key (unsigned char key,KEY *current)
{
  KEY *k,*a;

  /*{{{  is key already used?*/
  for (k = current->n_level;k;k = k->next) {
    /*{{{  used => return it*/
    if (k->key == key) {
      if (k->code) {
        error_po();
        printf(F_ISMASKED,decode(k->code));
        return(0);
      }
      return (k);
    }
    /*}}}  */
    if (k->next == 0) break;
  }
  /*}}}  */
  /*{{{  else generate new tree-entry*/
  /*{{{  get new tree-node*/
  if (!(a=key_alloc())) return(0);
  a->key = key;
  /*}}}  */
  if (k == 0)
    current->n_level = a;
  else
    k->next = a;
  return (a);
  /*}}}  */
}
/*}}}  */
/*{{{  set_key_code*/
private bool set_key_code (TOKEN code,KEY *current)
{ if (current->n_level) {
    error_po();
    printf(F_MASKS,decode(code));
    return(TRUE);
  }
  current->code = code;
  return (FALSE);
}
/*}}}  */

private bool add_keyseq(int curr_kbd,int curr_term,TOKEN k, char *s, int lg)
{ KEY *key_point=keytabs[curr_kbd].top_level[curr_term];

  keytabs[curr_kbd].count[curr_term]++;
  while (lg--)
    if (!(key_point=add_key(*s++,key_point)))
      return(TRUE);
  if (set_key_code(k,key_point)) return(TRUE);
  return(FALSE);
}
/*}}}  */
/*{{{  init_keytab*/
public KEY *init_keytab(int no,int t)
{
  /*{{{  first init all tables*/
  if (bind_off==std_kbd)
   { int i;
     int t;

     for (i=0;i<KBD_COUNT;i++)
      { keytabs[i].name="";
        keytabs[i].def=TRUE;
        for (t=0;t<termmax;t++)
         { keytabs[i].top_level[t]=0;
           keytabs[i].terms[t]="";
           keytabs[i].count[t]=0;
         }
      }

   }
  /*}}}  */
  keytabs[no].top_level[t]=key_alloc();
  keytabs[no].bind_mark[t]=bind_off++;
  return(keytabs[no].top_level[t]);
}
/*}}}  */

/*{{{  kb_com_parse*/
private bool kb_com_parse(TOKEN *f,char **n,K_SEQ *k)
{
  TOKEN tok;
  tokens token;
  static char simple_char_name[3];

  /*{{{  correct binding-object given?*/
  token=get_token(FALSE);
  /*{{{  if opcode*/
  if (token==OPCODE) {
    tok=tk_key->num;
    *n=tk_key->name;
  /*}}}  */
  /*{{{  string (check also length=1!)*/
  } else if ((token==MACRO)&&(strlen(tk_string)==1)) {
    simple_char_name[0]='\"';
    simple_char_name[1]=tk_string[0];
    simple_char_name[2]='\0';
    tok=tk_macro[0];
    *n=simple_char_name;
  /*}}}  */
  /*{{{  if operation*/
  } else if (token==OPERATION) {
    if (tk_operation->place==0)
     { if (tk_operation->length!=1)
        { error_po();
          fprintf(stderr,M_NODEBOUND);
          return(TRUE);
        }
       else
          tok= *(tk_operation->ops);
     }
    else
       tok=(TOKEN)((int)O_EXE_MACRO+tk_operation->place);
    *n=tk_operation->op_name;
  /*}}}  */
  /*{{{  else error*/
  } else {
    error_po();
    fprintf(stderr,M_NOCOMMAND);
    return(TRUE);
  }
  /*}}}  */
  /*}}}  */
  if (parse_keysequence(k)) return(TRUE);
  if (end_parse(M_KEYEND)) return(TRUE);
  *f=tok;

  return(FALSE);
}
/*}}}  */
/*{{{  keydef_code*/
public bool keydef_code(void)
{ TOKEN f;
  char *n;
  K_SEQ k;

  /*{{{  prepare standard keysequence-struct*/
  { int i;

    for (i=0;i<termmax;k.terms[i++][0]='\0');
    k.count=0;
  }
  /*}}}  */
  if (kb_com_parse(&f,&n,&k)) return(TRUE);
  if (f==O_BREAK)
   /*{{{  handle abort*/
   {
     /*{{{  incorrect sequence*/
     if (k.lg[0]!=1)
      { error_po();
        fprintf(stderr,M_NOABORT);
        return(TRUE);
      }
     /*}}}  */
     /*{{{  double*/
     if (ab_set)
      { error_po();
        fprintf(stderr,M_DUPAB);
        return(TRUE);
      }
     /*}}}  */
     write_break_rc(abort_key=k.codes[0][0]);
     write_bind(n,k.prot,abort_kbd);
     ab_set=TRUE;
   }
   /*}}}  */
  else
   /*{{{  handle a normal definition*/
   { int i;

     /*{{{  if using mkbd, error*/
     if (m_kbd_used)
      { error_po();
        fprintf(stderr,M_KBD_KEY);
        return(TRUE);
      }
     /*}}}  */
     for (i=0;i<termmax;i++)
        if (   keytabs[0].top_level[i]
            && k.lg[0]>=0
            && add_keyseq(0,i,f,k.codes[0],k.lg[0]))
           return(TRUE);
     write_bind(n,k.prot,keytabs[0].bind_mark[0]);
     key_used=TRUE;
   }
   /*}}}  */

  return(FALSE);
}
/*}}}  */
/*{{{  term_name*/
public tokens term_name()
{ tokens x=get_token(FALSE);

  if (x==DEFAULT)
   { strcpy(tk_string,default_term);x=NAME; }
  return(x);
}
/*}}}  */
/*{{{  find_term*/
private bool find_term(int kbd_no,int *t_no,char *tk_string)
{ for (*t_no=0;;(*t_no)++)
   { char **name;

     if (*t_no==termmax)
      /*{{{  error*/
      { error_po();
        fprintf(stderr,M_T_MAX);
        return(TRUE);
      }
      /*}}}  */
     else if (!**(name= &(keytabs[kbd_no].terms[*t_no])))
      /*{{{  init a new terminal*/
      {
        /*{{{  copy the name to correct place*/
        if (!(*name=malloc(strlen(tk_string)+1)))
         { fprintf(stderr,M_NOMEMORY);
           return(TRUE);
         }
        strcpy(*name,tk_string);
        /*}}}  */
        /*{{{  copy the default keyboard*/
        if (!(keytabs[kbd_no].top_level[*t_no]
              =key_copy(keytabs[kbd_no].top_level[0]))) return(TRUE);
        /*}}}  */
        keytabs[kbd_no].bind_mark[*t_no]=bind_off++;
        break;
      }
      /*}}}  */
     else if (!strcmp(*name,tk_string))
        break;
   }
  return(FALSE);
}
/*}}}  */
/*{{{  t_keydef_code*/
public bool t_keydef_code(K_SEQ *sk)
{ int t_no[termmax];
  int t_c=0;
  int l;
  TOKEN f;
  char *n;

  keytabs[0].terms[0]=default_term;
  /*{{{  get correct terminals*/
  for (l=0;l<sk->count;l++)
     if (find_term(0,t_no+t_c++,strcpy(tk_string,sk->terms[l])))
        return(TRUE);
  /*}}}  */
  if (kb_com_parse(&f,&n,sk)) return(TRUE);
  /*{{{  abort not allowed here, so check it*/
  if (f==O_BREAK)
   { error_po();
     fprintf(stderr,M_KBD_KEY);
     return(TRUE);
   }
  /*}}}  */
  /*{{{  add key to all given terminals*/
  for (l=0;l<t_c;l++)
   { if (   sk->lg[l]>=0
         && add_keyseq(0,t_no[l],f,sk->codes[l],sk->lg[l]))
        return(TRUE);
     write_bind(n,sk->prot,keytabs[0].bind_mark[t_no[l]]);
   }
  /*}}}  */
  return(FALSE);
}
/*}}}  */
/*{{{  mkbd_code*/
public bool mkbd_code(void)
{ tokens token;
  int kbd_anz=0;
  TOKEN t[2];

  /*{{{  kbd-key crash*/
  if (key_used || m_kbd_used) {
    error_po();
    fprintf(stderr,M_KBD_KEY);
    return(TRUE);
  }
  /*}}}  */
  t[0]=M_SW_KBD;
  /*{{{  read all kbd-names*/
  while ((token=get_token(FALSE))!=END)
    switch (token)
     {
       /*{{{  NAME: a new mode*/
       case NAME:
        {
          /*{{{  to many kbds*/
          if (kbd_anz==KBD_COUNT) {
            error_po();
            fprintf(stderr,M_MANY_KBD);
            return(TRUE);
          }
          /*}}}  */
          keytabs[kbd_anz].def=FALSE;
          keytabs[kbd_anz].name=malloc(strlen(tk_string)+1);
          strcpy(keytabs[kbd_anz].name,tk_string);
          keytabs[kbd_anz].terms[0]=default_term;
          t[1]=kbd_anz++;
          creat_op(tk_string,TRUE,2,t,0,FALSE);
          break;
        }
       /*}}}  */
       /*{{{  error*/
       default:
        { error_po();
          fprintf(stderr,M_KBD_ERR);
          return(TRUE);
        }
       /*}}}  */
     }

  /*}}}  */
  m_kbd_used=kbd_anz;
  /*{{{  verbose?*/
  if (verbose)
    for (kbd_anz=0;kbd_anz<m_kbd_used;kbd_anz++)
        fprintf(stderr,F_KBDS,keytabs[kbd_anz].name,kbd_anz);
  /*}}}  */

  return(FALSE);
}
/*}}}  */
/*{{{  kbd_code*/
public bool kbd_code(void)
{ tokens t;
  int kbd_no=0;

  /*{{{  get correct kbd-no*/
  /*{{{  no multikbd used?*/
  if (!m_kbd_used) {
    error_po();
    fprintf(stderr,M_KBD_KEY);
    return(TRUE);
  }
  /*}}}  */
  /*{{{  check if name in sourcefile*/
  if (get_token(FALSE)!=OPERATION) {
    error_po();
    fprintf(stderr,M_KBDNAME);
    return(TRUE);
  }
  /*}}}  */
  while (   (kbd_no<KBD_COUNT)
         && strcmp(tk_operation->op_name,keytabs[kbd_no].name))
    kbd_no++;
  /*{{{  check if name in list of def kbd's*/
  /*{{{  not a def kbd -> error*/
  if (kbd_no==KBD_COUNT)
   { error_po();
     fprintf(stderr,M_KBDNAME);
     return(TRUE);
   }
  /*}}}  */
  /*{{{  if not kbd 0, maybe init it*/
  if (kbd_no && !keytabs[kbd_no].def && !init_keytab(kbd_no,0))
   { fprintf(stderr,M_NOMEMORY);exit(1); }
  /*}}}  */
  /*}}}  */
  /*}}}  */
  keytabs[kbd_no].def=TRUE;
  while ((t=get_token(FALSE))!=END)
   switch (t)
    {
      /*{{{  command*/
      case BEGIN:
         if ((t=get_token(FALSE))==KEYDEF)
          /*{{{  keydef*/
          { TOKEN f;
            char *n;
            int j;
            K_SEQ k;

            /*{{{  init keysequence-struct*/
            k.count=0;
            /*}}}  */
            if (kb_com_parse(&f,&n,&k)) return(TRUE);
            /*{{{  abort not allowed here, so check it*/
            if (f==O_BREAK)
             { error_po();
               fprintf(stderr,M_KBD_KEY);
               return(TRUE);
             }
            /*}}}  */
            for (j=0;j<termmax && keytabs[kbd_no].terms[j][0];j++)
               if (   k.lg[0]>=0
                   && add_keyseq(kbd_no,j,f,k.codes[0],k.lg[0]))
                  return(TRUE);
            write_bind(n,k.prot,keytabs[kbd_no].bind_mark[0]);
            break;
          }
          /*}}}  */
         else if (t==TERMINAL)
          /*{{{  terminal*/
          { int t_no[termmax];
            int t_c=0;
            int l;
            K_SEQ k;

            /*{{{  get correct terminals*/
            if ((t=term_name())==NAME)
             /*{{{  only one terminal*/
             { if (find_term(kbd_no,t_no,tk_string)) return(TRUE);
               t_c=1;
             }
             /*}}}  */
            else if (t==BEGIN)
             /*{{{  list of terminals*/
             { while ((t=term_name())!=END)
                { if (t!=NAME)
                   /*{{{  error return*/
                   { error_po();
                     fprintf(stderr,M_T_NAME);
                     return(TRUE);
                   }
                   /*}}}  */
                  if (find_term(kbd_no,t_no+t_c++,tk_string)) return(TRUE);
                }
             }
             /*}}}  */
            else
             /*{{{  error*/
             { error_po();
               fprintf(stderr,M_T_NAME);
               return(TRUE);
             }
             /*}}}  */
            /*{{{  set in keysequence-struct*/
            for (l=0;l<t_c;l++)
               strcpy(k.terms[l],keytabs[kbd_no].terms[t_no[l]]);
            k.count=t_c;
            /*}}}  */
            /*{{{  verbose*/
            if (verbose)
               for (l=0;l<t_c;l++)
                  fprintf(stderr,F_T_START,keytabs[kbd_no].terms[t_no[l]]);
            /*}}}  */
            /*}}}  */
            while ((t=get_token(FALSE))!=END)
             { if (t==BEGIN && get_token(FALSE)==KEYDEF)
                /*{{{  keydef*/
                { TOKEN f;
                  char *n;

                  if (kb_com_parse(&f,&n,&k)) return(TRUE);
                  /*{{{  abort not allowed here, so check it*/
                  if (f==O_BREAK)
                   { error_po();
                     fprintf(stderr,M_KBD_KEY);
                     return(TRUE);
                   }
                  /*}}}  */
                  /*{{{  add key to all given terminals*/
                  for (l=0;l<t_c;l++)
                   { if (   k.lg[l]>=0
                         && add_keyseq(kbd_no,t_no[l],f,k.codes[l],k.lg[l]))
                        return(TRUE);
                     write_bind(n,k.prot,keytabs[kbd_no].bind_mark[t_no[l]]);
                   }
                  /*}}}  */
                }
                /*}}}  */
               else
                /*{{{  error*/
                { error_po();
                  fprintf(stderr,M_KBD_BEGIN);
                  return(TRUE);
                }
                /*}}}  */
             }
            if (verbose) fprintf(stderr,M_T_END,t_c);
          }
          /*}}}  */
         break;
      /*}}}  */
      /*{{{  error*/
      default:
       { error_po();
         fprintf(stderr,M_KBD_BEGIN);
         return(TRUE);
       }
      /*}}}  */
    }

  return(FALSE);
}
/*}}}  */
/*{{{  mkbd_check*/
public bool mkbd_check(void)
{ if (m_kbd_used)
   { int i=0;

     while (i<m_kbd_used)
        if (!keytabs[i++].def)
         { fprintf(stderr,F_KBD_MISS,keytabs[i-1].name);return(TRUE); }
   }

  return(FALSE);
}
/*}}}  */

/*{{{  count_keynodes*/
private int count_keynodes(KEY *n)
{
  if (!n) return(0);
  return(count_keynodes(n->next)+count_keynodes(n->n_level)+1);
}
/*}}}  */

/*{{{  write_kbds*/
/*{{{  write_keynodes*/
/*{{{  write_single_keynode*/
private bool write_single_keynode(KEY *n)
{
  if (!n || dest_mac) return(FALSE);
  rc_put_w(n->code,rc);
  /*{{{  abort-mapping?*/
  if (ab_set && n->key==abort_key) {
    printf(M_ABORTUSED);
    return(TRUE);
  }
  /*}}}  */
  rc_put_c(n->key,rc);
  if (n->next) rc_put_w(1,rc); else rc_put_w(0,rc);
  if (n->n_level) rc_put_w(count_keynodes(n->next)+1,rc); else rc_put_w(0,rc);
  if (write_single_keynode(n->next)) return(TRUE);
  if (write_single_keynode(n->n_level)) return(TRUE);
  return(FALSE);
}
/*}}}  */

private bool write_keynodes(int no,int t,char *term)
{ int x;

  if (dest_mac) return(FALSE);

  /*{{{  add mouse for this terminal*/
  { mouse_supports *x=m_list;
    char *m_n= *term?term:m_name;

    /*{{{  x=current mouse, or default*/
    for (;;)
     { if (x->name && strcmp(x->name,m_n))
          x++;
       else if (!(x->name) && (m_n==term))
        { m_n=m_name;x=m_list; }
       else
          break;
     }
    /*}}}  */
    if (x->code_lg && add_keyseq(no,t,K_MOUSE,x->code,x->code_lg)) return(TRUE);
  }
  /*}}}  */
  rc_put_c(RC_DEFKEY,rc);
  rc_put_c(no,rc);
  do rc_put_c(*term,rc); while (*term++);
  rc_put_w(keytabs[no].bind_mark[0],rc);
  rc_put_w(keytabs[no].bind_mark[t],rc);
  rc_put_w(x=count_keynodes(keytabs[no].top_level[t]),rc);
  if (verbose) fprintf(stderr,F_NODES,no,t,keytabs[no].count[t],x);
  if (write_single_keynode(keytabs[no].top_level[t])) return(TRUE);
  return(FALSE);
}
/*}}}  */

public bool write_kbds(void)
{ int i;

  if (key_used) m_kbd_used=1;
  /*{{{  maybe additional lines in bindhelp*/
  if (sorting) {
    int i;

    if (!key_used)
       for (i=m_kbd_used;--i>=0;)
          if (keytabs[i].name)
             write_bind(M_MO,keytabs[i].name,keytabs[i].bind_mark[0]);
  }
  /*}}}  */
  i=0;
  while (i<m_kbd_used)
   { int t;

     for (t=1;t<termmax;t++)
        if (   keytabs[i].terms[t][0]
            && write_keynodes(i,t,keytabs[i].terms[t])
           )
           return(TRUE);
     if (write_keynodes(i,0,"")) return(TRUE);
     i++;
   }
  return(FALSE);

}
/*}}}  */
