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

#include <local/bool.h>

#define CODEHELP_C

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

/*{{{  variables*/
#define DV_MAX (var_count>>2)
public bool new_vars_enabled=FALSE;
private int dv_list[DV_MAX];
private bool dv_init=FALSE;
/*}}}  */

/*{{{  end_parse*/
public bool end_parse(char *er)
{
  if (get_token(FALSE)!=END) {
    error_po();
    fprintf(stderr,er);

    return(TRUE);
  }

  return(FALSE);
}
/*}}}  */
/*{{{  begin_parse*/
public bool begin_parse(void)
{
  if (get_token(FALSE)!=BEGIN) {
    error_po();
    fprintf(stderr,M_WANTBEGIN);

    return(TRUE);
  }

  return(FALSE);
}
/*}}}  */
/*{{{  parse a keycode sequence*/
public bool parse_keysequence(K_SEQ *k)
{
  char *c[termmax];
  char *p;
  int i;
  tokens token;

  if (begin_parse()) return(TRUE);
  /*{{{  init the pointers*/
  p=k->prot;
  for (i=0;i<termmax;c[i]=k->codes[i],i++);
  /*}}}  */
  /*{{{  read and handle 'til )*/
  do {
    token=get_token(TRUE);
    switch (token) {
      /*{{{  HEX-handling*/
      case HEX:
        for (i=0;i<termmax;i++)
          if ((!i || i<k->count) && c[i]) *c[i]++=tk_char;
        /*{{{  generate protocol of this binding*/
        strcpy(p,tk_string);
        while (*p) p++;
        /*}}}  */
        break;
      /*}}}  */
      /*{{{  CHAR-handling*/
      case CHAR:
        for (i=0;i<termmax;i++)
          if ((!i || i<k->count) && c[i]) *c[i]++=tk_char;
        /*{{{  generate protocol of this binding*/
        if (tk_char==127)      {*p++='C';*p++='-';*p++='?';}
        else if (tk_char==' ') {*p++='s';*p++='p';*p++='a';*p++='c';*p++='e';}
        else if (tk_char>' ')  *p++=tk_char;
        else                   {*p++='C';*p++='-';*p++=tk_char+'A'-1;}
        *p++=' ';
        /*}}}  */
        break;
      /*}}}  */
      /*{{{  META-handling*/
      case META:
        for (i=0;i<termmax;i++)
          if ((!i || i<k->count) && c[i]) *c[i]++=27;
        *p++='M';*p++='-';
        break;
      /*}}}  */
      /*{{{  ALIAS-handling*/
      case DOLLAR: {
         { ALIAS **a=func_key;

           while (*a && strcmp((*a)->key_name,tk_string)) a++;
           if (*a)
            /*{{{  alias found -> normal alias, copy to all sequences*/
            { char *s=(*a)->code;
              int  lg=(*a)->length;

              for (i=0;i<termmax;i++,lg=(*a)->length,s=(*a)->code)
                 if ((!i || i<k->count) && c[i])
                    while (lg--) *c[i]++ = *s++;
            }
            /*}}}  */
           else
            /*{{{  handle as terminal-alias*/
            { bool tf=FALSE;

              for (i=0;i<k->count;i++)
                 if (c[i])
                  /*{{{  handle a terminal-alias*/
                  { char an[name_lg];

                    tf=TRUE;
                    /*{{{  create correct name*/
                    strcpy(an,k->terms[i]);
                    strcat(an,"@");
                    strcat(an,tk_string);
                    /*}}}  */
                    a=func_key;
                    while (*a && strcmp((*a)->key_name,an)) a++;
                    if (*a)
                     /*{{{  alias found*/
                     { char *s=(*a)->code;
                       int  lg=(*a)->length;

                       while (lg--) *c[i]++ = *s++;
                     }
                     /*}}}  */
                    else
                     /*{{{  don't bind this term-alias!*/
                     { if (verbose)
                        { error_po();fprintf(stderr,F_MISALIAS,an); }
                       c[i]=0;
                     }
                     /*}}}  */
                  }
                  /*}}}  */
              if (!tf)
               /*{{{  error*/
               { error_po();
                 fprintf(stderr,F_NOALIAS,tk_string);
                 return(TRUE);
               }
               /*}}}  */
            }
            /*}}}  */
           strcpy(p,tk_string);
           while (*p) p++;
           *p++=' ';
         }
      }
      /*}}}  */
      case END: break;
      case ERROR: return(TRUE);
      /*{{{  default=error*/
      default: {
        error_po();
        fprintf(stderr,M_WANTEND);
        return(TRUE);
      }
      /*}}}  */
    }
  } while (token!=END);
  /*}}}  */
  *p='\0';
  for (i=0;i<termmax;i++) k->lg[i]=c[i]?c[i]-k->codes[i]:-1;
  return(FALSE);
}
/*}}}  */
/*{{{  name_to_var transform token NAME to token VARIABLE, if possible*/
public tokens name_to_var(tokens token, bool newvars)
{ static VARS x;

  if (token==NAME && newvars)
   { (tk_var= &x)->no=creat_var(tk_string);return(VARIABLE); }
  return(token);
}
/*}}}  */
/*{{{  put variable-adress*/
public TOKEN *put_var(TOKEN *buff)
{
  tokens token=name_to_var(get_token(FALSE),new_vars_enabled);

  switch (token) {
    case VARIABLE:
      *buff++ = (TOKEN) tk_var->no;
      break;
    case ERROR:
      return(0);
      break;
    default:
      error_po();
      fprintf(stderr,M_NOVAR);
      return(0);
  }
  return(buff);
}
/*}}}  */
/*{{{  dummy_var*/
public int dummy_var(int no)
{
  /*{{{  maybe init it*/
  if (!dv_init)
   { int i;
     for (i=0;i<DV_MAX;dv_list[i++]= -1);
     dv_init=TRUE;
   }
  /*}}}  */
  /*{{{  no dummy-vars available -> error*/
  if (no>=DV_MAX)
   { error_po();
     fprintf(stderr,M_NOVAS);
     exit(1);
   }
  /*}}}  */
  if (dv_list[no]==-1)
   { char s[10];

     sprintf(s,"(dummy%d)",no);
     return(dv_list[no]=creat_var(s));
   }
  else
     return(dv_list[no]);
}
/*}}}  */
/*{{{  isdummy*/
public bool isdummy(int x)
{ int i;

  if (dv_init) for (i=0;i<DV_MAX;i++) if (dv_list[i]==x) return(TRUE);
  return(FALSE);
}
/*}}}  */
/*{{{  create a jump-command*/
public TOKEN *generate_jmp(TOKEN j, TOKEN *ad, TOKEN *dest)
{
  switch (j)
   { case M_JMP:
     case M_CALL:
     case M_JMP_TRUE:
     case M_JMP_FALSE:
        break;
     default:
        fprintf(stderr,M_NOJUMP);
        return(0);
   }
  *ad++ = j;
  *ad= dest-(ad+1);
  return(ad+1);
}
/*}}}  */
/*{{{  parse_term*/
private bool t_unchanged;
private int target;
private int copy_target;
private TOKEN *start_term;
/*{{{  recursion for term-parsing*/
/*{{{  shift_code*/
private TOKEN *shift_code(TOKEN *buff)
{ TOKEN *x;

  if (copy_target>=0) return(buff);
  for (x=buff;x!=start_term;x--,*(x+4)= *x);
  *x++=M_PUSH_INT;
  *x++=(TOKEN)target;
  *x++=M_POP_INT;
  *x++=(TOKEN) (copy_target= -1-copy_target);
  return(buff+4);
}
/*}}}  */

