/*
 * zle_move.c - editor movement
 *
 * This file is part of zsh, the Z shell.
 *
 * Copyright (c) 1992-1997 Paul Falstad
 * All rights reserved.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and to distribute modified versions of this software for any
 * purpose, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * In no event shall Paul Falstad or the Zsh Development Group be liable
 * to any party for direct, indirect, special, incidental, or consequential
 * damages arising out of the use of this software and its documentation,
 * even if Paul Falstad and the Zsh Development Group have been advised of
 * the possibility of such damage.
 *
 * Paul Falstad and the Zsh Development Group specifically disclaim any
 * warranties, including, but not limited to, the implied warranties of
 * merchantability and fitness for a particular purpose.  The software
 * provided hereunder is on an "as is" basis, and Paul Falstad and the
 * Zsh Development Group have no obligation to provide maintenance,
 * support, updates, enhancements, or modifications.
 *
 */

#include "zle.mdh"
#include "zle_move.pro"

static int vimarkcs[27], vimarkline[27];

/**/
int
beginningofline(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = endofline(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	if (cs == 0)
	    return 0;
	if (line[cs - 1] == '\n')
	    if (!--cs)
		return 0;
	while (cs && line[cs - 1] != '\n')
	    cs--;
    }
    return 0;
}

/**/
int
endofline(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = beginningofline(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	if (cs >= ll) {
	    cs = ll;
	    return 0;
	}
	if (line[cs] == '\n')
	    if (++cs == ll)
		return 0;
	while (cs != ll && line[cs] != '\n')
	    cs++;
    }
    return 0;
}

/**/
int
beginningoflinehist(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = endoflinehist(args);
	zmult = n;
	return ret;
    }
    while (n) {
	if (cs == 0)
	    break;
	if (line[cs - 1] == '\n')
	    if (!--cs)
		break;
	while (cs && line[cs - 1] != '\n')
	    cs--;
	n--;
    }
    if (n) {
	int m = zmult, ret;

	zmult = n;
	ret = uphistory(args);
	zmult = m;
	cs = 0;
	return ret;
    }
    return 0;
}

/**/
int
endoflinehist(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = beginningoflinehist(args);
	zmult = n;
	return ret;
    }
    while (n) {
	if (cs >= ll) {
	    cs = ll;
	    break;
	}
	if (line[cs] == '\n')
	    if (++cs == ll)
		break;
	while (cs != ll && line[cs] != '\n')
	    cs++;
	n--;
    }
    if (n) {
	int m = zmult, ret;

	zmult = n;
	ret = downhistory(args);
	zmult = m;
	return ret;
    }
    return 0;
}

/**/
int
forwardchar(char **args)
{
    cs += zmult;
    if (cs > ll)
	cs = ll;
    if (cs < 0)
	cs = 0;
    return 0;
}

/**/
int
backwardchar(char **args)
{
    cs -= zmult;
    if (cs > ll)
	cs = ll;
    if (cs < 0)
	cs = 0;
    return 0;
}

/**/
int
setmarkcommand(char **args)
{
    mark = cs;
    return 0;
}

/**/
int
exchangepointandmark(char **args)
{
    int x;

    x = mark;
    mark = cs;
    cs = x;
    if (cs > ll)
	cs = ll;
    return 0;
}

/**/
int
vigotocolumn(char **args)
{
    int x, y;

    findline(&x, &y);
    if (zmult >= 0)
	cs = x + zmult - (zmult > 0);
    else
	cs = y + zmult;
    if (cs > y)
	cs = y;
    if (cs < x)
	cs = x;
    return 0;
}

/**/
int
vimatchbracket(char **args)
{
    int ocs = cs, dir, ct;
    unsigned char oth, me;

  otog:
    if (cs == ll || line[cs] == '\n') {
	cs = ocs;
	return 1;
    }
    switch (me = line[cs]) {
    case '{':
	dir = 1;
	oth = '}';
	break;
    case /*{*/ '}':
	virangeflag = -virangeflag;
	dir = -1;
	oth = '{'; /*}*/
	break;
    case '(':
	dir = 1;
	oth = ')';
	break;
    case ')':
	virangeflag = -virangeflag;
	dir = -1;
	oth = '(';
	break;
    case '[':
	dir = 1;
	oth = ']';
	break;
    case ']':
	virangeflag = -virangeflag;
	dir = -1;
	oth = '[';
	break;
    default:
	cs++;
	goto otog;
    }
    ct = 1;
    while (cs >= 0 && cs < ll && ct) {
	cs += dir;
	if (line[cs] == oth)
	    ct--;
	else if (line[cs] == me)
	    ct++;
    }
    if (cs < 0 || cs >= ll) {
	cs = ocs;
	return 1;
    } else if(dir > 0 && virangeflag)
	cs++;
    return 0;
}

