/* Optimal cursor motion functions.

   Copyright (C) 1985 Free Software Foundation, Inc.
    Based primarily on public domain code written by Chris Torek
    Originally part of GNU Emacs.
    Vastly edited and modified for use within ne.

   Copyright (C) 1993 Sebastiano Vigna

    This file is part of ne, the nice editor.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 1, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */

#include <stdio.h>
#include <curses.h>
#include "cm.h"

#define	BIG	9999

int	cost;					/* sums up costs */


/* This function is used in place of putchar() in tputs() so can we can
computed the padded length of a capability string. Note that they should
be putchar()-like, so we have to care about the returned value. */


int evalcost (int c) {
	cost++;
	return(c);
}



/* This function is used in tputs(). */


int cmputc (int c) {
	return(putchar(c & 0177));
}



/* This function (Re)Initializes the cost factors. */

void cmcostinit (void) {

	char	*p;

#define	COST(x,e)	(x ? (cost = 0, tputs (x, 1, e), cost) : BIG)
#define CMCOST(x,e)	((x == 0) ? BIG : (p = tparm(x, 0, 0), COST(p ,e)))


	Wcm.cc_up =		COST (Wcm.cm_up, evalcost);
	Wcm.cc_down = 	COST (Wcm.cm_down, evalcost);
	Wcm.cc_left = 	COST (Wcm.cm_left, evalcost);
	Wcm.cc_right = COST (Wcm.cm_right, evalcost);
	Wcm.cc_home = 	COST (Wcm.cm_home, evalcost);
	Wcm.cc_cr = 	COST (Wcm.cm_cr, evalcost);
	Wcm.cc_ll = 	COST (Wcm.cm_ll, evalcost);

	/* These last three are actually minimum costs.  When (if) they are
	candidates for the least-cost motion, the real cost is computed.
	(Note that "0" is the assumed to generate the minimum cost.
	While this is not necessarily true, I have yet to see a terminal
	for which is not; all the terminals that have variable-cost
	cursor motion seem to take straight numeric values.  --ACT) */

	Wcm.cc_abs =  CMCOST (Wcm.cm_abs, evalcost);
	Wcm.cc_habs = CMCOST (Wcm.cm_habs, evalcost);
	Wcm.cc_vabs = CMCOST (Wcm.cm_vabs, evalcost);

#undef CMCOST
#undef COST
}



/* This function calculates the cost to move from (srcy, srcx) to (dsty, dstx)
using up and down, and left and right motions.  If doit is set actually perform
the motion. Originally, this function was also considering the existence of
tabs, but it is clear that almost all new systems will not allow usage of tabs
because of the lack of a clear semantics. */


static int calccost (int srcy, int srcx, int dsty, int dstx, int doit) {

	register int deltay, deltax, c, totalcost;
	register char *p;

	/* If have just wrapped on a terminal with xn,
	don't believe the cursor position: give up here
	and force use of absolute positioning.  */

	if (curX == Wcm.cm_cols) {
		if (doit) assert(FALSE);
		return(BIG);
	}

	totalcost = 0;

	if ((deltay = dsty - srcy) != 0) {

		if (deltay < 0) {
			p = Wcm.cm_up;
			c = Wcm.cc_up;
			deltay = -deltay;
		}
		else {
			p = Wcm.cm_down;
			c = Wcm.cc_down;
		}

		if (c == BIG) {
			if (doit) assert(FALSE);
			return c;
		}

		totalcost = c * deltay;

		if (doit)
			while (--deltay >= 0)
				tputs (p, 1, cmputc);
	}
	
	if ((deltax = dstx - srcx) == 0) return(totalcost);

	if (deltax > 0) {
		p = Wcm.cm_right;
		c = Wcm.cc_right;
	}
	else {
		p = Wcm.cm_left;
		c = Wcm.cc_left;
		deltax = -deltax;
	}

	if (c == BIG) {
		if (doit) assert(FALSE);
		return BIG;
	}

	totalcost += c * deltax;

	if (doit)
		while (--deltax >= 0)
			tputs (p, 1, cmputc);

	return(totalcost);
}



