/* This file is in the public domain. */

/*
 * The routines in this file move the cursor around on the screen. They
 * compute a new value for the cursor, then adjust ".". The display code
 * always updates the cursor location, so only moves between lines, or
 * functions that adjust the top line in the window and invalidate the
 * framing, are hard.
 */

#include <stdlib.h>		/* atoi(3), ugh */
#include "estruct.h"
#include "edef.h"

extern int getccol(int bflg);
extern void mlwrite();
extern int mlreplyt();

int forwchar(int f, int n);
int backchar(int f, int n);
int forwline(int f, int n);
int backline(int f, int n);
int pagedown(int f, int n);
int pageup(int f, int n);

/*
 * This routine, given a pointer to a LINE, and the current cursor goal
 * column, return the best choice for the offset. The offset is returned.
 * Used by "C-N" and "C-P".
 */
long getgoal(LINE *dlp)
{
  int col = 0;
  int dbo = 0;
  int newcol, c;

  while (dbo != llength(dlp))
    {
      c = lgetc(dlp, dbo);
      newcol = col;
      if (c == '\t')
	newcol |= 0x07;
      else if (c < 0x20 || c == 0x7F)
	++newcol;
      ++newcol;
      if (newcol > curgoal)
	break;
      col = newcol;
      ++dbo;
    }
  return (dbo);
}

/*
 * Move the cursor to the
 * beginning of the current line.
 * Trivial.
 */
/* ARGSUSED0 */
int gotobol(int f, int n)
{
  curwp->w_doto = 0;
  return (TRUE);
}

/*
 * Move the cursor to the end of the current line. Trivial. No errors.
 */
/* ARGSUSED0 */
int gotoeol(int f, int n)
{
  curwp->w_doto = llength(curwp->w_dotp);
  return (TRUE);
}

/*
 * Move the cursor backwards by "n" characters. If "n" is less than zero call
 * "forwchar" to actually do the move. Otherwise compute the new cursor
 * location. Error if you try and move out of the buffer. Set the flag if the
 * line pointer for dot changes.
 */
int backchar(int f, int n)
{
  LINE *lp;

  if (n < 0)
    return (forwchar(f, -n));
  while (n--)
    {
      if (curwp->w_doto == 0)
	{
	  if ((lp = lback(curwp->w_dotp)) == curbp->b_linep)
	    return (FALSE);
	  curwp->w_dotp = lp;
	  curwp->w_doto = llength(lp);
	  curwp->w_flag |= WFMOVE;
	  curwp->w_dotline--;
	}
      else
	curwp->w_doto--;
    }
  return (TRUE);
}

/*
 * Move the cursor forwards by "n" characters. If "n" is less than zero call
 * "backchar" to actually do the move. Otherwise compute the new cursor
 * location, and move ".". Error if you try and move off the end of the
 * buffer. Set the flag if the line pointer for dot changes.
 */
int forwchar(int f, int n)
{
  if (n < 0)
    return (backchar(f, -n));
  while (n--)
    {
      if (curwp->w_doto == llength(curwp->w_dotp))
	{
	  if (curwp->w_dotp == curbp->b_linep)
	    return (FALSE);
	  curwp->w_dotp = lforw(curwp->w_dotp);
	  curwp->w_doto = 0;
	  curwp->w_flag |= WFMOVE;
	  curwp->w_dotline++;
	}
      else
	curwp->w_doto++;
    }
  return (TRUE);
}

/*
 * move to a particular line. argument (n) must be a positive integer for this
 * to actually do anything
 */
int gotoline(int f, int n)
{
  if ((n < 1) || (n > curwp->w_bufp->b_lines))	/* if a bogus argument...then leave */
    return (FALSE);				/* but we should never get here */

  /* first, we go to the start of the buffer */
  curwp->w_dotp = lforw(curbp->b_linep);
  curwp->w_doto = 0;
  curwp->w_dotline = 0;		/* and reset the line number */
  return (forwline(f, n - 1));
}

