/* move.c - functions for moving around the file
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "mstring.h"
#include "terminal.h"
#include "editor.h"

long ed__curline;			 /* current line number */
long ed__curchar;			 /* current character index */
long ed__curcol;			 /* current readable column */

void ed_redraw__body(void);


/* Work out the length of the string that would be displayed.
 */
long ed__explen(char *line)
{
	char buf[4096];
	strncpy(buf, line, sizeof(buf) - 1);
	buf[sizeof(buf) - 1] = 0;
	mtagexpand(buf, sizeof(buf) - 10, t_cols + 1, 0, 0);
	return (strlen(buf));
}


/* Position the cursor in the X axis after moving in the X or Y.
 */
void ed__op__position_x(void)
{
	long d;

	d = ed__curchar - ed__leftchar;

	if ((d >= (t_cols - 2)) || (d < 1)) {
		ed__leftchar = ed__curchar - (t_cols / 2);
	}

	if (ed__leftchar < 0)
		ed__leftchar = 0;
}


/* Reposition the cursor in the X axis after moving in the Y.
 */
void ed__op__reposition_x(void)
{
	char *a;

	a = ed__array[ed__curline];
	if (!a)
		a = "";
	ed__curchar = mstrindex(a, ed__curcol);
	ed__op__position_x();
}


/* Move the cursor left one character. If the left edge is reached, move to
 * the end of the previous row.
 */
void ed__op_left(void)
{
	char buf[2];
	char *a;
	long o;
	int c;

	buf[0] = 0;
	a = ed__array[ed__curline];
	if (!a)
		a = buf;

	ed__curchar--;
	if (ed__curchar < 0) {
		if (ed__curline < 1) {
			ed__curchar = 0;
			return;
		}
		a = ed__array[ed__curline - 1];
		if (!a)
			a = "";
		ed__curchar = strlen(a);
		ed__curcol = mstrlen(a);
		ed__op_up();
		return;
	}

	if (ed__curchar > strlen(a))
		ed__curchar = strlen(a);
	c = a[ed__curchar];		    /* work out current column */
	a[ed__curchar] = 0;
	ed__curcol = mstrlen(a);
	a[ed__curchar] = c;

	o = ed__leftchar;
	ed__op__position_x();
	if (o != ed__leftchar) {
		t_goto(0, ed__start_line + (ed__curline - ed__topline));
		ed_redraw_line(ed__curline);
	}

	t_goto(ed__curchar - ed__leftchar,
	       ed__start_line + (ed__curline - ed__topline));
}


/* Move the cursor right one character. If the row end is reached, move to
 * the start of the next row.
 */
void ed__op_right(void)
{
	char buf[2];
	char *a;
	long o;
	int c;

	buf[0] = 0;			    /* work out current column */
	a = ed__array[ed__curline];
	if (!a)
		a = buf;

	ed__curchar++;
	if (ed__curchar > strlen(a)) {
		if (ed__curline >= (ed__lines - 1)) {
			ed__curchar--;
			return;
		}
		ed__curchar = 0;
		ed__curcol = 0;
		ed__op_down();
		return;
	}

	if (ed__curchar > strlen(a))
		ed__curchar = strlen(a);
	c = a[ed__curchar];
	a[ed__curchar] = 0;
	ed__curcol = mstrlen(a);
	a[ed__curchar] = c;

	o = ed__leftchar;
	ed__op__position_x();
	if (o != ed__leftchar) {
		t_goto(0, ed__start_line + (ed__curline - ed__topline));
		ed_redraw_line(ed__curline);
	}

	t_goto(ed__curchar - ed__leftchar,
	       ed__start_line + (ed__curline - ed__topline));
}


/* Move the cursor up one row.
 */
