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

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

#include <local/bool.h>

#define FOLDING_C
#define I_DISPLAY_C
#define I_FINDS_C
#define I_FOLDFILING_C
#define I_FOLDHELP_C
#define I_GETMSG_C
#define I_GETTK_C
#define I_KEYBOARD_C
#define I_LOOP_C
#define I_MAIN_C
#define I_MISC_C
#define I_MESSAGES_C
#define I_ORIEDT_C
#define I_PROMPT_C
#define I_READFOLDS_C
#define I_STRING_C
#define I_SCREEN_C
#define I_VIRTUAL_C
#define I_WRITEF_C

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

/*{{{  #define pre_pre_open_fold*/
#define pre_pre_open_fold(q)  {check_fold(q); \
          Line_after_fold->prec = End_of_fold; \
          Start_of_fold->next = Start_of_fold->fold; \
          Start_of_fold->foldline = START_OPEN_FOLD;}
/*}}}  */
/*{{{  variables*/
private element *select_ptr;
private int makefold_indent;
public bool select_on = FALSE;
public element *pick_head,
               *pick_tail,
               *current,
               *head,
               *tail,
               *real_head,
               *real_tail,
               *End_of_fold,
               *Start_of_fold,
               *Line_after_fold;
public unsigned char current_dsp_line[LINELEN+1];
public int enter_depth = 0,enter_depth_spaces = 0;
/*}}}  */

