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

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

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

int
cmp(void *va, void *vb)
{
	char **a = va;
	char **b = 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");
	}
	free(vec);
}

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->fmt);
		if(l->v->comt)
			Bprint(bout, " complex %s", l->v->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, "%lld", n->ival);
			break;
		case TFLOAT:
			Bprint(bout, "%g", n->fval);
			break;
		case TSTRING:
			pstr(n->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[n->op]);
		pexpr(r);
		Bputc(bout, ')');
		break;
	case OASGN:
		pexpr(l);
		Bprint(bout, binop[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->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;
	}
}

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, '"');
}
