#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#include <ctype.h>
#include "dat.h"

char *prefix = "";

static TypeList *thash[1021];
static TypeList *namehash[1021];
static TypeList *alltypes;

static uint
hash(uint num, uint num1)
{
	return (num*1009 + num1*1013) % nelem(thash);
}

static uint
shash(char *s)
{
	uint h;

	h = 0;
	for(; *s; s++)
		h = 37*h + *s;
	return h%nelem(namehash);
}

void
addnamehash(Type *t)
{
	uint h;

	if(t->name){
		h = shash(t->name);
		namehash[h] = mktl(t, namehash[h]);
	}
}

static void
addhash(Type *t)
{
	uint h;

	if(t->n1 || t->n2){
		h = hash(t->n1, t->n2);
		thash[h] = mktl(t, thash[h]);
	}
	if(t->name)
		addnamehash(t);
}

Type*
typebysue(char sue, char *name)
{
	Type *t;
	TypeList *tl;

	for(tl=namehash[shash(name)]; tl; tl=tl->tl){
		t = tl->hd;
		if(t->sue==sue && t->suename && strcmp(name, t->suename)==0)
			return t;
	}
	t = newtype();
	if(sue=='e')
		t->ty = Enum;
	else
		t->ty = Aggr;
	if(sue=='u')
		t->isunion = 1;
	t->sue = sue;
	t->suename = name;
	addnamehash(t);
	return t;
}

Type*
typebynum(uint n1, uint n2)
{
	Type *t;
	TypeList *tl;

	if(n1 || n2){
		for(tl=thash[hash(n1, n2)]; tl; tl=tl->tl){
			t = tl->hd;
			if(t->n1==n1 && t->n2==n2)
				return t;
		}
	}

	t = newtype();
	t->n1 = n1;
	t->n2 = n2;
	addhash(t);
	return t;
}	

Type*
newtype(void)
{
	Type *t;
	static int gen;

	t = emalloc(sizeof *t);
	t->gen = ++gen;
	alltypes = mktl(t, alltypes);
	return t;
}

struct {
	char *old;
	char *new;
} fixes[] = { /* Font Tab 4 */
	"append",		"$append",
	"builtin",		"$builtin",
	"complex",		"$complex",
	"delete",		"$delete",
	"do",			"$do",
	"else",			"$else",
	"eval",			"$eval",
	"fmt",			"$fmt",
	"fn",			"$fn",
	"head",			"$head",
	"if",			"$if",
	"local",		"$local",
	"loop",			"$loop",
	"ret",			"$ret",
	"tail",			"$tail",
	"then",			"$then",
	"whatis",			"$whatis",
	"while",		"$while",
};

char*
fixname(char *name)
{
	int i;

	if(name == nil)
		return nil;
	for(i=0; i<nelem(fixes); i++)
		if(name[0]==fixes[i].old[0] && strcmp(name, fixes[i].old)==0)
			return fixes[i].new;
	return name;
}

void
denumber(void)
{
	memset(thash, 0, sizeof thash);
	memset(namehash, 0, sizeof namehash);
}

void
renumber(TypeList *tl, uint n1)
{
	Type *t;

	for(; tl; tl=tl->tl){
		t = tl->hd;
		t->n1 = n1;
		addhash(t);
	}
}

Type*
defer(Type *t)
{
	Type *u, *oldt;
	int n;

	u = t;
	n = 0;
	oldt = t;
	while(t && (t->ty == Defer || t->ty == Typedef)){
		if(n++%2)
			u = u->sub;
		t = t->sub;
		if(t == u)	/* cycle */
			goto cycle;
	}
	return t;

cycle:
	fprint(2, "cycle\n");
	t = oldt;
	n = 0;
	while(t && (t->ty==Defer || t->ty==Typedef)){
		fprint(2, "t %p/%d %s\n", t, t->ty, t->name);
		if(t == u && n++ == 2)
			break;
		t = t->sub;
	}
	return u;
}

static void
dotypedef(Type *t)
{
	if(t->ty != Typedef && t->ty != Defer)
		return;

	if(t->didtypedef)
		return;

	t->didtypedef = 1;
	if(t->sub == nil)
		return;

	/* push names downward to remove anonymity */
	if(t->name && t->sub->name == nil)
		t->sub->name = t->name;

	dotypedef(t->sub);
}

static int
countbytes(uvlong x)
{
	int n;

	for(n=0; x; n++)
		x>>=8;
	return n;
}

static void
dorange(Type *t)
{
	Type *tt;

	if(t->ty != Range)
		return;
	if(t->didrange)
		return;
	t->didrange = 1;
	tt = defer(t->sub);
	if(tt == nil)
		return;
	dorange(tt);
	if(t != tt && tt->ty != Base)
		return;
	t->ty = Base;
	t->xsizeof = tt->xsizeof;
	if(t->lo == 0)
		t->printfmt = 'x';
	else
		t->printfmt = 'd';
	if(t->xsizeof == 0)
		t->xsizeof = countbytes(t->hi);
}