void ed__op_up(void)
{
	char buf[32];
	char *a;
	char *b;

	ed__curline--;

	if (ed__curline < 0) {
		ed__curline = 0;
		ed__op_home();
		return;
	}

	if ((ed__curline < 0) || (ed__curline >= ed__lines)) {
		a = "";
	} else {
		a = ed__array[ed__curline];
		if (!a)
			a = "";
	}

	if ((ed__curline < 0) || (ed__curline >= (ed__lines - 1))) {
		b = "";
	} else {
		b = ed__array[ed__curline + 1];
		if (!b)
			b = "";
	}

	if ((ed__leftchar != 0) || (ed__explen(b) != mstrlen(b))) {
		t_goto(0,
		       ed__start_line + ((ed__curline + 1) - ed__topline));
		ed_redraw_line(ed__curline + 1);
	}

	if (ed__curline < ed__topline) {    /* scroll screen */
		sprintf(buf, "\033[%dH\033[K", t_rows - 1);
		t_abswrite(buf);
		sprintf(buf, "\033[%d;%dr", ed__start_line + 1,
			t_rows - 1);
		t_abswrite(buf);
		sprintf(buf, "\033[%dH\033M", ed__start_line + 1);
		t_abswrite(buf);
		sprintf(buf, "\033[1;%dr", t_rows);
		t_abswrite(buf);
		ed__topline--;
		a = "\035Ra";		    /* force redraw of current line */
	}

	ed__op__reposition_x();

	if ((ed__leftchar != 0) || (ed__explen(a) != mstrlen(a))) {
		t_goto(0, ed__start_line + (ed__curline - ed__topline));
		ed_redraw_line(ed__curline);
	}

	t_goto(ed__curchar - ed__leftchar,
	       ed__start_line + (ed__curline - ed__topline));
}


/* Move the cursor down one row, or to the end of the line if we're at the
 * very last line.
 */
void ed__op_down(void)
{
	char buf[32];
	char *a;
	char *b;

	ed__curline++;

	if (ed__curline >= ed__lines) {
		ed__curline = ed__lines - 1;
		ed__op_end();
		return;
	}

	if ((ed__curline < 0) || (ed__curline >= ed__lines)) {
		b = "";
	} else {
		b = ed__array[ed__curline];
		if (!b)
			b = "";
	}

	if ((ed__curline < 1) || (ed__curline > ed__lines)) {
		a = "";
	} else {
		a = ed__array[ed__curline - 1];
		if (!a)
			a = "";
	}

	if ((ed__leftchar != 0) || (ed__explen(a) != mstrlen(a))) {
		t_goto(0,
		       ed__start_line + ((ed__curline - 1) - ed__topline));
		ed_redraw_line(ed__curline - 1);
	}

	if (ed__curline >= (ed__topline + (t_rows - 1 - ed__start_line))) {
		sprintf(buf, "\033[%dH\033[K", ed__start_line + 1);
		t_abswrite(buf);
		sprintf(buf, "\033[%d;%dr", ed__start_line + 1,
			t_rows - 1);
		t_abswrite(buf);
		sprintf(buf, "\033[%dH\n", t_rows - 1);
		t_abswrite(buf);
		sprintf(buf, "\033[1;%dr", t_rows);
		t_abswrite(buf);
		ed__topline++;
		b = "\035Ra";		    /* force redraw of current line */
	}

	ed__op__reposition_x();

	if ((ed__leftchar != 0) || (ed__explen(b) != mstrlen(b))) {
		t_goto(0, ed__start_line + (ed__curline - ed__topline));
		ed_redraw_line(ed__curline);
	}

	t_goto(ed__curchar - ed__leftchar,
	       ed__start_line + (ed__curline - ed__topline));
}


/* Move the cursor to the start of the current row.
 */
void ed__op_home(void)
{
	long o;

	ed__curchar = 0;
	ed__curcol = 0;

	o = ed__leftchar;
	ed__op__position_x();
	if (o != ed__leftchar) {
		t_goto(0, ed__start_line + (ed__curline - ed__topline));
		ed_redraw_line(ed__curline);
	}

	t_goto(ed__curchar - ed__leftchar,
	       ed__start_line + (ed__curline - ed__topline));
}


/* Move the cursor to the end of the current row.
 */
void ed__op_end(void)
{
	char *a;
	long o;

	a = ed__array[ed__curline];
	if (!a)
		a = "";

	ed__curchar = strlen(a);
	ed__curcol = mstrlen(a);

	o = ed__leftchar;
	ed__op__position_x();
	if (o != ed__leftchar) {
		t_goto(0, ed__start_line + (ed__curline - ed__topline));
		ed_redraw_line(ed__curline);
	}

	t_goto(ed__curchar - ed__leftchar,
	       ed__start_line + (ed__curline - ed__topline));
}


/* Move the cursor up one screen.
 */
void ed__op_prevscreen(void)
{
	long pl, ptl;

	if (ed__curline < 1)
		return;

	pl = ed__curline;
	ptl = ed__topline;

	ed__curline -= ((t_rows - 2) - ed__start_line) / 2;
	if (ed__curline < 0)
		ed__curline = 0;

	if (pl == ed__curline)
		return;

	ed__topline -= ((t_rows - 2) - ed__start_line) / 2;
	if (ed__topline < 0)
		ed__topline = 0;

	ed__op__reposition_x();

	if (ptl == ed__topline) {	    /* only redraw old & new lines */
		t_goto(0, ed__start_line + (pl - ed__topline));
		ed_redraw_line(pl);
		t_goto(0, ed__start_line + (ed__curline - ed__topline));
		ed_redraw_line(ed__curline);
		t_goto(ed__curchar - ed__leftchar,
		       ed__start_line + (ed__curline - ed__topline));
	} else
		ed_redraw__body();
}


