#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
#include <mach.h>
#define Extern extern
#include "acid.h"

static char *binop[NUMO];

static void
initbinop(void)
{
	binop[OMUL]=	"*";
	binop[ODIV]=	"/";
	binop[OMOD]=	"%";
	binop[OADD]=	"+";
	binop[OSUB]=	"-";
	binop[ORSH]=	">>";
	binop[OLSH]=	"<<";
	binop[OLT]=	"<";
	binop[OGT]=	">";
	binop[OLEQ]=	"<=";
	binop[OGEQ]=	">=";
	binop[OEQ]=	"==";
	binop[ONEQ]=	"!=";
	binop[OLAND]=	"&";
	binop[OXOR]=	"^";
	binop[OLOR]=	"|";
	binop[OCAND]=	"&&";
	binop[OCOR]=	"||";
	binop[OASGN]=	" = ";
}

static char *tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
char *typenames[] = {
	"integer",
	"float",
	"string",
	"list",
	"code"
};

void
initprint(void)
{
	initbinop();
}

int
cmp(const void *va, const void *vb)
{
	char **a = (char**)va;
	char **b = (char**)vb;

	return strcmp(*a, *b);
}

void
fundefs(void)
{
	Lsym *l;
	char **vec;
	int i, j, n, max, col, f, g, s;

	max = 0;
	f = 0;
	g = 100;
	vec = malloc(sizeof(char*)*g);
	if(vec == 0)
		fatal("out of memory");

	for(i = 0; i < Hashsize; i++) {
		for(l = hash[i]; l; l = l->hash) {
			if(l->proc == 0 && l->builtin == 0)
				continue;
			n = strlen(l->name);
			if(n > max)
				max = n;
			if(f >= g) {
				g *= 2;
				vec = realloc(vec, sizeof(char*)*g);
				if(vec == 0)
					fatal("out of memory");
			}
			vec[f++] = l->name;
		}
	}
        qsort(vec, f, sizeof(char*), cmp);
	max++;
	col = 60/max;
	s = (f+col-1)/col;

	for(i = 0; i < s; i++) {
		for(j = i; j < f; j += s)
			Bprint(bout, "%-*s", max, vec[j]);
		Bprint(bout, "\n");
	}
}

void
whatis(Lsym *l)
{
	int t;
	int def;
	Type *ti;

	if(l == 0) {
		fundefs();
		return;
	}

	def = 0;
	if(l->v->set) {
		t = l->v->type;
		Bprint(bout, "%s variable", typenames[t]);
		if(t == TINT || t == TFLOAT)
			Bprint(bout, " format %c", l->v->store.fmt);
		if(l->v->store.comt)
			Bprint(bout, " complex %s",
						l->v->store.comt->base->name);
		Bputc(bout, '\n');
		def = 1;
	}
	if(l->lt) {
		Bprint(bout, "complex %s {\n", l->name);
		for(ti = l->lt; ti; ti = ti->next) {
			if(ti->type) {
				if(ti->fmt == 'a') {
					Bprint(bout, "\t%s %d %s;\n",
					ti->type->name, ti->offset,
					ti->tag->name);
				}
				else {
					Bprint(bout, "\t'%c' %s %d %s;\n",
					ti->fmt, ti->type->name, ti->offset,
					ti->tag->name);
				}
			}
			else
				Bprint(bout, "\t'%c' %d %s;\n",
				ti->fmt, ti->offset, ti->tag->name);
		}
		Bprint(bout, "};\n");
		def = 1;
	}
	if(l->proc) {
		Bprint(bout, "defn %s(", l->name);
		pexpr(l->proc->left);
		Bprint(bout, ") {\n");
		pcode(l->proc->right, 1);
		Bprint(bout, "}\n");
		def = 1;
	}
	if(l->builtin) {
		Bprint(bout, "builtin function\n");
		def = 1;
	}
	if(def == 0)
		Bprint(bout, "%s is undefined\n", l->name);
}

void
slist(Node *n, int d)
{
	if(n == 0)
		return;
	if(n->op == OLIST)
		Bprint(bout, "%.*s{\n", d-1, tabs);
	pcode(n, d);
	if(n->op == OLIST)
		Bprint(bout, "%.*s}\n", d-1, tabs);
}

void
pcode(Node *n, int d)
{
	Node *r, *l;

	if(n == 0)
		return;

	r = n->right;
	l = n->left;

	switch(n->op) {
	default:
		Bprint(bout, "%.*s", d, tabs);
		pexpr(n);
		Bprint(bout, ";\n");
		break;
	case OLIST:
		pcode(n->left, d);
		pcode(n->right, d);
		break;
	case OLOCAL:
		Bprint(bout, "%.*slocal", d, tabs);
		while(l) {
			Bprint(bout, " %s", l->sym->name);
			l = l->left;
			if(l == 0)
				Bprint(bout, ";\n");
			else
				Bprint(bout, ",");
		}
		break;
	case OCOMPLEX:
		Bprint(bout, "%.*scomplex %s %s;\n", d, tabs, n->sym->name, l->sym->name);
		break;
	case OIF:
		Bprint(bout, "%.*sif ", d, tabs);
		pexpr(l);
		d++;
		Bprint(bout, " then\n");
		if(r && r->op == OELSE) {
			slist(r->left, d);
			Bprint(bout, "%.*selse\n", d-1, tabs);
			slist(r->right, d);
		}
		else
			slist(r, d);
		break;
	case OWHILE:
		Bprint(bout, "%.*swhile ", d, tabs);
		pexpr(l);
		d++;
		Bprint(bout, " do\n");
		slist(r, d);
		break;
	case ORET:
		Bprint(bout, "%.*sreturn ", d, tabs);
		pexpr(l);
		Bprint(bout, ";\n");
		break;
	case ODO:
		Bprint(bout, "%.*sloop ", d, tabs);
		pexpr(l->left);
		Bprint(bout, ", ");
		pexpr(l->right);
		Bprint(bout, " do\n");
		slist(r, d+1);
	}
}