char*
nameof(Type *t, int doanon)
{
	static char buf[1024];
	char *p;

	if(t->name)
		strcpy(buf, fixname(t->name));
	else if(t->suename)
		snprint(buf, sizeof buf, "%s_%s", t->isunion ? "union" : "struct", t->suename);
	else if(doanon)
		snprint(buf, sizeof buf, "%s_%lud_", prefix, t->gen);
	else
		return "";
	for(p=buf; *p; p++)
		if(isspace(*p))
			*p = '_';
	return buf;
}

static char
basecharof(Type *t)	//XXX
{
	switch(t->xsizeof){
	default:
		return 'X';
	case 1:
		return 'b';
	case 2:
		if(t->printfmt=='d')
			return 'd';
		else
			return 'x';
	case 4:
		if(t->printfmt=='d')
			return 'D';
		else if(t->printfmt=='f')
			return 'f';
		else
			return 'X';
	case 8:
		if(t->printfmt=='d')
			return 'V';
		else if(t->printfmt=='f')
			return 'F';
		else
			return 'Y';
	}
}

static int
nilstrcmp(char *a, char *b)
{
	if(a == b)
		return 0;
	if(a == nil)
		return -1;
	if(b == nil)
		return 1;
	return strcmp(a, b);
}

int careaboutaggrcount;

static int
typecmp(Type *t, Type *u)
{
	int i;

	if(t == u)
		return 0;
	if(t == nil)
		return -1;
	if(u == nil)
		return 1;

	if(t->ty < u->ty)
		return -1;
	if(t->ty > u->ty)
		return 1;

	if(t->isunion != u->isunion)
		return t->isunion - u->isunion;

	i = nilstrcmp(t->name, u->name);
	if(i)
		return i;

	i = nilstrcmp(t->suename, u->suename);
	if(i)
		return i;

	if(careaboutaggrcount && t->ty == Aggr){
		if(t->n > u->n)
			return -1;
		if(t->n < u->n)
			return 1;
	}

	if(t->name || t->suename)
		return 0;

	if(t->ty==Enum){
		if(t->n < u->n)
			return -1;
		if(t->n > u->n)
			return 1;
		if(t->n == 0)
			return 0;
		i = strcmp(t->tname[0], u->tname[0]);
		return i;
	}
	if(t < u)
		return -1;
	if(t > u)
		return 1;
	return 0;
}

static int
qtypecmp(const void *va, const void *vb)
{
	Type *t, *u;

	t = *(Type**)va;
	u = *(Type**)vb;
	return typecmp(t, u);
}