public TOKEN *rpt(tokens token,TOKEN *buff,int dest,int dummy,bool *ret_used)
{ tokens op=name_to_var(token,new_vars_enabled);

  switch (op)
   { case VARIABLE:
      /*{{{  maybe PUSH new POP old*/
      { int x=tk_var->no;

        if (!t_unchanged && x==target)
         { buff=shift_code(buff);x=copy_target; }
        if (x!=dest)
         { *buff++=M_PUSH_INT;*buff++=x;
           *buff++=M_POP_INT;*buff++=dest;
           if (dest==target) t_unchanged=FALSE;
         }
        break;
      }
      /*}}}  */
     case PRE:
      /*{{{  do the action first*/
      { if (!(buff=parse_macro(buff,ret_used))) return(0);
        if (!(buff=rpt(get_token(FALSE),buff,dest,dummy,ret_used))) return(0);
        break;
      }
      /*}}}  */
     case COUNTER:
      /*{{{  convert bool to int*/
      { TOKEN *x;

        if (!(buff=parse_cond(buff,ret_used))) return(0);
        /*{{{  set-counter dummy-0 0*/
        *buff++ = M_SET_COUNTER;
        *buff++ = (TOKEN) dest;
        *buff++ = 0;
        /*}}}  */
        buff=generate_jmp(M_JMP_FALSE,x=buff,buff);
        /*{{{  set-counter dummy-0 1*/
        *buff++ = M_SET_COUNTER;
        *buff++ = (TOKEN) dest;
        *buff++ = 1;
        /*}}}  */
        generate_jmp(M_JMP_FALSE,x,buff);
        break;
      }
      /*}}}  */
     case OPCODE:
       /*{{{  opcode*/
       { *buff++ = M_SET_COUNTER;
         *buff++ = dest;
         *buff++ = tk_key->num;
         break;
       }
       /*}}}  */
     case STORE_CHAR:
     case READ_REPEAT:
     case SET_ENTER:
     case SET_S_ENTER:
     case STORE_X:
     case STORE_Y:
      /*{{{  get data from origami*/
        *buff++ = (token==SET_ENTER)   ? M_ENTERED :
                  (token==STORE_X)     ? M_POS_TO_COUNTER :
                  (token==STORE_Y)     ? M_STORE_LINE_NO :
                  (token==STORE_CHAR)  ? M_STORE_C :
                  (token==READ_REPEAT) ? M_READ_REPEAT :
                                         M_S_ENTERED;
        *buff++ = dest;
        break;
      /*}}}  */
     case NUMBER:
      /*{{{  M_SET_COUNTER dest value*/
      { *buff++=M_SET_COUNTER;
        *buff++=(TOKEN)dest;
        *buff++=(TOKEN)tk_val;
        if (dest==target) t_unchanged=FALSE;
        break;
      }
      /*}}}  */
     case ADD:
     case MINUS:
     case MULT:
     case DIV:
     case MOD:
      /*{{{  list of arguments*/
      { token=get_token(FALSE);
        if (token!=BEGIN)
         /*{{{  compiling the unary -*/
         { if (op!=MINUS)
            { error_po();fprintf(stderr,M_WANTBEGIN);return(0); }
           if (!(buff=rpt(token,buff,dest,dummy,ret_used))) return(0);
           *buff++=M_INV_COUNTER;
           *buff++=(TOKEN)dest;
           if (dest==target) t_unchanged=FALSE;
           break;
         }
         /*}}}  */
        if (!(buff=rpt(get_token(FALSE),buff,dest,dummy,ret_used))) return(0);
        token=name_to_var(get_token(FALSE),new_vars_enabled);
        do
         /*{{{  code one argument from list*/
           switch (token)
            { case COMMA: break;
              case VARIABLE:
               /*{{{  code: op var var*/
               { int x=tk_var->no;

                 if (!t_unchanged && x==target)
                  { buff=shift_code(buff);x=copy_target; }
                 /*{{{  code the operation*/
                 switch (op)
                  { /* MINUS uses the code for ADD too!!          */
                    case MINUS: { *buff++=M_INV_COUNTER;*buff++=(TOKEN)dest; }
                    case ADD:   { *buff++=M_SUM_COUNTER;break; }
                    case MULT:  { *buff++=M_MULT;break; }
                    case DIV:   { *buff++=M_DIV;break; }
                    case MOD:   { *buff++=M_MOD;break; }
                    default:    { fprintf(stderr,M_CRASH);return(0); }
                  }
                 /*{{{  code arguments*/
                 *buff++=(TOKEN)dest;
                 *buff++=x;
                 /*}}}  */
                 /*}}}  */
                 if (op==MINUS) { *buff++=M_INV_COUNTER;*buff++=(TOKEN)dest; }
                 if (dest==target) t_unchanged=FALSE;
                 break;
               }
               /*}}}  */
              default:
                 if (!(buff=rpt(token,buff,dummy_var(dummy),dummy+1,ret_used)))
                    return(0);
                 /*{{{  code the operation*/
                 switch (op)
                  { /* MINUS uses the code for ADD too!!          */
                    case MINUS:
                     { *buff++=M_INV_COUNTER;*buff++=(TOKEN)dummy_var(dummy); }
                    case ADD:
                     { *buff++=M_SUM_COUNTER;break; }
                    case MULT:
                     { *buff++=M_MULT;break; }
                    case DIV:
                     { *buff++=M_DIV;break; }
                    case MOD:
                     { *buff++=M_MOD;break; }
                    default:
                     { fprintf(stderr,M_CRASH);return(0); }
                  }
                 /*}}}  */
                 *buff++=dest;
                 *buff++=dummy_var(dummy);
                 if (dest==target) t_unchanged=FALSE;
            }
         /*}}}  */
        while ((token=name_to_var(get_token(FALSE),new_vars_enabled))!=END);
        break;
      }
      /*}}}  */
     case MACRO:
      /*{{{  code a one Token macro as it's value'*/
        if (strlen(tk_string)==1)
         /*{{{  code the integer value of a given character*/
         { *buff++=M_SET_COUNTER;
           *buff++=(TOKEN)dest;
           *buff++=(TOKEN)tk_string[0];
           if (dest==target) t_unchanged=FALSE;
           break;
         }
         /*}}}  */
        else
           goto error_code;
      /*}}}  */
     default:
      /*{{{  return the error*/
      error_code:
      { error_po();
        fprintf(stderr,M_NOVAR);
        return(0);
      }
      /*}}}  */
   }
  return(buff);
}
/*}}}  */