/* This function marks the cursor position as unknown. It is mainly useful
for capabilities like set_scroll_region, which destroy the cursor position. */


void losecursor(void) {
	curY = -1;
}



#define	USEREL	0
#define	USEHOME	1
#define	USELL	2
#define	USECR	3


/* This function moves the cursor to a given position. */

void cmgoto (int row, int col) {
	int	homecost,
	crcost,
	llcost,
	relcost,
	directcost;
	int	use;
	char	*p,
	*dcm;

	/* First the degenerate case */
	if (row == curY && col == curX) /* already there */
		return;

	if (curY >= 0 && curX >= 0) {
		/* 
       * Pick least-cost motions
       */

		relcost = calccost (curY, curX, row, col, 0);
		use = USEREL;

		if ((homecost = Wcm.cc_home) < BIG)
			homecost += calccost (0, 0, row, col, 0);

		if (homecost < relcost)
			relcost = homecost, use = USEHOME;

		if ((llcost = Wcm.cc_ll) < BIG)
			llcost += calccost (Wcm.cm_rows - 1, 0, row, col, 0);

		if (llcost < relcost)
			relcost = llcost, use = USELL;

		if ((crcost = Wcm.cc_cr) < BIG) {
			if (Wcm.cm_autolf)
				if (curY + 1 >= Wcm.cm_rows)
					crcost = BIG;
				else
					crcost += calccost (curY + 1, 0, row, col, 0);
			else
				crcost += calccost (curY, 0, row, col, 0);
		}

		if (crcost < relcost)
			relcost = crcost, use = USECR;

		directcost = Wcm.cc_abs, dcm = Wcm.cm_abs;

		if (row == curY && Wcm.cc_habs < BIG)
			directcost = Wcm.cc_habs, dcm = Wcm.cm_habs;
		else if (col == curX && Wcm.cc_vabs < BIG)
			directcost = Wcm.cc_vabs, dcm = Wcm.cm_vabs;
	}
	else {
		directcost = 0, relcost = 100000;
		dcm = Wcm.cm_abs;
	}

	/* 
   * In the following comparison, the = in <= is because when the costs
   * are the same, it looks nicer (I think) to move directly there.
   */

	if (directcost <= relcost) {

		/* compute REAL direct cost */

		cost = 0;

		p = (dcm == Wcm.cm_habs) ? tparm (dcm, col) : (dcm == Wcm.cm_vabs) ? tparm (dcm, row) : tparm (dcm, row, col);

		tputs (p, 1, evalcost);

		if (cost <= relcost) {
			tputs (p, 1, cmputc);
			curY = row;
			curX = col;
			return;
		}
	}

	switch (use) {
	case USEHOME:
		tputs (Wcm.cm_home, 1, cmputc);
		curY = curX = 0;
		break;

	case USELL:
		tputs (Wcm.cm_ll, 1, cmputc);
		curY = Wcm.cm_rows - 1;
		curX = 0;
		break;

	case USECR:
		tputs (Wcm.cm_cr, 1, cmputc);
		if (Wcm.cm_autolf) curY++;
		curX = 0;
		break;
	}

	calccost (curY, curX, row, col, 1);

	curY = row;
	curX = col;
}


/* This function checks, after initialization of Wcm, that we 
have enough capabilities to move the cursor. */

int Wcm_init (void) {

	/* Check that we know the size of the screen.... */

	if (Wcm.cm_rows <= 0 || Wcm.cm_cols <= 0)
		return(-1);

	if (Wcm.cm_abs && !Wcm.cm_ds)
		return(0);

	/* Require up and left, and, if no absolute, down and right */

	if (!Wcm.cm_up || !Wcm.cm_left)
		return(-1);

	if (!Wcm.cm_abs && (!Wcm.cm_down || !Wcm.cm_right))
		return(-1);

	return(0);
}


