/*{{{  #includes*/
#ifdef CONFIG_H
#   include "config.h"
#endif

#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
#include <limits.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

#include <local/bool.h>

#define FIELDEDIT_C
#define I_DISPLAY_C
#define I_FOLDING_C
#define I_GETTK_C
#define I_GETMSG_C
#define I_KEYTAB_C
#define I_LOOP_C
#define I_MAIN_C
#define I_MESSAGES_C
#define I_ORIEDT_C
#define I_PROMPT_C
#define I_SCREEN_C
#define I_SET_C
#define I_SIGNALS_C
#define I_STRING_C
#define I_VIRTUAL_C

#include "origami.h"
#include "macros.h"
/*}}}  */

/*{{{  variables*/
public int x_offset=0;
private unsigned char deleted_ch = ' ';
private unsigned char curr_c=' ';
private bool x_off_changed=FALSE;
private int ind;
private int txt_pos;
private int txt_len;
/*}}}  */

/*{{{  change_edit_pos*/
#define change_edit_pos(by) \
  (first_edit_pos += (by), pre_edit_pos += (by))
/*}}}  */
/*{{{  field_scr2txt*/
#define field_scr2txt(o,x,s) \
  txt_pos=scr2txt(o,x,s);if (txt_pos>txt_len) txt_pos= -1