public TOKEN *parse_term(tokens token,TOKEN *buff,int dest,bool *ret_used)
{ int i;
  static int target_var= -1;

  for (i=DV_MAX;i-->0;) if (dv_list[i]==dest) break;
  t_unchanged=TRUE;
  start_term=buff;
  target=dest;
  if (target_var<0) target_var=creat_var("(target)");
  copy_target= -target_var-1;
  return(rpt(token,buff,dest,i+1,ret_used));
}
/*}}}  */
/*{{{  get_message*/
public TOKEN *get_message(TOKEN *buff)
{
  tokens read=get_token(FALSE);
  TOKEN *x=tk_macro;

  if (read!=MACRO) {
    /*{{{  complex prompt ( ... )*/
    /*{{{  no begin*/
    if (read!=BEGIN) {
      error_po();
      fprintf(stderr,M_NOMESSAGE);
      return(0);
    }
    /*}}}  */
    read=get_token(FALSE);
    while ((read==MACRO || read==COUNTER || read==NAME) && buff)
     { x=tk_macro;
       if (read==MACRO)
        /*{{{  copy the string*/
          while (*x) *buff++ = *x++;
        /*}}}  */
       else if (read==NAME)
        /*{{{  look for msg*/
        { int x=0;

          while (msg_list[x])
             if (strcmp(tk_string,msg_list[x])) x++; else break;
          if (msg_list[x]) *buff++ = -(x+1); else break;
        }
        /*}}}  */
       else
        /*{{{  put var*/
        { *buff++ = M_INT_STRING;
          buff = put_var(buff);
        }
        /*}}}  */
       read=get_token(FALSE);
     }
    /*{{{  errors?*/
    if (!buff) return(0);
    if (read!=END)
     { if (read!=ERROR) { error_po();fprintf(stderr,M_WANTEND); }
       return(0);
     }
    /*}}}  */
    /*}}}  */
  } else
     while (*x) *buff++ = *x++;
  return(buff);
}
/*}}}  */