/*
 * Prompt for which line number we want to go to, then execute gotoline()
 * with that number as its argument.
 *
 * Make sure the bounds are within the file.
 *
 * Bound to M-G
 */
int setline(int f, int n)
{
  char setl[6];
  int l;

  (void)mlreplyt("Go to line: ", setl, 6, 10);
  l = atoi(setl);		/* XXX: This sucks! */

  if (l < 1)
    l = 1;
  else if (l > curwp->w_bufp->b_lines)
    l = curwp->w_bufp->b_lines;

  gotoline(f, l);
  return (TRUE);
}

/*
 * Goto the beginning of the buffer. Massive adjustment of dot. This is
 * considered to be hard motion; it really isn't if the original value of dot
 * is the same as the new value of dot. Normally bound to "M-<".
 */
/* ARGSUSED0 */
int gotobob(int f, int n)
{
  curwp->w_dotp = lforw(curbp->b_linep);
  curwp->w_doto = 0;
  curwp->w_flag |= WFHARD;
  curwp->w_dotline = 0;
  return (TRUE);
}

/*
 * Move to the end of the buffer. Dot is always put at the end of the file
 * (ZJ). The standard screen code does most of the hard parts of update.
 * Bound to "M->".
 */
/* ARGSUSED0 */
int gotoeob(int f, int n)
{
  curwp->w_dotp = curbp->b_linep;
  curwp->w_doto = 0;
  curwp->w_flag |= WFHARD;
  curwp->w_dotline = curwp->w_bufp->b_lines;
  return (TRUE);
}

/*
 * Move forward by full lines. If the number of lines to move is less than
 * zero, call the backward line function to actually do it. The last command
 * controls how the goal column is set. Bound to "C-N". No errors are possible.
 */
int forwline(int f, int n)
{
  LINE *dlp;

  if (n < 0)
    return (backline(f, -n));
  if ((lastflag & CFCPCN) == 0)/* Reset goal if last */
    curgoal = getccol(FALSE); /* not C-P or C-N */
  thisflag |= CFCPCN;
  dlp = curwp->w_dotp;
  while (n-- && dlp != curbp->b_linep)
  {
    dlp = lforw(dlp);
    curwp->w_dotline++;
  }
  curwp->w_dotp = dlp;
  curwp->w_doto = getgoal(dlp);
  curwp->w_flag |= WFMOVE;
  return (TRUE);
}

/*
 * This function is like "forwline", but goes backwards. The scheme is exactly
 * the same. Check for arguments that are less than zero and call your
 * alternate. Figure out the new line and call "movedot" to perform the
 * motion. No errors are possible. Bound to "C-P".
 */
int backline(int f, int n)
{
  LINE *dlp;

  if (n < 0)
    return (forwline(f, -n));
  if ((lastflag & CFCPCN) == 0)/* Reset goal if the */
    curgoal = getccol(FALSE); /* last isn't C-P, C-N */
  thisflag |= CFCPCN;
  dlp = curwp->w_dotp;
  while (n-- && lback(dlp) != curbp->b_linep)
  {
    dlp = lback(dlp);
    curwp->w_dotline--;
  }
  curwp->w_dotp = dlp;
  curwp->w_doto = getgoal(dlp);
  curwp->w_flag |= WFMOVE;
  return (TRUE);
}

/*
 * PgDn. Scroll down (ROWS - 1).
 * Just forwline(f, (ROWS -1))
 * Bound to C-V
 */
int pagedown(int f, int n)
{
  forwline(f, (ROWS - 1));
  return (TRUE);
}

/*
 * PgUp. Scroll up (ROWS - 1).
 * Just backline(f, (ROWS -1))
 * Bound to M-V
 */
int pageup(int f, int n)
{
  backline(f, (ROWS - 1));
  return (TRUE);
}

/*
 * Set the mark in the current window to the value of "." in the window. No
 * errors are possible. Bound to "M-.".
 */
/* ARGSUSED0 */
int setmark(int f, int n)
{
  curwp->w_markp = curwp->w_dotp;
  curwp->w_marko = curwp->w_doto;
  mlwrite("[Mark set]");
  return (TRUE);
}
