/*
 *	Math
 *	Copyright
 *		(C) 1992 Joseph H. Allen
 *
 *	This file is part of JOE (Joe's Own Editor)
 */
#include "config.h"
#include "types.h"

#include <stdio.h>
#include <string.h>

#include "b.h"
#include "pw.h"
#include "utils.h"
#include "vs.h"
#include "w.h"

char *merr;

static RETSIGTYPE fperr(int unused)
{
	if (!merr) {
		merr = "Float point exception";
	}
	REINSTALL_SIGHANDLER(SIGFPE, fperr);
}

struct var {
	char *name;
	int set;
	double val;
	struct var *next;
} *vars = NULL;

static struct var *get(char *str)
{
	struct var *v;

	for (v = vars; v; v = v->next) {
		if (!strcmp(v->name, str)) {
			return v;
		}
	}
	v = (struct var *) joe_malloc(sizeof(struct var));

	v->set = 0;
	v->next = vars;
	vars = v;
	v->name = strdup(str);
	return v;
}

char *ptr;
struct var *dumb;

static double expr(int prec, struct var **rtv)
{
	double x = 0.0;
	struct var *v = NULL;

	while (*ptr == ' ' || *ptr == '\t') {
		++ptr;
	}
	if ((*ptr >= 'a' && *ptr <= 'z') || (*ptr >= 'A' && *ptr <= 'Z')
	    || *ptr == '_') {
		char *s = ptr, c;

		while ((*ptr >= 'a' && *ptr <= 'z')
		       || (*ptr >= 'A' && *ptr <= 'Z')
		       || *ptr == '_' || (*ptr >= '0' && *ptr <= '9')) {
			++ptr;
		}
		c = *ptr;
		*ptr = 0;
		v = get(s);
		x = v->val;
		*ptr = c;
	} else if ((*ptr >= '0' && *ptr <= '9') || *ptr == '.') {
		sscanf(ptr, "%lf", &x);
		while ((*ptr >= '0' && *ptr <= '9') || *ptr == '.' || *ptr == 'e' || *ptr == 'E')
			++ptr;
	} else if (*ptr == '(') {
		++ptr;
		x = expr(0, &v);
		if (*ptr == ')')
			++ptr;
		else {
			if (!merr)
				merr = "Missing )";
		}
	} else if (*ptr == '-') {
		++ptr;
		x = -expr(10, &dumb);
	}
      loop:
	while (*ptr == ' ' || *ptr == '\t')
		++ptr;
	if (*ptr == '*' && 5 > prec) {
		++ptr;
		x *= expr(5, &dumb);
		goto loop;
	} else if (*ptr == '/' && 5 > prec) {
		++ptr;
		x /= expr(5, &dumb);
		goto loop;
	} else if (*ptr == '+' && 4 > prec) {
		++ptr;
		x += expr(4, &dumb);
		goto loop;
	} else if (*ptr == '-' && 4 > prec) {
		++ptr;
		x -= expr(4, &dumb);
		goto loop;
	} else if (*ptr == '=' && 2 >= prec) {
		++ptr;
		x = expr(2, &dumb);
		if (v) {
			v->val = x;
			v->set = 1;
		} else {
			if (!merr)
				merr = "Left side of = is not an l-value";
		}
		goto loop;
	}
	*rtv = v;
	return x;
}

double calc(BW *bw, char *s)
{
	double result;
	struct var *v;
	BW *tbw = bw->parent->main->object;

	v = get("top");
	v->val = tbw->top->line + 1;
	v->set = 1;
	v = get("lines");
	v->val = tbw->b->eof->line + 1;
	v->set = 1;
	v = get("line");
	v->val = tbw->cursor->line + 1;
	v->set = 1;
	v = get("col");
	v->val = tbw->cursor->col + 1;
	v->set = 1;
	v = get("byte");
	v->val = tbw->cursor->byte + 1;
	v->set = 1;
	v = get("height");
	v->val = tbw->h;
	v->set = 1;
	v = get("width");
	v->val = tbw->w;
	v->set = 1;
	ptr = s;
	merr = 0;
      up:
	result = expr(0, &dumb);
	if (!merr) {
		while (*ptr == ' ' || *ptr == '\t') {
			++ptr;
		}
		if (*ptr == ';') {
			++ptr;
			while (*ptr == ' ' || *ptr == '\t') {
				++ptr;
			}
			if (*ptr) {
				goto up;
			}
		} else if (*ptr) {
			merr = "Extra junk after end of expr";
		}
	}
	return result;
}

/* Main user interface */
static int domath(BW *bw, char *s, void *object, int *notify)
{
	double result = calc(bw, s);

	if (notify) {
		*notify = 1;
	}
	if (merr) {
		msgnw(bw->parent, merr);
		return -1;
	}
	vsrm(s);
	snprintf(msgbuf, JOE_MSGBUFSIZE, "%G", result);
	if (bw->parent->watom->what != TYPETW) {
		binsm(bw->cursor, sz(msgbuf));
		pfwrd(bw->cursor, strlen(msgbuf));
		bw->cursor->xcol = piscol(bw->cursor);
	} else {
		msgnw(bw->parent, msgbuf);
	}
	return 0;
}

B *mathhist = NULL;

int umath(BW *bw)
{
	joe_set_signal(SIGFPE, fperr);
	if (wmkpw(bw->parent, "=", &mathhist, domath, "math", NULL, NULL, NULL, NULL)) {
		return 0;
	} else {
		return -1;
	}
}
