/* Build a human readable string from an expression */
#include "evalint.h"
#include <math.h>
#include "support.h"
#include <string.h>

#undef strlen

/* When to add brackets */
/* It goes thus: in an expression of the form
  (a op0 b) op (c op1 d)
  bracket[op][op0] is TRUE if the brackets are necessary round the left expression
  bracket[op][op1] is TRUE if  "     "      "      "       "    "  right    "
*/
static char bracket[opsize][opsize][2] = {
    { { FALSE, FALSE }, /* a + b */
      { FALSE, FALSE },
      { FALSE, FALSE },
      { FALSE, FALSE },
      { FALSE, FALSE } },
    { { FALSE, TRUE },	/* a - b */
      { FALSE, TRUE },
      { FALSE, FALSE },
      { FALSE, FALSE },
      { FALSE, TRUE } },
    { { TRUE, TRUE },	/* a * b */
      { TRUE, TRUE },
      { FALSE, FALSE },
      { FALSE, FALSE },
      { FALSE, FALSE } },
    { { TRUE, TRUE },	/* a / b */
      { TRUE, TRUE },
      { FALSE, TRUE },
      { FALSE, TRUE },
      { FALSE, FALSE } },
    { { TRUE, TRUE },	/* a ^ b */
      { TRUE, TRUE },
      { TRUE, TRUE },
      { TRUE, TRUE },
      { FALSE, TRUE } }
};


static char *namefunc(func)
FUNCTION func;
{
    onefunc *f = locate(func);

    if (f) return(f->functext);
    else return(NULL);
}

/* decompile expr into str. len is the length of str */
/* If there is not enough place return 0, otherwise the number of chars used */
static int _decompile(expr, str, len)
register value expr;
char *str;
int len;
{
    switch (expr->type)
    {
	case cste: {
	    register int l;
	    char buf[30];

	    l = strlen(gcvt(expr->cte, 4, buf));
	    if (l >= len) return(0);
	    strcpy(str, buf);
	    return(l);
	}
	case fonc: {
	    char *tmp;
	    register int l, nl;

	    /* Special treatment for -(expression) */
	    if (expr->fn.func == neg)
	    {
		int brackets;

		/* brackets necessary for -(-x) and -(a op b) if necessary for 0 - (a op b) */
		brackets = expr->fn.arg->type == fonc && expr->fn.arg->fn.func == neg ||
			   expr->fn.arg->type == op && bracket[minus][expr->fn.arg->opr.op][1];
		len -= 1 + 2 * brackets;
		if (len <= 0) return(0);
		*(str++) = '-';
		if (brackets) *(str++) = '(';
		nl = _decompile(expr->fn.arg, str, len);
		if (nl == 0) return(0);
		str += nl;
		if (brackets) *(str++) = ')';
		*str = '\0';
		return(nl + 1 + 2 * brackets);
	    }
	    else
	    {
		tmp = namefunc(expr->fn.func);
		if (!tmp) return(0);
		l = strlen(tmp);
		if (l >= len - 2) return(0); /* no place */
		strcpy(str, tmp);
		*(str += l) = '(';
		nl = _decompile(expr->fn.arg, ++str, len - l - 2);
		if (nl == 0) return(0);
		*(str += nl) = ')';
		*++str = '\0';
		return(l + nl + 2);
	    }
	}
	case var: {
	    register int l;

	    l = strlen(expr->vr.name);
	    if (l >= len) return(0);
	    strcpy(str, expr->vr.name);
	    return(l);
	}
	case op: {
	    e_op e_op;
	    register int bracket1, bracket2;
	    register int l1, l2;

	    /* Check for brackets */
	    e_op = expr->opr.op;
	    bracket1 = expr->opr.arg1->type == op &&
		       bracket[e_op][expr->opr.arg1->opr.op][0];
	    bracket2 = expr->opr.arg2->type == op &&
		       bracket[e_op][expr->opr.arg2->opr.op][1];
	    len -= 2 * bracket1 + 2 * bracket2 + 1;
	    if (len <= 0) return(0);

	    if (bracket1) *str++ = '(';
	    l1 = _decompile(expr->opr.arg1, str, len);
	    if (l1 == 0) return(0);
	    str += l1;
	    len -= l1;
	    if (bracket1) *str++ = ')';

	    *str++ = "+-*/^"[e_op];

	    if (bracket2) *str++ = '(';
	    l2 = _decompile(expr->opr.arg2, str, len);
	    if (l2 == 0) return(0);
	    str += l2;
	    if (bracket2)
	    {
		*str++ = ')';
		*str = '\0';
	    }

	    return(2 * bracket1 + l1 + 1 + 2 * bracket2 + l2);
	}
    }
}

char *decompile(expr, str, len)
value expr;
char *str;
int len;
{
    if (_decompile(expr, str, len) == 0) return(NULL);
    else return(str);
}

