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

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

#include <local/bool.h>

#define FINDS_C
#define I_DISPLAY_C
#define I_FIELDEDIT_C
#define I_FOLDING_C
#define I_GETTK_C
#define I_GETMSG_C
#define I_KEYBOARD_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_STRING_C

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

/*{{{  variables*/
public unsigned char item_to_look_for[LINELEN+1];
private unsigned char replace_item[LINELEN+1];
/*}}}  */

/*{{{  line_no*/
public int line_no(element *q)
{
  int i;
  element *p;

  i = 0;
  p = real_head;
  while (p != q) {
    if (p->foldline==START_FILED) i += 3; else i++;
    move_on(&p);
  }
  return i;
}
/*}}}  */
/*{{{  close_and_open_folds*/
public void close_and_open_folds(element *qq)
{
  element *q, *old_q;

  q = qq;
  while (q != head) {
    old_q = q;
    q = q->prec;
    if (q->foldline == START_FOLD && q->fold == old_q) pre_open_fold(q);
    if (q->foldline == END_FOLD) {
      if (q->other_end->foldline == START_OPEN_FOLD) {
        q = q->other_end;
        close_fold_at(q);
      }
    }
  }
  q = qq;
  while (q != tail) {
    q = q->next;
    if (q->foldline == START_OPEN_FOLD) close_fold_at(q);
  }
}
/*}}}  */
/*{{{  find_item*/
public bool find_item(bool backup,bool display)
{
  int position;
  element *p, *q;
  bool found;
  unsigned char *f;

  q=p=current;
  found = FALSE;
  copyin(line_buffer, p, FALSE);
  position=scr2txt(enter_depth_spaces,scr_pos,line_buffer);
  if (backup) {
    /*{{{  search reverse*/
    while (p != head->next && !found) {
      copyin(line_buffer, p, FALSE);
      if (p == current)
         line_buffer[position+strlen((char*)item_to_look_for)-1]='\0';
      if ((f=(unsigned char*)strrstr((char*)line_buffer,(char*)item_to_look_for))) {
        found = TRUE;
        position=f-line_buffer;
      } else if (p->prec->foldline==START_FOLD && p->prec->next==p)
        p=p->prec->other_end;
      else
        p=p->prec;
    }
    /*}}}  */
  } else {
    /*{{{  search forward*/
    while (p != tail && !found) {
      copyin(line_buffer, p, FALSE);
      if (p == current) {
        if (position < strlen((char*)line_buffer))
          f=(unsigned char*)strstr((char *)line_buffer+position,
                                   (char *)item_to_look_for);
        else
          f=0;
      } else
         f=(unsigned char*)strstr((char *)line_buffer,
                                  (char *)item_to_look_for);
      if (f==0)
        move_on(&p);
      else {
        found = TRUE;
        position=f-line_buffer;
      }
    }
    /*}}}  */
  }
  if (!found) return FALSE;
  if (p != q)
   /*{{{  open folds*/
   { int off=strlen((char*)line_buffer);

     current=p;
     close_and_open_folds(p);
     copyin(line_buffer, p, FALSE);
     position+=strlen((char*)line_buffer)-off;
     if (display) restore_element(SCREEN_LEN/2);
   }
   /*}}}  */
  scr_pos=txt2scr(enter_depth_spaces,position,line_buffer);
  return TRUE;
}
/*}}}  */
/*{{{  get_search*/
public void get_search(void)
{
  unsigned char s[LINELEN+1];

  readprompt(s,get_msg(M_SEARCH),LINELEN);
  if (*s!='\0')
    strcpy((char *)item_to_look_for, (char *)s);
  else if (aborted)
     *item_to_look_for='\0';
  else {
    unsigned char x[LINELEN+1];

    strcpy((char *)x,(char *)get_msg(M_SEARCH));
    strcat((char *)x,question);
    strcat((char *)x,(char *)item_to_look_for);
    message(x,FALSE);
  }
}
/*}}}  */
/*{{{  pre_find_element*/
public element *pre_find_element(int new_line_no)
{
  int current_line_no;
  element *p;

  current_line_no = 0;
  p = head->next;
  while (current_line_no < new_line_no && p != tail) {
    if (p->foldline==START_FILED) current_line_no += 3; else current_line_no++;
    move_on(&p);
  }
  return p;
}
/*}}}  */
/*{{{  find_element*/
public void find_element(int new_line_no, int close_line)
{
  element *p;

  while (tail != real_tail) pre_exit_fold();
  p = pre_find_element(new_line_no);
  close_and_open_folds(p);
  current = p;
  restore_element(close_line);
}
/*}}}  */
/*{{{  goto_line*/
public void goto_line(void)
{
  int no=0;
  unsigned char command_str[LINELEN+1];

  readprompt(command_str,get_msg(M_GOTO), 15);
  if (command_str[0] != '\0')
    if ((no=atoi((char *)command_str))>0) {
      scr_pos = 1;
      find_element(no, SCREEN_LEN/2);
    }
}
/*}}}  */
/*{{{  find_field*/
private void find_field(void)
{
  if (scr_pos<1) return;
  copyin(line_buffer, current, FALSE);
  x_shift(scr_pos);
  go_prt_clreol(1,
                cursor_level,
                enter_depth_spaces,
                LEN-1,
                TRUE,
                line_buffer+scr2txt(enter_depth_spaces,x_offset+1,line_buffer));
  title_op(PRTTITLE);
  gotoxy(scr_pos-x_offset,cursor_level);
}
/*}}}  */
/*{{{  query_replace*/
public void query_replace(void)
{
  unsigned char ch='\0';
  bool display=TRUE;
  bool ok=TRUE;

  get_search();
  if ((*item_to_look_for!='\0') & !aborted) {
    readprompt(replace_item,get_msg(M_REPLACE), LINELEN);
    if (aborted) return;
    while (!aborted && find_item(FALSE,display)) {
      copyin(current_dsp_line, current, FALSE);
      /*{{{  maybe menu*/
      if (ch!=DO_REST) {
        message(get_msg(M_Q_R_PROMPT),FALSE);
        find_field();
        ch=hide_key();
        ch=toupper(ch);
        if (ch==' ') ch=YES;
        if (ch==DO_REST) { enable_abort();display=FALSE; }
      }
      /*}}}  */
      /*{{{  maybe replace*/
      if ((ch==YES || ch==DO_AB || ch==DO_REST) && !aborted) {
        /*{{{  do the replace*/
        proc_to_edit_pos();
        if (scr_pos<first_edit_pos)
         { ok=FALSE;
           message(get_msg(M_MARK_PATTERN),TRUE);
           break;
         }
        if (proc_replace(item_to_look_for,replace_item,current_dsp_line,
                         scr2txt(enter_depth_spaces,scr_pos,current_dsp_line),
                         LINELEN-enter_depth_spaces)<0
           )
         { ok=FALSE;
           message(get_msg(M_LONG_LINE),TRUE);
           break;
        }
        proc_from_edit_pos();
        move_cursor(strlen((char *)replace_item));
        copyout(current_dsp_line, current);
        if (display) write_dsp_line(current, cursor_level);
        /*}}}  */
      } else
        move_cursor(1);
      /*}}}  */
      if (ch==AB || ch==DO_AB) break;
    }
    if (!display)
     { restore_element(SCREEN_LEN/2);x_offset=0; }
    else if (x_offset)
     { write_dsp_line(current, cursor_level);x_offset=0; }
    disable_abort();
    if (ok) no_message();
  }
}
/*}}}  */
/*{{{  replace*/
public void replace(void)
{
  int counts=0;
  int x=0;

  get_search();
  if (*item_to_look_for!='\0' && !aborted)
   {
     /*{{{  get replace-pattern*/
     readprompt(replace_item,get_msg(M_REPLACE), LINELEN);
     if (aborted) return;
     /*}}}  */
     enable_abort();
     /*{{{  do all the replaces*/
     while (find_item(FALSE,FALSE) && !aborted)
      { scan_abort_key();
        copyin(current_dsp_line, current, FALSE);
        proc_to_edit_pos();
        /*{{{  check replace-position*/
        if (scr_pos<first_edit_pos)
         { message(get_msg(M_MARK_PATTERN),TRUE);
           x= -1;
           break;
         }
        /*}}}  */
        x=proc_replace(item_to_look_for, replace_item,current_dsp_line,
                       scr2txt(enter_depth_spaces,scr_pos,current_dsp_line),
                       LINELEN-enter_depth_spaces);
        /*{{{  add number of replaces*/
        if (x>=0)
          counts += x;
        else
         { message(get_msg(M_LONG_LINE),TRUE);
           break;
         }
        /*}}}  */
        proc_from_edit_pos();
        move_cursor(strlen((char *)replace_item));
        copyout(current_dsp_line, current);
      }
     /*}}}  */
     disable_abort();
     restore_element(SCREEN_LEN/2);
     x_offset=0;
     if (x>=0) message(get_msg(F_SUBSTITUTIONS,counts),TRUE);
   }
}
/*}}}  */
/*{{{  its_search*/
public void its_search(bool reverse)
{
  /*{{{  variables*/
  /*{{{  prompt and pointer to last char of prompt*/
  unsigned char prompt[LINELEN+21];
  unsigned char *i_ptr;
  /*}}}  */
  /*{{{  length of search-pattern*/
  int  i_lg=strlen((char *)item_to_look_for);
  /*}}}  */
  /*{{{  pointer to last char of search-pattern*/
  unsigned char *its_item=item_to_look_for+i_lg;
  /*}}}  */
  /*{{{  stack of positions*/
  int  x[LINELEN+1],
       y[LINELEN+1];
  /*}}}  */
  bool found=TRUE;
  bool first=TRUE;
  int k=0;
  TOKEN c=O_NOP;
  /*}}}  */

  /*{{{  init search, prompt*/
  /*{{{  init position-stack*/
  x[0]=scr_pos;
  y[0]=line_no(current)-1;
  while (++k <= i_lg) {x[k]=x[0];y[k]=y[0]; }
  /*}}}  */
  /*{{{  init prompt*/
  strcpy((char *)prompt,"I-Search: ");
  i_ptr=prompt+strlen((char *)prompt);
  /*}}}  */
  /*}}}  */
  find_field();
  /*{{{  handle commands*/
  while (c!=O_BREAK && !aborted && c!=O_RETURN) {
    /*{{{  store position in stack*/
    x[i_lg] = scr_pos;
    y[i_lg] = line_no(current)-1;
    /*}}}  */
    /*{{{  show prompt and position*/
    if (reverse) prompt[0]='R'; else prompt[0]='I';
    message(prompt,FALSE);
    if (!found) pline(get_msg(M_FI_FAILED));
    find_field();
    /*}}}  */
    /*{{{  handle command*/
    do
      c=hide_key();
    while ((c==O_BREAK) && !aborted);
    switch (c) {
      /*{{{  O_DELETE O_DEL_CHAR_R*/
      case O_DEL_CHAR_R:
      case O_DELETE: {
        if (i_lg) {
          /*{{{  first -> use last string!*/
          if (first)
           { i_ptr+=i_lg;
             strcat((char *)prompt,(char *)item_to_look_for);
           }
          /*}}}  */
          /*{{{  delete char from string (prompt and search-string!)*/
          /*{{{  delete char from strings*/
          *--i_ptr = *--its_item ='\0';
          /*}}}  */
          /*{{{  move to old position*/
          scr_pos=x[--i_lg];
          if (y[i_lg+1]!=y[i_lg]) find_element(y[i_lg],SCREEN_LEN/2);
          /*}}}  */
          /*}}}  */
          found=TRUE;
        }
        break;
      }
      /*}}}  */
      /*{{{  O_RETURN*/
      case O_RETURN:
        break;
      /*}}}  */
      /*{{{  O_FIND         O_DOWN O_RIGHT*/
      case O_DOWN:
      case O_RIGHT:
      case O_ITS_SEARCH:
      case O_FIND: {
        reverse=FALSE;
        /*{{{  first: search-string stays*/
        if (first) {
          i_ptr += i_lg;
          strcat((char *)prompt,(char *)item_to_look_for);
        }
        /*}}}  */
        /*{{{  if not empty search-string, look for next*/
        if (i_lg) {
          move_cursor(1);
          if (!(found=find_item(reverse,TRUE)))
             move_cursor(-1);
        }
        /*}}}  */
        break;
      }
      /*}}}  */
      /*{{{  O_FIND_REVERSE O_UP   O_LEFT*/
      case O_UP:
      case O_ITS_REVERSE:
      case O_LEFT:
      case O_FIND_REVERSE: {
        reverse=TRUE;
        /*{{{  first: search-string stays*/
        if (first) {
          i_ptr+=i_lg;
          strcat((char *)prompt,(char *)item_to_look_for);
        }
        /*}}}  */
        /*{{{  if not empty search-string, look for previous*/
        if (i_lg) found=find_item(reverse,TRUE);
        /*}}}  */
        break;
      }
      /*}}}  */
      /*{{{  ascii*/
      default: {
        /*{{{  first: search-string= ascii*/
        if (first) {
          i_lg=0;
          *(its_item=item_to_look_for)='\0';
        }
        /*}}}  */
        if (c>=O_NOP) break;
        /*{{{  append char to prompt*/
        *i_ptr++ = c;
        *i_ptr='\0';
        /*}}}  */
        /*{{{  append char to search-string*/
        *its_item++ = c;
        *its_item='\0';
        /*}}}  */
        ++i_lg;
        if (reverse) move_cursor(1);
        found=find_item(reverse,TRUE);
        if (reverse && !found) move_cursor(-1);
        break;
      }
      /*}}}  */
    }
    /*}}}  */
    first=FALSE;
  }
  /*}}}  */
  /*{{{  <> O_RETURN -> jump to calling position*/
  if (c!=O_RETURN) {
    find_element(y[0],SCREEN_LEN/2);
    scr_pos=x[0];
    x_offset=0;
  }
  /*}}}  */
  no_message();
}
/*}}}  */