void
pexpr(Node *n)
{
	Node *r, *l;

	if(n == 0)
		return;

	r = n->right;
	l = n->left;

	switch(n->op) {
	case ONAME:
		Bprint(bout, "%s", n->sym->name);
		break;
	case OCONST:
		switch(n->type) {
		case TINT:
			Bprint(bout, "%d", (int)n->store.u.ival);
			break;
		case TFLOAT:
			Bprint(bout, "%g", n->store.u.fval);
			break;
		case TSTRING:
			pstr(n->store.u.string);
			break;
		case TLIST:
			break;
		}
		break;
	case OMUL:
	case ODIV:
	case OMOD:
	case OADD:
	case OSUB:
	case ORSH:
	case OLSH:
	case OLT:
	case OGT:
	case OLEQ:
	case OGEQ:
	case OEQ:
	case ONEQ:
	case OLAND:
	case OXOR:
	case OLOR:
	case OCAND:
	case OCOR:
		Bputc(bout, '(');
		pexpr(l);
		Bprint(bout, binop[(uchar)n->op]);
		pexpr(r);
		Bputc(bout, ')');
		break;
	case OASGN:
		pexpr(l);
		Bprint(bout, binop[(uchar)n->op]);
		pexpr(r);
		break;
	case OINDM:
		Bprint(bout, "*");
		pexpr(l);
		break;
	case OEDEC:
		Bprint(bout, "--");
		pexpr(l);
		break;
	case OEINC:
		Bprint(bout, "++");
		pexpr(l);
		break;
	case OPINC:
		pexpr(l);
		Bprint(bout, "++");
		break;
	case OPDEC:
		pexpr(l);
		Bprint(bout, "--");
		break;
	case ONOT:
		Bprint(bout, "!");
		pexpr(l);
		break;
	case OLIST:
		pexpr(l);
		if(r) {
			Bprint(bout, ",");
			pexpr(r);
		}
		break;
	case OCALL:
		pexpr(l);
		Bprint(bout, "(");
		pexpr(r);
		Bprint(bout, ")");
		break;
	case OCTRUCT:
		Bprint(bout, "{");
		pexpr(l);
		Bprint(bout, "}");
		break;
	case OHEAD:
		Bprint(bout, "head ");
		pexpr(l);
		break;
	case OTAIL:
		Bprint(bout, "tail ");
		pexpr(l);
		break;
	case OAPPEND:
		Bprint(bout, "append ");
		pexpr(l);
		Bprint(bout, ",");
		pexpr(r);
		break;
	case ODELETE:
		Bprint(bout, "delete ");
		pexpr(l);
		Bprint(bout, ",");
		pexpr(r);
		break;
	case ORET:
		Bprint(bout, "return ");
		pexpr(l);
		break;
	case OINDEX:
		pexpr(l);
		Bprint(bout, "[");
		pexpr(r);
		Bprint(bout, "]");
		break;
	case OINDC:
		Bprint(bout, "@");
		pexpr(l);
		break;
	case ODOT:
		pexpr(l);
		Bprint(bout, ".%s", n->sym->name);
		break;
	case OFRAME:
		Bprint(bout, "%s:%s", n->sym->name, l->sym->name);
		break;
	case OCAST:
		Bprint(bout, "(%s)", n->sym->name);
		pexpr(l);
		break;
	case OFMT:
		pexpr(l);
		Bprint(bout, "\\%c", (int)r->store.u.ival);
		break;
	case OEVAL:
		Bprint(bout, "eval ");
		pexpr(l);
		break;
	case OWHAT:
		Bprint(bout, "whatis");
		if(n->sym)
			Bprint(bout, " %s", n->sym->name);
		break;
	case OUPLUS:
		Bprint(bout, "+");
		pexpr(l);
		break;
	}
}

void
pstr(String *s)
{
	int i, c;

	Bputc(bout, '"');
	for(i = 0; i < s->len; i++) {
		c = s->string[i];
		switch(c) {
		case '\0':
			c = '0';
			break;
		case '\n':
			c = 'n';
			break;
		case '\r':
			c = 'r';
			break;
		case '\t':
			c = 't';
			break;
		case '\b':
			c = 'b';
			break;
		case '\f':
			c = 'f';
			break;
		case '\a':
			c = 'a';
			break;
		case '\v':
			c = 'v';
			break;
		case '\\':
			c = '\\';
			break;
		case '"':
			c = '"';
			break;
		default:
			Bputc(bout, c);
			continue;
		}
		Bputc(bout, '\\');
		Bputc(bout, c);
	}
	Bputc(bout, '"');
}