/* Move the cursor down one screen.
 */
void ed__op_nextscreen(void)
{
	long pl, ptl;

	if (ed__curline >= (ed__lines - 1))
		return;

	pl = ed__curline;
	ptl = ed__topline;

	ed__curline += ((t_rows - 2) - ed__start_line) / 2;
	if (ed__curline >= (ed__lines - 1))
		ed__curline = ed__lines - 1;

	if (pl == ed__curline)
		return;

	ed__topline = ed__curline - ((t_rows - 2) - ed__start_line);
	if (ed__topline < 0)
		ed__topline = 0;

	ed__op__reposition_x();

	if (ptl == ed__topline) {	    /* only redraw old & new lines */
		t_goto(0, ed__start_line + (pl - ed__topline));
		ed_redraw_line(pl);
		t_goto(0, ed__start_line + (ed__curline - ed__topline));
		ed_redraw_line(ed__curline);
		t_goto(ed__curchar - ed__leftchar,
		       ed__start_line + (ed__curline - ed__topline));
	} else
		ed_redraw__body();
}


/* Move to the top of the file.
 */
void ed__op_top(void)
{
	if (ed__curline < 1)
		return;
	ed__curline = 0;
	ed__topline = 0;
	ed__op__reposition_x();
	ed_redraw__body();
}


/* Move to the bottom of the file.
 */
void ed__op_bottom(void)
{
	if (ed__curline >= (ed__lines - 1))
		return;
	ed__curline = ed__lines - 1;
	if (ed__curline < 0)
		ed__curline = 0;
	ed__topline = ed__lines - 1 - ((t_rows - 2) - ed__start_line);
	if (ed__topline < 0)
		ed__topline = 0;
	ed__op__reposition_x();
	ed_redraw__body();
}


/* Move the cursor to the start of the previous word.
 */
void ed__op_prevword(void)
{
	char *a;
	long o;

	do {
		ed__curchar--;
		if (ed__curchar < 0) {
			if (ed__curline < 1)
				a = 0;
			else
				a = ed__array[ed__curline - 1];
			if (!a)
				a = "";
			ed__curchar = strlen(a);
			ed__curcol = mstrlen(a);

			if (ed__curline < 1) {

				o = ed__leftchar;
				ed__op__position_x();
				if (o != ed__leftchar) {
					t_goto(0,
					       ed__start_line +
					       (ed__curline -
						ed__topline));
					ed_redraw_line(ed__curline);
				}

				t_goto(ed__curchar - ed__leftchar,
				       ed__start_line + (ed__curline -
							 ed__topline));

				return;

			} else
				ed__op_up();

		}

		a = ed__array[ed__curline];
		if (!a)
			a = "";

	} while (a[ed__curchar] == ' ' || a[ed__curchar] == 0);

	do {
		ed__curchar--;

		if (ed__curchar < 0) {

			if (ed__curline < 1)
				a = 0;
			else
				a = ed__array[ed__curline - 1];
			if (!a)
				a = "";
			ed__curchar = strlen(a);
			ed__curcol = mstrlen(a);

			if (ed__curline < 1) {

				o = ed__leftchar;
				ed__op__position_x();
				if (o != ed__leftchar) {
					t_goto(0,
					       ed__start_line +
					       (ed__curline -
						ed__topline));
					ed_redraw_line(ed__curline);
				}

				t_goto(ed__curchar - ed__leftchar,
				       ed__start_line + (ed__curline -
							 ed__topline));

				return;

			} else
				ed__op_up();

		}

		a = ed__array[ed__curline];
		if (!a)
			a = "";

	} while (a[ed__curchar] != ' ' && a[ed__curchar] != 0);

	ed__op_right();
}


/* Move the cursor to the end of the current word.
 */
