#include "buffer.h"
#include "externs.h"
#include "display.h"
#include "line.h"
  
int MOST_W_OPT = 0;

unsigned char *BEG;             /* beginning of current buffer */
unsigned char *EOB;             /* end of current buffer */
unsigned char MINI_BUF[132];

Buffer *BUF;

extern int SQUEEZE_LINES;
int NUM_LINES;
int ACTUAL_LINES;

unsigned char *C_POS;
int C_LINE;

unsigned char *beg_of_line1()
{
    unsigned char *pos;
    
    if (C_POS == BEG) return BEG;

    pos = C_POS;
    if (pos == EOB) pos--;

    if ((*pos != '\n') && (*(pos-1) == '\n')) return(pos);
    
    if ((*pos == '\n') && (pos == BEG)) return(BEG);
    /* suppose BEG[] is "....\nabcde\n\n\n\n\n..." and we are somwhere in
       the middle of the \n's.  Then we want to return the 2nd \n if the
       SQUEEZE is on otherwise just return where we are. */
    if ((*pos-- == '\n') && (*pos == '\n'))
      {
          if (SQUEEZE_LINES)
            {
                /* if we are between '\n's then skip past all of them. */
                while ((pos > BEG) && (*pos == '\n')) pos--;
                if ((pos == BEG) && (*pos == '\n')) return (BEG);
                pos++;
            }
          return (++pos);
      }      
    
    while((pos > BEG) && (*pos != '\n')) pos--;
    if (pos != BEG) pos++;
    else if ((pos == BEG) && (*pos == '\n')) pos++;
    return pos;
}

unsigned char *beg_of_line()
{
    unsigned char *b;
    int d,n;
    
    if (!MOST_W_OPT) return beg_of_line1();
    b = beg_of_line1();
    d = C_POS - b;
    n = d / SCREEN_WIDTH_M1;
    return(b +  (int) (n * SCREEN_WIDTH_M1));
}

/* does not move point */
unsigned char *end_of_line1()
{
    unsigned char *pos;

    pos = C_POS;
    if (pos >= EOB)  return(EOB);
    /*     if ((pos == BEG) && (*pos == '\n')) return (BEG); */
    
    /* find the first '\n' */
    if (*pos != '\n')
      {
          while ((pos < EOB) && (*pos != '\n')) pos++;
          return(pos);
      }
    
    if (!SQUEEZE_LINES) return (pos);

    /* if BEG = "....abc\n\n\n\n\ndef..." then we are at some first \n.  We
       want to return the last '\n' unless we wre at the first '\n'. */

    if ((pos > BEG) && ( pos--, *pos++ != '\n')) return (pos); 
    
    while ((pos < EOB) && (*pos == '\n')) pos++;
    if (pos-- == EOB) return (pos);
    if (pos < BEG) pos = BEG;
    return pos;
}

unsigned char *end_of_line()
{
    unsigned char *b, *e;
    int n;
    
    if (!MOST_W_OPT) return end_of_line1();

    b = beg_of_line();
    e = end_of_line1();
    n = (e - b) / SCREEN_WIDTH_M1;
    if (n) return(b + SCREEN_WIDTH_M1);
    return (e);
}

int forward_line(int n)
{
    int m, ok;
    int save = n;
    unsigned char *p;
    if (n > 0) 
      {
          if (MOST_B_OPT)
            {
                m = (EOB - C_POS)/16;
                if (n > m) n = m;
                C_POS += n * 16;
                C_LINE += n;
                return n;
            }
          else while (n--)
            {
                C_POS = end_of_line();
                /* next step handles newline at the EOB */
                if ((C_POS == EOB) || ((C_POS == EOB - 1) && (*C_POS == '\n'))) return (save - n - 1);
                C_LINE++;
                C_POS++;
                if (MOST_S_OPT)
                  {
                      p = C_POS;
                      ok = 1;
                      while ((*p <= ' ') && ok)
                        {
                            if (*p != '\n') p++;
                            if (p >= EOB) break;
                            if ((*p == '\n') || (apparant_distance(p) >= MOST_S_OPT))
                              {
                                  ok = 0;
                                  C_LINE--;
                                  n++;
                              }
                        }
                  } /* MOST_S_OPT */
            }
      }
    else
      {
          if (MOST_B_OPT)
            {
                m = (BEG - C_POS)/16;
                if (n < m) n = m;
                C_POS += n * 16;
                C_LINE += n;
                return n;
            }
          else while (n++)
            {
                C_POS = beg_of_line();
                if (C_POS == BEG) return (n - save - 1);
                C_POS--;
                C_LINE--;
                if (MOST_S_OPT)
                  {
                      C_POS = beg_of_line();
                      p = C_POS;
                      ok = 1;
                      while ((*p <= ' ') && ok)
                        {
                            if (*p != '\n') p++;
                            if (p >= EOB) break;
                            if ((*p == '\n') || (apparant_distance(p) >= MOST_S_OPT))
                              {
                                  ok = 0;
                                  C_LINE++;
                                  n--;
                              }
                        }
                  } /* MOST_S_OPT */
            }
      }
    return(save);
}