void
printtype(Biobuf *b, Type *t)
{
	char *name;
	int j, nprint;
	Type *tt, *ttt;

	if(t->printed)
		return;
	t->printed = 1;
	switch(t->ty){
	case Aggr:
		name = nameof(t, 1);
		Bprint(b, "sizeof%s = %lud;\n", name, t->xsizeof);
		Bprint(b, "aggr %s {\n", name);
		nprint = 0;
		for(j=0; j<t->n; j++){
			tt = defer(t->t[j]);
			if(tt && tt->equiv)
				tt = tt->equiv;
			if(tt == nil){
				Bprint(b, "// oops: nil type\n");
				continue;
			}
			switch(tt->ty){
			default:
				Bprint(b, "// oops: unknown type %d for %p/%s (%d,%d; %c,%s; %p)\n",
					tt->ty, tt, fixname(t->tname[j]),
					tt->n1, tt->n2, tt->sue ? tt->sue : '.', tt->suename, tt->sub);
Bprint(b, "// t->t[j] = %p\n", ttt=t->t[j]);
while(ttt){
Bprint(b, "// %s %d (%d,%d) sub %p\n", ttt->name, ttt->ty, ttt->n1, ttt->n2, ttt->sub);
ttt=ttt->sub;
}
			case Base:
			case Pointer:
			case Enum:
			case Array:
			case Function:
				nprint++;
				Bprint(b, "\t'%c' %lud %s;\n", basecharof(tt), t->val[j], fixname(t->tname[j]));
				break;
			case Aggr:
				nprint++;
				Bprint(b, "\t%s %lud %s;\n", nameof(tt, 1), t->val[j], fixname(t->tname[j]));
				break;
			}
		}
		if(nprint == 0)
			Bprint(b, "\t'X' 0 __dummy;\n");
		Bprint(b, "};\n\n");
	
		name = nameof(t, 1);	/* might have smashed it */
		Bprint(b, "defn %s(addr) { indent_%s(addr, \"\"); }\n", name, name);
		Bprint(b, "defn\nindent_%s(addr, indent) {\n", name);
		Bprint(b, "\tcomplex %s addr;\n", name);
		for(j=0; j<t->n; j++){
			name = fixname(t->tname[j]);
			tt = defer(t->t[j]);
			if(tt == nil){
				Bprint(b, "// oops nil %s\n", name);
				continue;
			}
			switch(tt->ty){
			case Base:
			base:
				Bprint(b, "\tprint(indent, \"%s\t\", addr.%s, \"\\n\");\n",
					name, name);
				break;
			case Pointer:
				ttt = defer(tt->sub);
				if(ttt && ttt->ty == Aggr)
					Bprint(b, "\tprint(indent, \"%s\t(%s)\", addr.%s, \"\\n\");\n",
						name, nameof(ttt, 1), name);
				else
					goto base;
				break;
			case Array:
				Bprint(b, "\tprint(indent, \"%s\t\", addr.%s\\X, \"\\n\");\n",
					name, name);
				break;
			case Enum:
				Bprint(b, "\tprint(indent, \"%s\t\", addr.%s, \" \", %s(addr.%s), \"\\n\");\n",
					name, name, nameof(tt, 1), name);
				break;
			case Aggr:
				Bprint(b, "\tprint(indent, \"%s\t%s{\\n\");\n",
					name, nameof(tt, 0));
				Bprint(b, "\tindent_%s(addr+%lud, indent+\"  \");\n",
					nameof(tt, 1), t->val[j]);
				Bprint(b, "\tprint(indent, \"}\\n\");\n");
				break;
			}
		}
		Bprint(b, "};\n\n");
		break;
	
	case Enum:
		name = nameof(t, 1);
		Bprint(b, "// enum %s\n", name);
		for(j=0; j<t->n; j++)
			Bprint(b, "%s = %ld;\n", fixname(t->tname[j]), t->val[j]);
	
		Bprint(b, "vals_%s = {\n", name);
		for(j=0; j<t->n; j++)
			Bprint(b, "\t%lud,\n", t->val[j]);
		Bprint(b, "};\n");
		Bprint(b, "names_%s = {\n", name);
		for(j=0; j<t->n; j++)
			Bprint(b, "\t\"%s\",\n", fixname(t->tname[j]));
		Bprint(b, "};\n");
		Bprint(b, "defn\n%s(val) {\n", name);
		Bprint(b, "\tlocal i;\n");
		Bprint(b, "\ti = match(val, vals_%s);\n", name);
		Bprint(b, "\tif i >= 0 then return names_%s[i];\n", name);
		Bprint(b, "\treturn \"???\";\n");
		Bprint(b, "};\n");
		break;
	}
}

void
printtypes(Biobuf *b)
{
	int i, n, nn;
	Type *t, *tt, **all;
	TypeList *tl;

	/* check that pointer resolved */
	for(tl=alltypes; tl; tl=tl->tl){
		t = tl->hd;
		if(t->ty==None){
			if(t->n1 || t->n2)
				warn("type %d,%d referenced but not defined", t->n1, t->n2);
			else if(t->sue && t->suename)
				warn("%s %s referenced but not defined",
					t->sue=='s' ? "struct" :
					t->sue=='u' ? "union" :
					t->sue=='e' ? "enum" : "???", t->suename);
		}
	}

	/* push typedefs down, base types up */
	n = 0;
	for(tl=alltypes; tl; tl=tl->tl){
		n++;
		t = tl->hd;
		if(t->ty == Typedef || t->ty == Defer)
			dotypedef(t);
	}

	/* push ranges around */
	for(tl=alltypes; tl; tl=tl->tl)
		dorange(tl->hd);

	/*
	 * only take one type of a given name; acid is going to do this anyway,
	 * and this will reduce the amount of code we output considerably.
	 * we could run a DFA equivalence relaxation sort of algorithm
	 * to find the actual equivalence classes, and then rename types 
	 * appropriately, but this will do for now.
	 */
	all = emalloc(n*sizeof(all[0]));
	n = 0;
	for(tl=alltypes; tl; tl=tl->tl)
		all[n++] = tl->hd;

	careaboutaggrcount = 1;
	qsort(all, n, sizeof(all[0]), qtypecmp);
	careaboutaggrcount = 0;

	nn = 0;
	for(i=0; i<n; i++){
		if(i==0 || typecmp(all[i-1], all[i]) != 0)
			all[nn++] = all[i];
		else
			all[i]->equiv = all[nn-1];
	}

	for(tl=alltypes; tl; tl=tl->tl){
		t = tl->hd;
		tt = defer(t);
		if(tt && tt->equiv)
			t->equiv = tt->equiv;
	}

	for(i=0; i<nn; i++)
		printtype(b, all[i]);

	free(all);
}

void
freetypes(void)
{
	memset(thash, 0, sizeof(thash));
	memset(namehash, 0, sizeof namehash);
}
