#include "sam.h"
#include "parse.h"

Address	addr;
String	lastpat;
int	patset;
File	*menu;

File	*matchfile(String*);
Address	charaddr(Posn, Address, int);

Address
address(Addr *ap, Address a, int sign)
{
	File *f = a.f;
	Address a1, a2;

	do{
		switch(ap->type){
		case 'l':
		case '#':
			a = (*(ap->type=='#'?charaddr:lineaddr))(ap->num, a, sign);
			break;

		case '.':
			a = f->dot;
			break;

		case '$':
			a.r.p1 = a.r.p2 = f->nc;
			break;

		case '\'':
			a.r = f->mark;
			break;

		case '?':
			sign = -sign;
			if(sign == 0)
				sign = -1;
			/* fall through */
		case '/':
			nextmatch(f, ap->are, sign>=0? a.r.p2 : a.r.p1, sign);
			a.r = sel.p[0];
			break;

		case '"':
			a = matchfile(ap->are)->dot;
			f = a.f;
			if(f->unread)
				load(f);
			break;

		case '*':
			a.r.p1 = 0, a.r.p2 = f->nc;
			return a;

		case ',':
		case ';':
			if(ap->left)
				a1 = address(ap->left, a, 0);
			else
				a1.f = a.f, a1.r.p1 = a1.r.p2 = 0;
			if(ap->type == ';'){
				f = a1.f;
				a = a1;
				f->dot = a1;
			}
			if(ap->next)
				a2 = address(ap->next, a, 0);
			else
				a2.f = a.f, a2.r.p1 = a2.r.p2 = f->nc;
			if(a1.f != a2.f)
				error(Eorder);
			a.f = a1.f, a.r.p1 = a1.r.p1, a.r.p2 = a2.r.p2;
			if(a.r.p2 < a.r.p1)
				error(Eorder);
			return a;

		case '+':
		case '-':
			sign = 1;
			if(ap->type == '-')
				sign = -1;
			if(ap->next==0 || ap->next->type=='+' || ap->next->type=='-')
				a = lineaddr(1L, a, sign);
			break;
		default:
			panic("address");
			return a;
		}
	}while(ap = ap->next);	/* assign = */
	return a;
}

void
nextmatch(File *f, String *r, Posn p, int sign)
{
	compile(r);
	if(sign >= 0){
		if(!execute(f, p, INFINITY))
			error(Esearch);
		if(sel.p[0].p1==sel.p[0].p2 && sel.p[0].p1==p){
			if(++p>f->nc)
				p = 0;
			if(!execute(f, p, INFINITY))
				panic("address");
		}
	}else{
		if(!bexecute(f, p))
			error(Esearch);
		if(sel.p[0].p1==sel.p[0].p2 && sel.p[0].p2==p){
			if(--p<0)
				p = f->nc;
			if(!bexecute(f, p))
				panic("address");
		}
	}
}

File *
matchfile(String *r)
{
	File *f;
	File *match = 0;
	int i;

	for(i = 0; i<file.nused; i++){
		f = file.filepptr[i];
		if(f == cmd)
			continue;
		if(filematch(f, r)){
			if(match)
				error(Emanyfiles);
			match = f;
		}
	}
	if(!match)
		error(Efsearch);
	return match;
}

int
filematch(File *f, String *r)
{
	char *c, *s;
	String *t;

	c = Strtoc(&f->name);
	s = smprint("%c%c%c %s\n", " '"[f->mod],
		"-+"[f->rasp!=0], " ."[f==curfile], c);
	if(s == nil)
		error(Etoolong);
	free(c);
	t = tmpcstr(s);
	Strduplstr(&genstr, t);
	freetmpstr(t);
	/* A little dirty... */
	if(menu == 0)
		menu = fileopen();
	bufreset(menu);
	bufinsert(menu, 0, genstr.s, genstr.n);
	compile(r);
	free(s);
	return execute(menu, 0, menu->nc);
}

Address
charaddr(Posn l, Address addr, int sign)
{
	if(sign == 0)
		addr.r.p1 = addr.r.p2 = l;
	else if(sign < 0)
		addr.r.p2 = addr.r.p1-=l;
	else if(sign > 0)
		addr.r.p1 = addr.r.p2+=l;
	if(addr.r.p1<0 || addr.r.p2>addr.f->nc)
		error(Erange);
	return addr;
}

Address
lineaddr(Posn l, Address addr, int sign)
{
	int n;
	int c;
	File *f = addr.f;
	Address a;
	Posn p;

	a.f = f;
	if(sign >= 0){
		if(l == 0){
			if(sign==0 || addr.r.p2==0){
				a.r.p1 = a.r.p2 = 0;
				return a;
			}
			a.r.p1 = addr.r.p2;
			p = addr.r.p2-1;
		}else{
			if(sign==0 || addr.r.p2==0){
				p = (Posn)0;
				n = 1;
			}else{
				p = addr.r.p2-1;
				n = filereadc(f, p++)=='\n';
			}
			while(n < l){
				if(p >= f->nc)
					error(Erange);
				if(filereadc(f, p++) == '\n')
					n++;
			}
			a.r.p1 = p;
		}
		while(p < f->nc && filereadc(f, p++)!='\n')
			;
		a.r.p2 = p;
	}else{
		p = addr.r.p1;
		if(l == 0)
			a.r.p2 = addr.r.p1;
		else{
			for(n = 0; n<l; ){	/* always runs once */
				if(p == 0){
					if(++n != l)
						error(Erange);
				}else{
					c = filereadc(f, p-1);
					if(c != '\n' || ++n != l)
						p--;
				}
			}
			a.r.p2 = p;
			if(p > 0)
				p--;
		}
		while(p > 0 && filereadc(f, p-1)!='\n')	/* lines start after a newline */
			p--;
		a.r.p1 = p;
	}
	return a;
}