int count_lines(unsigned char *beg, unsigned char *end)
{
    int save_line = C_LINE, n;
    unsigned char *save_beg = beg, *save_eob = EOB, *save_pos = C_POS;

    if (MOST_B_OPT) return((int)(end - beg) / 16);
    BEG = C_POS = beg;
    EOB = end;
    n = 1;
    while(forward_line(1)) n++;
    if (*end ==  '\n') n--;
    if (!n) n = 1;
    C_POS = save_pos;
    EOB = save_eob;
    BEG = save_beg;
    C_LINE = save_line;
    return(n);
}

void goto_line(int line)
{
    register int i,j;
    unsigned char *pos;
    int dif_c, dif_b,dif_t;

    if (line < 1) line = 1;
    if (line > NUM_LINES) line = NUM_LINES;

    if (MOST_B_OPT)
      {
          C_POS = BEG + (16 * (line - 1));
          C_LINE = line;
          return;
      }
    
    dif_c = line - C_LINE;
    dif_b = line - NUM_LINES;
    dif_t = line - 1;

    /* 4 possibilites */
    if (dif_c <= 0)
      {
          if (dif_t < -dif_c) /* go from top */
            {
                C_LINE = 1;
                C_POS = BEG;
                (void) forward_line(dif_t);
            }
          else  /* from curr back */
            {
                (void) forward_line(dif_c);
            }
      }
    else if (dif_c > 0)
      {
          if ((dif_c + dif_b) < 0) /* go from curr */
            {
                (void) forward_line(dif_c);
            }
          else
            {
                C_LINE = NUM_LINES;
                C_POS = EOB;
                (void) forward_line(dif_b);
            }
      }
}       

/* return line the point is on without the final '\n's
    unless beg = end in which case we take care of it later ...

    returns 1 if the line should be wrapped */
int extract_line(unsigned char **beg, unsigned char **end)
{
    *beg = beg_of_line();
    *end = end_of_line();
    if (**end != '\n') return(1);
    while ((*end > BEG) && (**end == '\n')) *end = *end - 1;
    return(0);
}    

int what_line(unsigned char *pos)
{
    
    unsigned char *save_pos;
    int save_line, dir;
    register int dif_c, dif_b,dif_t;
    int ret;

    if (MOST_B_OPT)
      {
          return (1 + (pos - BEG)/16);
      }
    
        
    save_pos = C_POS;
    save_line = C_LINE;
    
    dif_c = pos - C_POS;
    dif_b = pos - EOB;
    dif_t = pos - BEG;

    /* 4 possibilites */
    if (dif_c <= 0)
      {
          if (dif_t < -dif_c) /* go from top */
            {
                C_LINE = 1;
                C_POS = BEG;
                dir = 1;
            }
          else  /* from curr back */
            {
                dir = -1;
            }
      }
    else if (dif_c > 0)
      {
          if ((dif_c + dif_b) < 0) /* go from curr */
            {
                dir = 1;
            }
          else
            {
                C_LINE = NUM_LINES;
                C_POS = EOB;
                dir = -1;
            }
      }
    if (dir == 1)
      {
          while(C_POS = end_of_line(), C_POS < pos)
            {
                C_POS++;
                C_LINE++;
            }
      }
    else
      {
          while(C_POS = beg_of_line(), pos < C_POS)
            {
                C_LINE--;
                C_POS--;
            }
      }

    ret = C_LINE;
    C_POS = save_pos;
    C_LINE = save_line;
    return(ret);
}

/* given a buffer position, find the line and column */
void find_row_column(unsigned char *pos, int *r, int *c)
{
    unsigned char *beg, *save_pos;
    int save_line;


    if (pos <= BEG)
      {
          *r = 1;
          *c = 1;
          return;
      }

    save_line = C_LINE;
    save_pos = C_POS;
    *r = what_line(pos);
    
    if (MOST_B_OPT)
      {
          *c = (int) (pos - BEG) - *r * 16 + 1;
          return;
      }
    C_LINE = *r;
    C_POS = pos;
    
    /* Now we have found the line it is on so.... */
    beg = beg_of_line();
    *c = 1;
    while (beg++ < pos) *c = *c + 1;
    C_LINE = save_line;
    C_POS = save_pos;
}       

Buffer *switch_to_buffer(Buffer *new)
{
    Buffer *old;
    old = BUF;
    BUF = new;
    BEG = BUF->beg;
    EOB = BUF->end;
    return(old);
}

void delete_buffer(Buffer *old)
{
    (void) free(BUF);
    BUF = old;
    BEG = BUF->beg;
    EOB = BUF->end;
}

Buffer *create_buffer(char *file)
{
    Buffer *buf;

    buf = (Buffer *) malloc(sizeof(Buffer));
    strcpy(buf->file,file);
    return(buf);
}