void ed__op_nextword(void)
{
	char *a;
	long o;
	int c;

	do {
		a = ed__array[ed__curline];
		if (!a)
			a = "";

		ed__curchar++;

		if (ed__curchar > strlen(a)) {

			if (ed__curline >= (ed__lines - 1)) {
				ed__curchar--;
				o = ed__leftchar;
				ed__op__position_x();
				if (o != ed__leftchar) {
					t_goto(0,
					       ed__start_line +
					       (ed__curline -
						ed__topline));
					ed_redraw_line(ed__curline);
				}

				t_goto(ed__curchar - ed__leftchar,
				       ed__start_line + (ed__curline -
							 ed__topline));

				return;

			} else {

				ed__curchar = 0;
				ed__curcol = 0;
				ed__op_down();
				a = ed__array[ed__curline];
				if (!a)
					a = "";

			}

		}

	} while (a[ed__curchar] == ' ' || a[ed__curchar] == 0);

	do {
		a = ed__array[ed__curline];
		if (!a)
			a = "";

		ed__curchar++;

		if (ed__curchar > strlen(a)) {

			if (ed__curline >= ed__lines) {
				ed__curchar--;
				o = ed__leftchar;
				ed__op__position_x();
				if (o != ed__leftchar) {
					t_goto(0,
					       ed__start_line +
					       (ed__curline -
						ed__topline));
					ed_redraw_line(ed__curline);
				}

				t_goto(ed__curchar - ed__leftchar,
				       ed__start_line + (ed__curline -
							 ed__topline));

				return;

			} else {

				ed__curchar = 0;
				ed__curcol = 0;
				ed__op_down();
				a = ed__array[ed__curline];
				if (!a)
					a = "";

			}

		}

	} while (a[ed__curchar] != ' ' && a[ed__curchar] != 0);

	c = a[ed__curchar];		    /* work out current column */
	a[ed__curchar] = 0;
	ed__curcol = mstrlen(a);
	a[ed__curchar] = c;

	o = ed__leftchar;
	ed__op__position_x();
	if (o != ed__leftchar) {
		t_goto(0, ed__start_line + (ed__curline - ed__topline));
		ed_redraw_line(ed__curline);
	}

	t_goto(ed__curchar - ed__leftchar,
	       ed__start_line + (ed__curline - ed__topline));
}


/* Ask the user for a line number, and jump to it.
 */
void ed__op_goto(void)
{
	char buf[16];
	long n;

	buf[0] = 0;

	t_goto(0, t_rows - 1);
	t_clrtoeol();

	do {
		n = t_input("Go to line : ", buf, sizeof(buf) - 1);
	} while (n == 1);

	t_goto(0, t_rows - 1);
	t_clrtoeol();

	t_goto(ed__curchar - ed__leftchar,
	       ed__start_line + (ed__curline - ed__topline));

	n = atol(buf);
	n--;
	if (n < 0)
		n = 0;
	if (n > (ed__lines - 1))
		n = ed__lines - 1;

	if (n == ed__curline)
		return;

	ed__curline = n;

	ed__topline = ed__curline - (((t_rows - 2) - ed__start_line) / 2);
	if (ed__topline < 0)
		ed__topline = 0;

	ed__op__reposition_x();
	ed_redraw__body();
}


/* Ask the user for a text string, find it, and jump to it.
 */
void ed__op_find(void)
{
	static char buf[64] = { 0 };	 /* persistent across calls */
	char work[4096];
	int found;
	char *a;
	char *p;
	char *q;
	long o;
	int n;

	t_goto(0, t_rows - 1);
	t_clrtoeol();

	do {
		n = t_input("Search : ", buf, sizeof(buf) - 1);
	} while (n == 1);

	t_goto(0, t_rows - 1);
	t_clrtoeol();

	t_goto(ed__curchar - ed__leftchar,
	       ed__start_line + (ed__curline - ed__topline));

	found = 0;
	o = ed__curline;

	p = work;

	while ((!found) && (ed__curline < (ed__lines - 1))) {	/* search loop */

		t_bored(1);

		a = ed__array[ed__curline];
		if (!a)
			a = "";

		if (ed__curline == o) {
			strncpy(work, a + ed__curchar, sizeof(work) - 1);
		} else {
			strncpy(work, a, sizeof(work) - 1);
		}

		work[sizeof(work) - 1] = 0;
		mstripattr(work);

		p = work;

		do {
			q = p;
			p = strchr(q, tolower(buf[0]));
			if (!p)
				p = strchr(q, toupper(buf[0]));
			if (p) {
				if (strncasecmp(p, buf, strlen(buf)) == 0)
					found = 1;
				else
					p++;
			}
		} while ((p) && (!found));

		if (!found)
			ed__curline++;
	}

	if (found) {
		ed__curcol =
		    ((ed__curline == o) ? ed__curcol : 0) + (p - work);
	}

	t_bored(0);

	ed__topline = ed__curline - (((t_rows - 2) - ed__start_line) / 2);
	if (ed__topline < 0)
		ed__topline = 0;

	ed__op__reposition_x();
	ed_redraw__body();
}

/* EOF */