/*}}}  */
/*{{{  app_space*/
void app_space(bool expand)
{ if (txt_pos==-1 || txt_pos==txt_len)
   { int i;
     unsigned char *x;

     if (txt_pos==-1)
        txt_pos=scr2txt(enter_depth_spaces,scr_pos,current_dsp_line);
     i=txt_pos-txt_len;
     x=current_dsp_line+txt_len;
     if (expand && (LINELEN-enter_depth_spaces-txt_len)>8) i+=8;
     txt_len+=i;
     while (i--) *(x++)=' ';
     *x='\0';
   }
}
/*}}}  */
/*{{{  x_shift*/
public int x_shift(int po)
{
  int s=0;

  while (po>(LEN-1)) {s += SHIFT; po -= SHIFT;}
  x_off_changed=(x_offset!=s);
  return(x_offset=s);
}
/*}}}  */
/*{{{  refresh_field*/
private void refresh_field(int froms,bool clr2eoln)
{ int i;

  if (froms<1) froms = 1;
  x_shift(scr_pos);
  if (x_off_changed) froms=x_offset+1;
  x_off_changed=FALSE;
  i=scr2txt(enter_depth_spaces,froms,current_dsp_line);
  if (i>=txt_len)
   { if (clr2eoln) goclreol(froms-x_offset,cursor_level); }
  else
     if (clr2eoln)
        go_prt_clreol(froms-x_offset,
                      cursor_level,
                      enter_depth_spaces+froms-1,
                      LEN-froms+x_offset,
                      TRUE,
                      current_dsp_line+i);
     else
      { gotoxy(froms-x_offset,cursor_level);
        prt_bin_text(enter_depth_spaces+froms-1,
                     LEN-froms+x_offset,
                     TRUE,
                     current_dsp_line+i);
      }
}
/*}}}  */
/*{{{  restore_cursor*/
private void restore_cursor(void)
{
  title_op(UPDTITLE);
  x_shift(scr_pos);
  if (x_off_changed) refresh_field(x_offset+1,TRUE);
  gotoxy((int)(1 + (scr_pos-1-x_offset) % LEN), cursor_level);
}
/*}}}  */
/*{{{  restore_cursor_and_sleep*/
public void restore_cursor_and_sleep(int t)
{ if (!scr_off)
   { title_op(PRTTITLE);
     restore_cursor();
     oflush;
     if (t>=10) sleep(t/10);
     if (t%=10) usleep(t*(unsigned long)100000);
   }

  return;
}
/*}}}  */
/*{{{  field_move_cursor*/
private void field_move_cursor(int dist)
{ while (dist)
    if (dist>0)
     /*{{{  move right*/
     { if (txt_pos==-1 || txt_pos==txt_len)
        /*{{{  simple go right on screen*/
        { scr_pos+=dist;
          txt_pos= -1;
          dist=0;
        }
        /*}}}  */
       else
        /*{{{  maybe check if tab*/
        { if (current_dsp_line[txt_pos]=='\t' && hard_tab>0)
             scr_pos=((scr_pos+enter_depth_spaces+hard_tab-1)/hard_tab)*hard_tab+1;
          else if (current_dsp_line[txt_pos]>=' ')
             scr_pos++;
          else
             scr_pos+=inv_ctrl?1:2;
          txt_pos++;
          if (txt_pos>txt_len) txt_pos= -1;
          dist--;
        }
        /*}}}  */
     }
     /*}}}  */
    else
     /*{{{  move left*/
     { if (txt_pos==-1)
          txt_pos=scr2txt(enter_depth_spaces,scr_pos,current_dsp_line);
       txt_pos--;
       if (txt_pos<txt_len)
          /*{{{  check the given character too*/
          if (current_dsp_line[txt_pos]=='\t' && hard_tab>0)
             scr_pos=txt2scr(enter_depth_spaces,txt_pos,current_dsp_line);
          else if (!inv_ctrl && current_dsp_line[txt_pos]<' ')
             scr_pos-=2;
          else
             scr_pos--;
          /*}}}  */
       else
          /*{{{  char is ' '!*/
          scr_pos--;
          /*}}}  */
       dist++;
     }
     /*}}}  */
  /*{{{  maybe overflow-controll*/
  if (txt_pos>txt_len) txt_pos= -1;
  if (scr_pos<=0) { scr_pos=1;txt_pos=0; }
  if (scr_pos>LINELEN-enter_depth_spaces+1 )
   { scr_pos=LINELEN-enter_depth_spaces+1;
     txt_pos=field_scr2txt(enter_depth_spaces,scr_pos,current_dsp_line);
   }
  /*}}}  */
}
/*}}}  */
/*{{{  move_cursor*/
public void move_cursor(int dist)
{
  txt_len=strlen((char*)current_dsp_line);
  txt_pos=field_scr2txt(enter_depth_spaces,scr_pos,current_dsp_line);
  field_move_cursor(dist);
}
/*}}}  */
/*{{{  insert_*/
private void insert_(char ch)
{
  /*{{{  errorcheck && prepare*/
  /*{{{  edit-position*/
  if (   (scr_pos>pre_edit_pos || ch!=' ' || scr_pos<=ind || overwrite)
      && scr_pos<first_edit_pos)
   { vmessage(get_msg(M_ERR_PO));return; }
  if (scr_pos>LINELEN-enter_depth_spaces)
   { message(get_msg(M_LONG_LINE),TRUE);return; }
  /*}}}  */
  app_space(TRUE);
  /*}}}  */
  if (overwrite) {
   /*{{{  replace char*/
     current_dsp_line[txt_pos]=ch;
   /*}}}  */
  } else {
    /*{{{  insert char*/
    /*{{{  check long line*/
    if (   (txt_len>=LINELEN-enter_depth_spaces)
        || (dialect==F_C_TDS && txt_len>=TDS_LEN-enter_depth_spaces) )
     { trailing_spaces(current_dsp_line);
       txt_len=strlen((char*)current_dsp_line);
       if (txt_len>=LINELEN-enter_depth_spaces)
        { message(get_msg(M_LONG_LINE),TRUE);return; }
       if (dialect==F_C_TDS && txt_len>=TDS_LEN-enter_depth_spaces)
          message(get_msg(M_TDS_LONG_LINE),TRUE);
       if (txt_pos>txt_len)
        { txt_pos= -1;app_space(TRUE); }
     }
    /*}}}  */
    /*{{{  insert the character at correct position in line*/
    { unsigned char *s=current_dsp_line;
      unsigned char *d=line_buffer;
      int x;

      x=txt_pos;
      while (x-- > 0) *d++ = *s++;
      *d++ = ch;
      txt_len++;
      strcpy((char*)d,(char*)s);
    }
    /*}}}  */
    strcpy((char *)current_dsp_line, (char *)line_buffer);
    /*}}}  */
  }
  refresh_field(scr_pos,overwrite?TRUE:FALSE);
  if (scr_pos<=pre_edit_pos) change_edit_pos(1);
  field_move_cursor(1);
}
/*}}}  */
/*{{{  delete__*/
private bool delete__(void)
{
  /*{{{  before linestart*/
  if (scr_pos <= ind + 1) return(FALSE);
  /*}}}  */
  /*{{{  inv edit po*/
  if (scr_pos <= first_edit_pos && scr_pos > pre_edit_pos)
   { vmessage(get_msg(M_ERR_PO));return(TRUE); }
  /*}}}  */
  app_space(TRUE);
  field_move_cursor(-1);
  deleted_ch=current_dsp_line[txt_pos];
  /*{{{  do the delete*/
  { unsigned char *d,*s;

    d=current_dsp_line+txt_pos;
    s=d+1;
    while (*d++ = *s++);
    txt_len--;
  }
  /*}}}  */
  if (scr_pos <= pre_edit_pos) change_edit_pos(-1);
  refresh_field(scr_pos,TRUE);
  return(TRUE);
}
/*}}}  */
/*{{{  delete_under*/
private bool delete_under(void)
{
  /*{{{  do not del_under*/
  /*{{{  errorpo*/
  if (   scr_pos > LINELEN-enter_depth_spaces
      || scr_pos <= ind
      || (scr_pos<first_edit_pos && scr_pos>=pre_edit_pos))
   { vmessage(get_msg(M_ERR_PO));return(TRUE); }
  /*}}}  */
  /*{{{  behind eoln -> join lines!*/
  if (txt_pos==-1) return(FALSE);
  { unsigned char *x=current_dsp_line+txt_pos;

    while (*x==' ') x++;
    if (!*x) return(FALSE);
  }
  /*}}}  */
  /*}}}  */
  deleted_ch = curr_c;
  /*{{{  delete the character*/
  { unsigned char *s,*d;

    s=d=current_dsp_line+txt_pos;
    while (*d++ = *++s);
    txt_len--;
  }
  /*}}}  */
  if (scr_pos<pre_edit_pos) change_edit_pos(-1);
  refresh_field(scr_pos,TRUE);
  return(TRUE);
}
/*}}}  */
/*{{{  field_edit*/
public TOKEN field_edit(bool *keypad)
{
  /*{{{  declarations*/
  TOKEN Result, ch;
  bool field_key;
  /*}}}  */

  /*{{{  prepare editing field*/
  ind = current->UU.U1.indent;
  txt_len=strlen((char *)current_dsp_line);
  if (txt_len > LINELEN-enter_depth_spaces)
     exit_origami(r_ocl_err,get_msg(M_LONG_LINE));
  txt_pos=field_scr2txt(enter_depth_spaces,scr_pos,current_dsp_line);
  if (txt_pos>=0) scr_pos=txt2scr(enter_depth_spaces,txt_pos,current_dsp_line);
  /*}}}  */
  do {
    /*{{{  get the next operation*/
    field_key = FALSE;
    curr_c=(txt_pos==-1 || txt_pos==txt_len) ? ' ' : current_dsp_line[txt_pos];
    /*{{{  maybe user-signal*/
#    ifdef USER_SIGNAL
      user_signal_handle();
#    endif
    /*}}}  */
    /*{{{  maybe window changed!*/
#    if defined(SIGWINCH) || defined(MGR)
      if (win_changed) {ch=O_REFRESH;*keypad=TRUE;break;}
#    endif
    /*}}}  */
    do {
      restore_cursor();
      ch = edit_key(keypad);
    } while (!valid_key(keypad, &ch));
    field_key = valid_field_key(keypad, &ch);
    /*}}}  */
    if (field_key) {
      no_message();
      if (*keypad) {
        /*{{{  a fieldedit-command*/
        switch (ch) {   /*if and case*/
          /*{{{  M_BEGIN_OF_LINE*/
          case M_BEGIN_OF_LINE: {
            int po=0;
            unsigned char *t=current_dsp_line;

            while (*t==' ' || *t=='\t') {t++;po++;}
            macro_tag= *t ? (txt2scr(enter_depth_spaces,po,current_dsp_line)==scr_pos)
                          : scr_pos==ind+1;
            break;
          }
          /*}}}  */
          /*{{{  M_TOP_OF_FOLD*/
          case M_TOP_OF_FOLD: {
            macro_tag=(current==head->next);
            break;
          }
          /*}}}  */
          /*{{{  M_BOT_OF_FOLD*/
          case M_BOT_OF_FOLD: {
            macro_tag=(current==tail);
            break;
          }
          /*}}}  */
          /*{{{  M_TEST_CHAR_SET*/
          case M_TEST_CHAR_SET: {
            macro_tag=test_char_set(curr_c,get_arg());
            break;
          }
          /*}}}  */
          /*{{{  M_TEST_CHAR (LOW/HIGH/ ) _C*/
          case M_TEST_CC:
          case M_TEST_L_CC:
          case M_TEST_H_CC:
          case M_TEST_CHAR_LOW:
          case M_TEST_CHAR_HIGH:
          case M_TEST_CHAR: {
            unsigned char b;

            b= (ch==M_TEST_CC || ch==M_TEST_L_CC || ch==M_TEST_H_CC )
               ? macro_int[get_arg()] : get_arg();
            if (ch==M_TEST_CHAR || ch==M_TEST_CC)
              macro_tag=(curr_c==b);
            else if (ch==M_TEST_CHAR_LOW || ch==M_TEST_L_CC)
              macro_tag=(curr_c<b);
            else
              macro_tag=(curr_c>b);
            break;
          }
          /*}}}  */
          /*{{{  M_STORE_C*/
          case M_STORE_C: {
            macro_int[get_arg()]=curr_c;
            break;
          }
          /*}}}  */
          /*{{{  M_END_OF_LINE*/
          case M_END_OF_LINE: {
            if (txt_len>ind)
             { unsigned char *x;
               int i=txt_len;

               if (txt_pos==-1) { macro_tag=TRUE;break; }
               x=current_dsp_line+(txt_len-1);
               while (i-- && (*x==' ' || *x=='\t')) x--;
               macro_tag=(x<(current_dsp_line+txt_pos));
             }
            else
               macro_tag=TRUE;
            break;
          }
          /*}}}  */
          /*{{{  M_GO_COUNTER_X_POS*/
          case M_GO_COUNTER_X_POS: {
            int diff;

            diff=  scr2txt(enter_depth_spaces,macro_int[get_arg()],current_dsp_line)
                 - (txt_pos==-1?scr2txt(enter_depth_spaces,scr_pos,current_dsp_line):txt_pos);
            field_move_cursor(diff);
            break;
          }
          /*}}}  */
          /*{{{  M_BEHIND_COUNTER_X_POS*/
          case M_BEHIND_COUNTER_X_POS: {
            macro_tag = scr_pos>macro_int[get_arg()];
            break;
          }
          /*}}}  */
          /*{{{  M_POS_TO_COUNTER*/
          case M_POS_TO_COUNTER: {
            macro_int[get_arg()]= scr_pos;
            break;
          }
          /*}}}  */
          /*{{{  O_UNDEL_CHAR*/
          case O_UNDEL_CHAR: {
            insert_(deleted_ch);
            break;
          }
          /*}}}  */
          /*{{{  O_START_OF_LINE*/
          case O_START_OF_LINE:
           { if (txt_len>ind)
              { unsigned char *x=current_dsp_line;
                int o=0;

                while (*x==' ' || *x=='\t') { o++;x++; }
                if (!*x) o=ind;
                field_move_cursor(
                  o-(  (txt_pos==-1)
                     ? scr2txt(enter_depth_spaces,scr_pos,current_dsp_line)
                     : txt_pos));
              }
             else
                field_move_cursor(ind+1-scr_pos);
             break;
           }
          /*}}}  */
          /*{{{  O_END_OF_LINE*/
          case O_END_OF_LINE: {
            unsigned char *x=current_dsp_line+(txt_len-1);
            int i=txt_len;

            while (txt_len && (*x==' ' || *x=='\t')) { x--;i--; }
            if (txt_pos==-1)
               txt_pos=scr2txt(enter_depth_spaces,scr_pos,current_dsp_line);
            field_move_cursor((i>ind ? i : ind)-txt_pos);
            /*{{{  maybe correct the position*/
            if (   scr_pos<first_edit_pos
                && first_edit_pos<LINELEN-enter_depth_spaces)
               field_move_cursor(first_edit_pos-scr_pos);
            /*}}}  */
            break;
          }
          /*}}}  */
          /*{{{  O_DELTO_EOL*/
          case O_DELTO_EOL: {
            if (scr_pos >= first_edit_pos) {
              if (txt_pos != -1)
               { current_dsp_line[txt_pos]='\0';
                 txt_len=txt_pos;
               }
              refresh_field(scr_pos,TRUE);
            }
            break;
          }
          /*}}}  */
          /*{{{  O_LEFT*/
          case O_LEFT: {
            field_move_cursor(-1);
            break;
          }
          /*}}}  */
          /*{{{  O_RIGHT*/
          case O_RIGHT: {
            field_move_cursor(1);
            break;
          }
          /*}}}  */
          /*{{{  O_DEL_CHAR_R*/
          case O_DEL_CHAR_R: {
            field_key=delete_under();
            break;
          }
          /*}}}  */
          /*{{{  O_DELETE*/
          case O_DELETE: {
            field_key=delete__();
            break;
          }
          /*}}}  */
          /*{{{  O_RETURN*/
          case O_RETURN:   /* cr */ {
            /*{{{  folding -> error*/
            if (select_on)
             { message(get_msg(M_NOFOLDKEY), TRUE);break; }
            /*}}}  */
            /*{{{  do and break, if possible*/
            if (   scr_pos>pre_edit_pos
                || (current->foldline!=NOT_FOLD && scr_pos==pre_edit_pos))
             { int first_c;
               unsigned char *first_txt;

               /*{{{  fill exact with spaces*/
               /*{{{  cut trailing spaces*/
               trailing_spaces(current_dsp_line);
               txt_len=strlen((char*)current_dsp_line);
               if (txt_pos>=txt_len) txt_pos= -1;
               /*}}}  */
               app_space(FALSE);
               /*}}}  */
               /*{{{  first_c=first-non-space*/
               first_c=0;
               first_txt=current_dsp_line;
               while (first_c<txt_pos && (*first_txt==' ' || *first_txt=='\t'))
                { first_c++;first_txt++; }
               /*}}}  */
               if (   (current != tail || scr_pos == 1)
                   && (   txt_pos==txt_len
                       || (   (!entered(current) || current!= head->next)
                           && (current->foldline==NOT_FOLD || txt_pos==first_c))))
                /*{{{  do the return*/
                { split_line = txt_pos>first_c;
                  proc_new(&part_line);
                  if (current->foldline!=END_FOLD)
                     part_line->UU.U1.indent = ind;
                  else
                     part_line->UU.U1.indent =
                         current->other_end->UU.U1.indent
                        -current->other_end->UU.U1.fold_indent;
                  /*{{{  maybe splitt the line*/
                  if (split_line)
                   { set_data(part_line,current_dsp_line+txt_pos);
                     current_dsp_line[txt_pos]='\0';
                     txt_len=txt_pos;
                     refresh_field(scr_pos,TRUE);
                     field_move_cursor((first_c<=ind?ind:first_c)-txt_pos);
                   }
                  /*}}}  */
                  line_buffer[0]='\0';
                  spaces(line_buffer,scr_pos - ind - 1);
                  strcat((char *)line_buffer,(char *)get_data(part_line));
                  set_data(part_line,line_buffer);
                  field_key = FALSE;
                  break;
                }
                /*}}}  */
             }
            /*}}}  */
            vmessage(get_msg(M_ERR_PO));
            break;
          }
          /*}}}  */
          /*{{{  O_TOGGLE_CASE*/
          case O_TOGGLE_CASE: {
            if (scr_pos<first_edit_pos && scr_pos>pre_edit_pos)
             { vmessage(get_msg(M_ERR_PO));break; }
            app_space(TRUE);
            if (isalpha(curr_c))
              current_dsp_line[txt_pos]=islower(curr_c)?toupper(curr_c):tolower(curr_c);
            refresh_field(scr_pos,FALSE);
            break;
          }
          /*}}}  */
          /*{{{  O_TRANSPOSE_CHARACTERS*/
          case O_TRANSPOSE_CHARACTERS:
          { unsigned char *s;

            /*{{{  check position*/
            if (scr_pos<=first_edit_pos || scr_pos>LINELEN-enter_depth_spaces)
             { vmessage(get_msg(M_ERR_PO));break; }
            /*}}}  */
            /*{{{  if not behind end-of-line, do the change*/
            app_space(TRUE);
            s=current_dsp_line+txt_pos;
            *s     = *(s-1);
            *(s-1) = curr_c;
            refresh_field(txt2scr(enter_depth_spaces,txt_pos-1,current_dsp_line),TRUE);
            /*}}}  */
            field_move_cursor(1);
            break;
          }
          /*}}}  */
          /*{{{  O_A_VIEW*/
          case O_A_VIEW: {
            user_view=read_only=TRUE;
            title_op(SET_VIEW);
            break;
          }
          /*}}}  */
          /*{{{  O_D_VIEW*/
          case O_D_VIEW: {
            user_view=read_only=FALSE;
            title_op(RESET_VIEW);
            break;
          }
          /*}}}  */
          /*{{{  O_A_OVER*/
          case O_A_OVER: {
            overwrite=TRUE;
            title_op(SET_OVER);
            break;
          }
          /*}}}  */
          /*{{{  O_D_OVER*/
          case O_D_OVER: {
            overwrite=FALSE;
            title_op(RESET_OVER);
            break;
          }
          /*}}}  */
          /*{{{  default*/
          default: break;
          /*}}}  */
        }
        /*}}}  */
      } else
        insert_(ch);
      if (dirty)
       { ch=O_FLUSH;break; }
    }
  } while (field_key);
  Result = ch;
  trailing_spaces(current_dsp_line);
  return Result;
}
/*}}}  */
