#include "sam.h"

void
moveto(File *f, Range r)
{
	Posn p1 = r.p1, p2 = r.p2;

	f->dot.r.p1 = p1;
	f->dot.r.p2 = p2;
	if(f->rasp){
		telldot(f);
		outTsl(Hmoveto, f->tag, f->dot.r.p1);
	}
}

void
telldot(File *f)
{
	if(f->rasp == 0)
		panic("telldot");
	if(f->dot.r.p1==f->tdot.p1 && f->dot.r.p2==f->tdot.p2)
		return;
	outTsll(Hsetdot, f->tag, f->dot.r.p1, f->dot.r.p2);
	f->tdot = f->dot.r;
}

void
tellpat(void)
{
	outTS(Hsetpat, &lastpat);
	patset = FALSE;
}

#define	CHARSHIFT	128

void
lookorigin(File *f, Posn p0, Posn ls)
{
	int nl, nc, c;
	Posn p, oldp0;

	if(p0 > f->nc)
		p0 = f->nc;
	oldp0 = p0;
	p = p0;
	for(nl=nc=c=0; c!=-1 && nl<ls && nc<ls*CHARSHIFT; nc++)
		if((c=filereadc(f, --p)) == '\n'){
			nl++;
			oldp0 = p0-nc;
		}
	if(c == -1)
		p0 = 0;
	else if(nl==0){
		if(p0>=CHARSHIFT/2)
			p0-=CHARSHIFT/2;
		else
			p0 = 0;
	}else
		p0 = oldp0;
	outTsl(Horigin, f->tag, p0);
}

int
isalnum(int c)
{
	/*
	 * Hard to get absolutely right.  Use what we know about ASCII
	 * and assume anything above the Latin control characters is
	 * potentially an alphanumeric.
	 */
	if(c<=' ')
		return 0;
	if(0x7F<=c && c<=0xA0)
		return 0;
	if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
		return 0;
	return 1;
}

int
isspace(Rune c)
{
	return c == 0 || c == ' ' || c == '\t' ||
		c == '\n' || c == '\r' || c == '\v';
}

int
inmode(Rune r, int mode)
{
	return (mode == 0) ? isalnum(r) : r && !isspace(r);
}

int
clickmatch(File *f, int cl, int cr, int dir, Posn *p)
{
	int c;
	int nest = 1;

	for(;;){
		if(dir > 0){
			if(*p >= f->nc)
				break;
			c = filereadc(f, (*p)++);
		}else{
			if(*p == 0)
				break;
			c = filereadc(f, --(*p));
		}
		if(c == cr){
			if(--nest==0)
				return 1;
		}else if(c == cl)
			nest++;
	}
	return cl=='\n' && nest==1;
}

Rune*
strrune(Rune *s, Rune c)
{
	Rune c1;

	if(c == 0) {
		while(*s++)
			;
		return s-1;
	}

	while(c1 = *s++)
		if(c1 == c)
			return s-1;
	return 0;
}

/*
 * Stretches a selection out over current text,
 * selecting matching range if possible.
 * If there's no matching range, mode 0 selects
 * a single alphanumeric region. Mode 1 selects
 * a non-whitespace region.
 */
void
stretchsel(File *f, Posn p1, int mode)
{
	int c, i, lc, rc;
	Rune *r, *l, *x;
	Posn p;

	if(p1 > f->nc)
		return;
	if(mode){
		lc = f->dot.r.p1 > 0     ? filereadc(f, f->dot.r.p1-1) : '\n';
		rc = f->dot.r.p2 < f->nc ? filereadc(f, f->dot.r.p2)   : '\n';
		for(i=0; left[i]; i++){
			l = left[i];
			r = right[i];
			x = strrune(l, lc);
			if(x && r[x-l] == rc){
				f->dot.r.p1 -= f->dot.r.p1 > 0 && lc != '\n';
				f->dot.r.p2 += f->dot.r.p2 < f->nc;
				return;
			}
		}
	}

	f->dot.r.p1 = f->dot.r.p2 = p1;
	for(i=0; left[i]; i++){
		l = left[i];
		r = right[i];
		/* try left match */
		p = p1;
		if(p1 == 0)
			c = '\n';
		else
			c = filereadc(f, p - 1);
		if(strrune(l, c)){
			if(clickmatch(f, c, r[strrune(l, c)-l], 1, &p)){
				f->dot.r.p1 = p1;
				f->dot.r.p2 = p-(c!='\n');
			}
			return;
		}
		/* try right match */
		p = p1;
		if(p1 == f->nc)
			c = '\n';
		else
			c = filereadc(f, p);
		if(strrune(r, c)){
			if(clickmatch(f, c, l[strrune(r, c)-r], -1, &p)){
				f->dot.r.p1 = p;
				if(c!='\n' || p!=0 || filereadc(f, 0)=='\n')
					f->dot.r.p1++;
				f->dot.r.p2 = p1+(p1<f->nc && c=='\n');
			}
			return;
		}
	}
	/* try filling out word to right */
	p = p1;
	while(p < f->nc && inmode(filereadc(f, p++), mode))
		f->dot.r.p2++;
	/* try filling out word to left */
	p = p1;
	while(--p >= 0 && inmode(filereadc(f, p), mode))
		f->dot.r.p1--;
}

