/***************************************************************************
 * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
 * is provided to you without charge, and with no warranty.  You may give  *
 * away copies of JOVE, including sources, provided that this notice is    *
 * included in all the files.                                              *
 ***************************************************************************/

#include "jove.h"
#include "fmt.h"
#include "marks.h"

bool	MarksShouldFloat = YES;

Mark *
MakeMark(line, column, type)
register Line	*line;
int	column,
	type;
{
	register Mark	*newmark = (Mark *) emalloc(sizeof *newmark);

	MarkSet(newmark, line, column);
	newmark->m_next = curbuf->b_marks;
	newmark->m_flags = type;
	curbuf->b_marks = newmark;
	return newmark;
}

void
flush_marks(b)
Buffer	*b;
{
	register Mark	*m,
			*next;

	m = b->b_marks;
	while (m != NULL) {
		next = m->m_next;
		free((UnivPtr) m);
		m = next;
	}
}

void
DelMark(m)
register Mark	*m;
{
	register Mark	*mp = curbuf->b_marks;

	if (m == mp)
		curbuf->b_marks = m->m_next;
	else {
		while (mp != NULL && mp->m_next != m)
			mp = mp->m_next;
		if (mp == NULL)
			complain("Unknown mark!");
		mp->m_next = m->m_next;
	}
	free((UnivPtr) m);
}

void
AllMarkSet(b, line, col)
Buffer	*b;
register Line	*line;
int	col;
{
	register Mark	*mp;

	for (mp = b->b_marks; mp != NULL; mp = mp->m_next)
		MarkSet(mp, line, col);
}

void
MarkSet(m, line, column)
Mark	*m;
Line	*line;
int	column;
{
	m->m_line = line;
	m->m_char = column;
}

void
PopMark()
{
	int	pmark;

	if (curmark == NULL)
		return;
	if (curbuf->b_markring[(curbuf->b_themark + 1) % NMARKS] == NULL) {
		pmark = curbuf->b_themark;
		do {
			if (--pmark < 0)
				pmark = NMARKS - 1;
		} while (curbuf->b_markring[pmark] != NULL);

		curbuf->b_markring[pmark] = MakeMark(curline,
			curchar, MarksShouldFloat ? M_FLOATER : M_FIXED);
		ToMark(curmark);
		DelMark(curmark);
		curmark = NULL;
	} else
		PtToMark();

	pmark = curbuf->b_themark - 1;
	if (pmark < 0)
		pmark = NMARKS - 1;
	curbuf->b_themark = pmark;
}

void
SetMark()
{
	if (is_an_arg())
		PopMark();
	else
		set_mark();
}

void
set_mark()
{
	do_set_mark(curline, curchar);
}

void
do_set_mark(l, c)
Line	*l;
int	c;
{
	curbuf->b_themark = (curbuf->b_themark + 1) % NMARKS;
	if (curmark == NULL)
		curmark = MakeMark(l, c,
			MarksShouldFloat ? M_FLOATER : M_FIXED);
	else
		MarkSet(curmark, l, c);
	s_mess("[Point pushed]");
}

/* Move point to Mark */

void
ToMark(m)
Mark	*m;
{
	int	len;

	if (m == NULL)
		return;
	DotTo(m->m_line, m->m_char);
	if (curchar > (len = length(curline)))
		curchar = len;
}

Mark *
CurMark()
{
	if (curmark == NULL)
		complain("No mark.");
	return curmark;
}

void
PtToMark()
{
	Line	*mline;
	int	mchar;
	Mark	*m = CurMark();

	mline = curline;
	mchar = curchar;

	ToMark(m);
	MarkSet(m, mline, mchar);
}

/* Fix marks for after a deletion.  For now, even marks that don't
   float will actually float, because we can't allow marks to point
   to non-existant lines. */

void
DFixMarks(line1, char1, line2, char2)
register Line	*line1,
		*line2;
int	char1,
	char2;
{
	register Mark	*m;
	Line	*lp;

	if (curbuf->b_marks == NULL)
		return;
	for (lp = line1; lp != line2->l_next; lp = lp->l_next) {
		for (m = curbuf->b_marks; m != NULL; m = m->m_next) {
			if (m->m_line == lp
			&& (lp != line1 || m->m_char > char1))
			{
				if (lp == line2 && m->m_char >= char2)
					m->m_char -= char2-char1;
				else
					m->m_char = char1;
				m->m_line = line1;
				if (line1 != line2)
					m->m_flags |= M_BIG_DELETE;
			}
		}
	}
}

/* Fix marks after an insertion.  Marks that don't float are ignored
   on insertion, which means PtToMark has to be careful ... */

void
IFixMarks(line1, char1, line2, char2)
register Line	*line1,
		*line2;
int	char1,
	char2;
{
	register Mark	*m;

	for (m = curbuf->b_marks; m != NULL; m = m->m_next) {
		if ((m->m_flags & M_FLOATER)
		&& m->m_line == line1
		&& m->m_char > char1)
		{
			m->m_line = line2;
			m->m_char += char2 - char1;
		}
	}
}