/**/
int
viforwardchar(char **args)
{
    int lim = findeol() - invicmdmode();
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = vibackwardchar(args);
	zmult = n;
	return ret;
    }
    if (cs >= lim)
	return 1;
    while (n-- && cs < lim)
	cs++;
    return 0;
}

/**/
int
vibackwardchar(char **args)
{
    int n = zmult;

    if (n < 0) {
	int ret;
	zmult = -n;
	ret = viforwardchar(args);
	zmult = n;
	return ret;
    }
    if (cs == findbol())
	return 1;
    while (n--) {
	cs--;
	if (cs < 0 || line[cs] == '\n') {
	    cs++;
	    break;
	}
    }
    return 0;
}

/**/
int
viendofline(char **args)
{
    int oldcs = cs, n = zmult;

    if (n < 1)
	return 1;
    while(n--) {
	if (cs > ll) {
	    cs = oldcs;
	    return 1;
	}
	cs = findeol() + 1;
    }
    cs--;
    lastcol = 1<<30;
    return 0;
}

/**/
int
vibeginningofline(char **args)
{
    cs = findbol();
    return 0;
}

static int vfindchar, vfinddir, tailadd;

/**/
int
vifindnextchar(char **args)
{
    if ((vfindchar = vigetkey()) != -1) {
	vfinddir = 1;
	tailadd = 0;
	return virepeatfind(args);
    }
    return 1;
}

/**/
int
vifindprevchar(char **args)
{
    if ((vfindchar = vigetkey()) != -1) {
	vfinddir = -1;
	tailadd = 0;
	return virepeatfind(args);
    }
    return 1;
}

/**/
int
vifindnextcharskip(char **args)
{
    if ((vfindchar = vigetkey()) != -1) {
	vfinddir = 1;
	tailadd = -1;
	return virepeatfind(args);
    }
    return 1;
}

/**/
int
vifindprevcharskip(char **args)
{
    if ((vfindchar = vigetkey()) != -1) {
	vfinddir = -1;
	tailadd = 1;
	return virepeatfind(args);
    }
    return 1;
}

/**/
int
virepeatfind(char **args)
{
    int ocs = cs, n = zmult;

    if (!vfinddir)
	return 1;
    if (n < 0) {
	int ret;
	zmult = -n;
	ret = virevrepeatfind(args);
	zmult = n;
	return ret;
    }
    while (n--) {
	do
	    cs += vfinddir;
	while (cs >= 0 && cs < ll && line[cs] != vfindchar && line[cs] != '\n');
	if (cs < 0 || cs >= ll || line[cs] == '\n') {
	    cs = ocs;
	    return 1;
	}
    }
    cs += tailadd;
    if (vfinddir == 1 && virangeflag)
	cs++;
    return 0;
}

/**/
int
virevrepeatfind(char **args)
{
    int ret;

    if (zmult < 0) {
	zmult = -zmult;
	ret = virepeatfind(args);
	zmult = -zmult;
	return ret;
    }
    vfinddir = -vfinddir;
    ret = virepeatfind(args);
    vfinddir = -vfinddir;
    return ret;
}

/**/
int
vifirstnonblank(char **args)
{
    cs = findbol();
    while (cs != ll && iblank(line[cs]))
	cs++;
    return 0;
}

/**/
int
visetmark(char **args)
{
    int ch;

    ch = getkey(0);
    if (ch < 'a' || ch > 'z')
	return 1;
    ch -= 'a';
    vimarkcs[ch] = cs;
    vimarkline[ch] = histline;
    return 0;
}

/**/
int
vigotomark(char **args)
{
    int ch;

    ch = getkey(0);
    if (ch == c)
	ch = 26;
    else {
	if (ch < 'a' || ch > 'z')
	    return 1;
	ch -= 'a';
    }
    if (!vimarkline[ch])
	return 1;
    if (curhist != vimarkline[ch] && !zle_goto_hist(vimarkline[ch], 0, 0)) {
	vimarkline[ch] = 0;
	return 1;
    }
    cs = vimarkcs[ch];
    if (cs > ll)
	cs = ll;
    return 0;
}

/**/
int
vigotomarkline(char **args)
{
    vigotomark(args);
    return vifirstnonblank(zlenoargs);
}