/*{{{  normal_att*/
public bool normal_att(element *p,bool text)
{
  return((p->UU.U1.fold_type==0) && ((p->UU.U1.fold_contents==1)||text));
}
/*}}}  */
/*{{{  insert_link_before*/
public void insert_link_before(element *ptr_to_new)
{
  element *ptr_to_prec;

  ptr_to_new->UU.U1.indent = insert_indent_of(current);
  ptr_to_prec = current->prec;
  if (screen_start == current) screen_start = ptr_to_new;
  join_links(ptr_to_prec, ptr_to_new);
  join_links(ptr_to_new, current);
  current = ptr_to_new;
}
/*}}}  */
/*{{{  copy_a_line*/
public bool copy_a_line(element **to_ptr)
{
  element *p, *q, *last, *fold_ptr;

  /*{{{  check if filed fold is inside*/
  { bool filed = FALSE;

    p = current;
    while (p != current->next) {
      if (p->foldline == START_FILED) { filed=TRUE;break; }
      move_on(&p);
    }
    if (filed) {
      message(get_msg(M_NO_FF_COPY),TRUE);
      return (FALSE);
    }
  }
  /*}}}  */
  /*{{{  copy the line*/
  /*{{{  init the links*/
  p = current;
  last = 0;
  fold_ptr = 0;
  /*}}}  */
  while (p != current->next) {
    /*{{{  copy the node to a new one*/
    proc_new(&q);
    if (p == current) *to_ptr = q;
    *q = *p;
    /*}}}  */
    /*{{{  copy data to new node!*/
    new_data(q);                  /* *q=*p destroyed the initialised data! */
    {unsigned char *s=get_data(p); if (*s) set_data(q,s);}
    /*}}}  */
    /*{{{  handle the links*/
    q->prec = last;
    if (last) {
      last->next = q;
      if (last->foldline == START_FOLD) last->fold = q;
      if (last->foldline == END_FOLD) {
        q->prec = last->other_end;
        q->prec->next = q;
      }
    }
    if (q->foldline == START_FOLD) {
      q->other_end = fold_ptr;
      fold_ptr = q;
    }
    move_on(&p);
    if (q->foldline == END_FOLD) {
      q->other_end = fold_ptr;
      fold_ptr = fold_ptr->other_end;
      q->other_end->other_end = q;
    }
    last = q;
    /*}}}  */
  }
  /*}}}  */
  return (TRUE);
}
/*}}}  */
/*{{{  change_foldtype*/
public void change_foldtype(int type, int contents)
{ if (current->foldline == START_FILED || current->foldline == START_FOLD)
   { current->UU.U1.fold_type=type;current->UU.U1.fold_contents=contents; }
  else
     message(get_msg(M_FAILED),TRUE);
}
/*}}}  */
/*{{{  start_make_fold*/
public void start_make_fold(void)
{
  element *p;

  if (entered(current)) return;
  proc_new(&p);
  p->fold_close_line = cursor_level;
  makefold_indent = scr_pos - 1;
  /*{{{  set the string for p*/
  *line_buffer='\0';
  if (current->foldline==START_OPEN_FOLD)
     spaces(line_buffer,
            makefold_indent
             -current->UU.U1.indent
             +current->UU.U1.fold_indent);
  else
   { if (makefold_indent<current->UU.U1.indent)
        makefold_indent=current->UU.U1.indent;
     spaces(line_buffer,makefold_indent-current->UU.U1.indent);
   }
  strcat((char *)line_buffer,(char *)fold_open_str);
  set_data(p,line_buffer);
  /*}}}  */
  undelete_before(p);
  select_on = TRUE;
  select_ptr = current;
  move_down();
}
/*}}}  */
/*{{{  make_fold*/
public void make_fold(void)
{
  /*{{{  variables*/
  element *p;
  int open_count, ind, i;
  bool indented_enough;
  int on_screen_line;
  /*}}}  */

  /*{{{  check indentation and open/close-folds*/
  p = select_ptr->next;
  indented_enough = TRUE;
  open_count = 0;
  while (p != current && open_count >= 0 && indented_enough)
   { if (p==tail) { message(get_msg(M_NO_BACK_FOLDING),TRUE);return; }
     /*{{{  check indentation >= makefold_indent*/
     /* check not across open fold boundaries,ie no of open folds=no of end folds */
     if (open_count == 0) {
       if (p->foldline == START_OPEN_FOLD) indented_enough = (makefold_indent <= p->UU.U1.indent);
       if (filed_or_fold(p)) indented_enough = (makefold_indent <= total_indent(p));
       if (p->foldline == NOT_FOLD)
        /*{{{  check spaces in front*/
        { unsigned char *s=get_data(p);

          for (i=makefold_indent-current->UU.U1.indent;i--;)
           { if (!*s)
                break;
             else if (*s!=' ')
                { indented_enough=FALSE;break; }
             else
                s++;
           }
        }
        /*}}}  */
     }
     /*}}}  */
     if (p->foldline == START_OPEN_FOLD) open_count++;
     if (p->foldline == END_FOLD) open_count--;
     p = p->next;
   }
  if (!(indented_enough && open_count == 0))
   { message(get_msg(M_FOLDERR),TRUE);return; }
  /*}}}  */
  /*{{{  change indentation data of folded lines*/
  p = select_ptr->next;
  ind = makefold_indent - select_ptr->UU.U1.indent;
  while (p != current) {
    if (p->foldline == START_OPEN_FOLD) close_fold_at(p);
    if (filed_or_fold(p)) p->UU.U1.fold_indent -= ind;
    if (p->foldline == NOT_FOLD)
    set_data(p,strsub(get_data(p),ind+1));
    p->UU.U1.indent = 0;
    p = p->next;
  }
  /*}}}  */
  /*{{{  new node for close, join links and set data*/
  proc_new(&p);
  insert_link_before(p);
  End_of_fold = current;
  Line_after_fold = End_of_fold->next;
  Start_of_fold = select_ptr;
  Start_of_fold->fold = Start_of_fold->next;
  Start_of_fold->other_end = End_of_fold;
  End_of_fold->other_end = Start_of_fold;
  Line_after_fold->prec = Start_of_fold;
  Start_of_fold->next = Line_after_fold;
  Start_of_fold->foldline = START_FOLD;
  Start_of_fold->UU.U1.indent = makefold_indent - ind;
  Start_of_fold->UU.U1.fold_indent = ind;
  End_of_fold->foldline = END_FOLD;
  End_of_fold->UU.U1.indent = 0;
  current = Start_of_fold;
  select_on = FALSE;
  set_data(End_of_fold,empty_text);
  set_data(Start_of_fold,empty_text);
  /*}}}  */
  /*{{{  update the screen*/
  scr_pos = total_indent(current) + 6;
  if (on_screen(current,&on_screen_line))
     current->fold_close_line=on_screen_line;
  restore_element(current->fold_close_line);
  /* if on_screen and enough lines to fill screen */
  /*   then restore_from(on_screen_line);         */
  /*}}}  */
}
/*}}}  */
/*{{{  remove_line*/
public void remove_line(element **p)
{
  if (!(current->foldline == NOT_FOLD || filed_or_fold(current))) return;
  if (current == tail) return;
  if (*p) proc_dispose(*p);
  pre_remove_line(p);
  if (cursor_level < SCREEN_LEN)
     up_a_bit(cursor_level);
  else
     goclreol(1,SCREEN_LEN);
  if (screen_end != tail) {
    screen_end = screen_end->next;
    write_dsp_line(screen_end, SCREEN_LEN);
  } else {
    screen_end_level--;
    whole_screen_down();
  }
  if (dirty) restore(cursor_level);
}
/*}}}  */
/*{{{  pre_enter_fold*/
private void pre_enter_fold(element *cur)
{
  check_fold(cur);
  Line_after_fold->prec = End_of_fold;
  enter_depth++;
  scr_pos-=total_indent(Start_of_fold);
  enter_depth_spaces += total_indent(Start_of_fold);
  Start_of_fold->next = Start_of_fold->fold;
  Start_of_fold->foldline = START_ENTER_FOLD;
  Start_of_fold->fold = head;
  head = Start_of_fold->prec;
  End_of_fold->fold = tail;
  tail = End_of_fold;
  /*save indent*/
  End_of_fold->UU.U1.fold_indent = Start_of_fold->UU.U1.indent;
  Start_of_fold->UU.U1.indent = 0;
}
/*}}}  */
/*{{{  erase_current_link*/
private void erase_current_link(void)
{
  element *prec_ptr, *p;

  if (current == tail) return;
  p = current;
  prec_ptr = p->prec;
  current = p->next;
  proc_dispose(p);
  join_links(prec_ptr, current);
}
/*}}}  */
/*{{{  delete_list*/
public void delete_list(element *from_ptr, element *to_ptr)
{
  element *p, *q;

  q = from_ptr;   /*head^.next^.next*/
  while (q != to_ptr) {   /*tail*/
    p = q;
    q = q->next;
    proc_dispose(p);
  }
  proc_dispose(to_ptr);
}
/*}}}  */
/*{{{  pre_exit_fold*/
public void pre_exit_fold(void)
{
  element *p;

  enter_depth--;
  Start_of_fold = head->next;
  End_of_fold = tail;
  Line_after_fold = tail->next;
  tail = End_of_fold->fold;
  Start_of_fold->foldline = START_FOLD;
  head = Start_of_fold->fold;
  Start_of_fold->fold = Start_of_fold->next;
  Start_of_fold->next = Line_after_fold;
  Line_after_fold->prec = Start_of_fold;
  /*restore indent*/
  Start_of_fold->UU.U1.indent = End_of_fold->UU.U1.fold_indent;
  scr_pos+=total_indent(Start_of_fold);
  enter_depth_spaces -= total_indent(Start_of_fold);
  current = Start_of_fold;
  check_fold(current);
  p = Start_of_fold->fold;
  while (p->foldline != END_FOLD) {
    if (p->foldline == START_OPEN_FOLD) close_fold_at(p);
    p = p->next;
  }
}
/*}}}  */
/*{{{  pre_open_fold*/
public void pre_open_fold(element *q)
{
  element *p;
  int i;

  pre_pre_open_fold(q);
  p = Start_of_fold;
  i = total_indent(p);
  p->UU.U1.indent = i;
  do {
    p = p->next;
    p->UU.U1.indent += i;
  } while (p != End_of_fold);
}
/*}}}  */
/*{{{  enter_fold*/
public void enter_fold(void)
{ element *p;

  if (current->foldline == START_FOLD)
   /*{{{  normal fold -> enter*/
   { current->fold_close_line = cursor_level;
     pre_enter_fold(current);
     current = current->next;
     restore_element(4);
     return;
   }
   /*}}}  */
  else
   /*{{{  try to enter file*/
   { unsigned char *filename;

     /*{{{  prepare filed-fold*/
     if (current->foldline == START_FILED)
      /*{{{  check filed fold*/
      { if (!normal_att(current,TRUE))
         /*{{{  no access*/
         { message(get_msg(M_NO_TEXT),TRUE);
           return;
         }
         /*}}}  */
        if (current->fold != current->other_end)
         /*{{{  no list enter*/
         { message(get_msg(M_NO_ENTER_LIST_FF),TRUE);
           return;
         }
         /*}}}  */
        if (access((char *)(filename=get_data(current->other_end)),R_OK) == -1)
         /*{{{  cannot open file*/
         { err_message(get_msg(M_CANTOPEN),filename,TRUE);
           return;
         }
         /*}}}  */
        p = current;
        total_save((FILE*)0);
        if (file_changed && !yes(get_msg(M_NO_SAVE_CANCEL))) return;
        scr_pos = 1;
        current = p;
        current->foldline = START_FOLD; /* for copy ! */
      }
      /*}}}  */
     else if (current->foldline==NOT_FOLD)
      /*{{{  get filename to push on enter-stack*/
      { unsigned char buff[_POSIX_PATH_MAX];
        unsigned char *hd;
        int cursor_x_pos;

        hd=filename=get_data(current);
        cursor_x_pos=scr2txt(enter_depth_spaces,scr_pos,filename);
        /*{{{  get filename*/
        /*{{{  copy pattern from current line*/
        while (cursor_x_pos--)
           if (*filename ) filename++; else break;
        { unsigned char *d=buff;

          while
           (   *filename
            && *filename!='"'
            && *filename!='\''
            && *filename!=' '
           ) *d++ = *filename++;
          *d='\0';
        }
        /*}}}  */
        filename=buff;
        if (!*filename || access((char*)filename,R_OK) == -1)
           hd=fileprompt(filename);
        if (aborted || !*filename) return;
        /*}}}  */
        find_element(line_no(current)-1,cursor_level);
        total_save((FILE*)0);
        restore_element(cursor_level);
        if (file_changed && !yes(get_msg(M_NO_SAVE_CANCEL))) return;
        /*{{{  make fold for filename, after save!*/
        start_make_fold();
        make_fold();
        set_data(current,hd);
        set_data(current->other_end,filename);
        current->foldline = START_FOLD;
        /*}}}  */
      }
      /*}}}  */
     else
      /*{{{  error-return*/
      { message(get_msg(M_NO_SIMPLE_FOLD),TRUE);
        return;
      }
      /*}}}  */
     /*}}}  */
     /*{{{  read the filed fold*/
     real_tail->UU.U0.int1 = line_no(head->next);
     real_tail->UU.U0.int2 = line_no(current);
     real_tail->UU.U0.v_mode = read_only;
     real_tail->fold_close_line = cursor_level;
     copy_a_line(&p);
     current = p;
     delete_list(head->next->next, tail->prec);   /*except for tail/head*/
     current->foldline = START_ENTER_FILED;
     real_tail = current->fold;
     current->next = real_tail;
     head = real_head->next;
     join_links(head, current);
     real_head = head;
     join_links(real_tail, tail);
     tail = real_tail;   /*ie file_ptr*/
     file_changed=FALSE;
     title_op(RESET_CF);
     insert_file(tail, current, tail, TRUE, &dir_edit, (FILE*)0);
     current = current->next;
     restore_element(4);
#     ifdef WINDOW_TITLE_CHANGE
        chg_w_title((char*)0);
#     endif
     /*}}}  */
     delay_auto_write();
     call_number_macro(auto_macro);
     /*{{{  current_ring_file*/
     add_edit_file((char*)get_data(real_tail),TRUE);
     /*}}}  */
   }
   /*}}}  */
}
/*}}}  */
/*{{{  exit_fold*/
public void exit_fold(void)
{
  /*{{{  normal fold -> exit*/
  if (tail != real_tail) {
    pre_exit_fold();
    restore_element(current->fold_close_line);
    return;
  }
  /*}}}  */
  /*{{{  at top -> error*/
  if (real_tail->next == (element*)0 && !*get_data(real_tail))
   { message(get_msg(M_TOP),TRUE);return; }
  /*}}}  */
  total_save((FILE*)0);
  if (file_changed && !yes(get_msg(M_NO_SAVE_CANCEL))) return;
  if ( real_tail->next == (element*)0 || !(*get_data(real_tail)))
     open_arg_list();
  else
   /*{{{  open the normal filed fold*/
   {
     /*{{{  remove data of entered file*/
     real_tail = tail->next;
     real_head = head->prec;
     delete_list(head->next, tail);
     join_links(head,real_tail);
     tail = real_tail;
     head = real_head;
     /*}}}  */
     /*{{{  show new, corrected title, before reading!*/
     title_op(  read_only==(real_tail->UU.U0.v_mode||user_view)
              ? CHGTITLE
              :   (read_only=(real_tail->UU.U0.v_mode||user_view))
                ? SET_VIEW
                : RESET_VIEW);
     /*}}}  */
     /*{{{  get file*/
     { int l=real_tail->UU.U0.int2-real_tail->UU.U0.int1;

       real_tail->UU.U0.int2=real_tail->UU.U0.int1=0;
       insert_file(tail, head->next, tail, TRUE, &dir_edit, (FILE*)0);
       find_element(l,real_tail->fold_close_line);
     }
     /*}}}  */
     /*{{{  update title and windowtitle*/
     title_op(RESET_CF);
#     ifdef WINDOW_TITLE_CHANGE
        chg_w_title((char*)0);
#     endif
     /*}}}  */
     delay_auto_write();
     call_number_macro(auto_macro);
     /*{{{  current_ring_file*/
     add_edit_file((char*)get_data(real_tail),TRUE);
     /*}}}  */
   }
   /*}}}  */
}
/*}}}  */
/*{{{  open_fold*/
public void open_fold(void)
{
  if (current->foldline != START_FOLD) {
    message(get_msg(M_NO_SIMPLE_FOLD),TRUE);
    return;
  }
  current->fold_close_line = cursor_level;
  pre_open_fold(current);
  restore_to_end(cursor_level);
}
/*}}}  */
/*{{{  close_fold*/
public void close_fold(void)
{
  element *p;

  p = current;
  if (p->foldline == END_FOLD)
    p = p->other_end;
  while (p != head && p->foldline != START_OPEN_FOLD) {
    if (p->foldline == END_FOLD)
      p = p->other_end->prec;
    else
      p = p->prec;
  }
  if (p != head) {
    current = p;
    close_fold_at(p);
    restore_or_restore_to_end();
    return;
  }
  if (tail != real_tail)
    message(get_msg(M_USE_EXIT),TRUE);
  else
    vmessage(get_msg(M_TOP));
}
/*}}}  */
/*{{{  remove_fold*/
public void remove_fold(void)
{
  element *new, *p;
  int ind, f_ind;

  if (current->foldline != START_FOLD) {
    message(get_msg(M_NO_SIMPLE_FOLD),TRUE);
    return;
  }
  ind = current->UU.U1.indent;
  f_ind = current->UU.U1.fold_indent;
  pre_pre_open_fold(current);
  new = Start_of_fold->next;
  p = new;
  while (p != End_of_fold) {
    p->UU.U1.indent = ind;
    if (p->foldline == NOT_FOLD) {
      line_buffer[0]='\0';
      spaces(line_buffer,f_ind);
      strcat((char *)line_buffer,(char *)get_data(p));
      set_data(p,line_buffer);
    } else
      p->UU.U1.fold_indent += f_ind;
    p = p->next;
  }
  if (new == End_of_fold) new = Line_after_fold;
  current = End_of_fold;
  erase_current_link();
  current = Start_of_fold;
  erase_current_link();
  current = new;
  if (screen_start == Start_of_fold) {
    restore_element(4);
    return;
  }
  if (new == Line_after_fold) restore_or_restore_to_end();
  else restore_to_end(cursor_level);
}
/*}}}  */
/*{{{  create_list*/
public void create_list(FILE *pipe)
{
  element *p;

  proc_new(&head);
  head->prec = 0;
  proc_new(&tail);
  tail->next = 0;
  proc_new(&p);
  join_links(head, p);
  join_links(p, tail);
  p->foldline = START_ENTER_FILED;
  p->other_end = tail;
  tail->other_end = p;
  tail->foldline = END_FOLD;
  insert_file((element *)0, p, tail, TRUE, &dir_edit, pipe);
  current = p->next;
  real_head = head;
  real_tail = tail;
}
/*}}}  */
