#ifndef RENESASBOARD
#include <stdio.h>
#include <stdlib.h>
#endif /*RENESASBOARD*/
#include <math.h>
#include <assert.h>

#include "engine.h"
#include "heap.h"
#include "show.h"
#include "debug.h"
#include "within.h"

#define STACKSIZE 18
#define HEAPSIZE 42
#define WIRESIZE 152
#define nWires 5
#define nBoxes 3
#define nCons 0

uint16 wireCnt() { return nWires; }
uint16 boxCnt() { return nBoxes; }

wire_t wire[nWires];
box_t box[nBoxes];
STACK stack[STACKSIZE];
HEAP heap[HEAPSIZE + WIRESIZE]; /* box uses the bottom, while wires steal memory from the top */

#ifdef SUPPORT_DEBUG
const char *boxName[nBoxes] = {
	"itermult",
	"mult2",
	"output"
};
const char *wireName[nWires] = {
	"itermult0->itermult1",
	"mult21->itermult0",
	"mult22->output0",
	"itermult1->mult21",
	"mult20->mult20"
};
const char *getBoxName(box_t *b) { return boxName[b - box]; }
const char *getWireName(wire_t *w) { return wireName[w - wire]; }
const char *getConName(uint16 c) { return "?"; }
#else
const char *getBoxName(box_t *b) { return NULL; }
const char *getWireName(wire_t *w) { return NULL; }
const char *getConName(uint16 c) { return NULL; }
#endif /*SUPPORT_DEBUG*/

/* Constant Strings */
const char *str_0 = "\n";

int exitCode;
char **arguments; /* NULL terminated */
extern char **environ; /* NULL terminated */

#include <unistd.h> /* for STDOUT_FILENO */
int16 fd0 = STDOUT_FILENO;
void ioGrpInit0() {/*nothing*/ }
#include <unistd.h> /* for STDERR_FILENO */
void ioInit2() {
	ioGrpInit0(); /* @TODO check the fd0 and panic if -1 */
}
void ioOutput2() {
	HEAP *ptr = wire[2].available;
	if(ptr->tag == TSTR) {
		char *str = (char*)(ptr+1);
		int len = strlen(str);
		if(write(fd0, str, len) != len) {
			write(STDERR_FILENO, "<partial write>\n", 16);
			exit(1);
		}
	} else {
		char buff[1000001];
		int len = textValue(buff, 1000000, ptr);
		if(len != -1) {
			buff[len] = '\0';
			if(write(fd0, buff, len) != len) {
				write(STDERR_FILENO, "<partial write>\n", 16);
				exit(1);
			}
		} else {
			write(fd0, "<data show overflow>\n", 21);
			write(STDERR_FILENO, "<data show overflow>\n", 21);
			exit(1);
		}
	}
	wireConsume(wire+2);
}

/*
 * prepare the engine
 *
 */
void engineInit() {
	uint16 i;
	hpLimit = heap + HEAPSIZE + WIRESIZE;
	spLimit = STACKSIZE;
	initWithin();
	updateCurrentTime();
	for(i=0;i<nWires;i++)
		wireInit(wire+i);
	for(i=0;i<nBoxes;i++)
		boxInit(box+i);
	schedules = 0;
	ioInit2();
}

/*
 * super-step output
 *
 */
void output() {
	hp = heap;
	/* box itermult */
	if(box[0].state == RunnableOut) {
		uint16 blocking = box[0].blockPattern;
		if(blocking) {
			blocking = 0x0000;
			if(wire[0].availableFlag && wire[0].pendingFlag) blocking |= 0x0001;
			if(wire[3].availableFlag && wire[3].pendingFlag) blocking |= 0x0002;
			box[0].blockPattern = blocking;
			box[0].state = BlockedOut;
		}
		if(!blocking) {
			if(wire[0].pendingFlag) { /* wire to <internal> */
				wireOutput(wire+0);
			}
			if(wire[3].pendingFlag) { /* wire to mult2 */
				wireOutput(wire+3);
				if(box[1].state == BlockedIn) box[1].state = RunnableIn;
			}
			box[0].blockPattern = 0x8000;
			box[0].state = RunnableIn;
		}
	}
	/* box mult2 */
	if(box[1].state == RunnableOut) {
		uint16 blocking = box[1].blockPattern;
		if(blocking) {
			blocking = 0x0000;
			if(wire[4].availableFlag && wire[4].pendingFlag) blocking |= 0x0001;
			if(wire[1].availableFlag && wire[1].pendingFlag) blocking |= 0x0002;
			if(wire[2].availableFlag && wire[2].pendingFlag) blocking |= 0x0004;
			box[1].blockPattern = blocking;
			box[1].state = BlockedOut;
		}
		if(!blocking) {
			if(wire[4].pendingFlag) { /* wire to <internal> */
				wireOutput(wire+4);
			}
			if(wire[1].pendingFlag) { /* wire to itermult */
				wireOutput(wire+1);
				if(box[0].state == BlockedIn) box[0].state = RunnableIn;
			}
			if(wire[2].pendingFlag) { /* wire to output */
				wireOutput(wire+2);
				updateCurrentTime();
				boxBegin(box+2);
				ioOutput2();
				updateCurrentTime();
				boxEnd(box+2);
			}
			box[1].blockPattern = 0x8000;
			box[1].state = RunnableIn;
		}
	}
}

/*
 * main function of the engine
 *
 */
void engineRun() {
	if(setjmp(excJmp) == 0)
		goto itermult_init; /* next init */
__humeHandle:
	#ifdef SUPPORT_DEBUG
	{	exception_e exc = stack[sp-4].hp->tag - TCON;
		dumpException(stderr, exc);
		debugException(exc);
	}
	#endif
	/* No handlers */
	currentBox->state = Dead;
	#ifdef SUPPORT_DEBUG
	dumpBox(stderr, currentBox);
	#endif /*SUPPORT_DEBUG*/
	return; /* terminate on unhandled exception */
__humeNext:
	switch(currentBox - box) {
		case 0: 	goto mult2; /* next box */
		case 1: 	goto __humeSuperstep; /* exit run */
		case 2: 	goto __humeSuperstep; /* exit run */
	}
	return; /* unreachable code? */

/*
 * Main scheduling loop
 *
 */
__humeSuperstep:
	currentBox = NULL; /* so the debugger knows we are superstep-ing */
	output();
	schedules++;
	if((nSchedules != 0) && (schedules > nSchedules)) return;
	goto itermult; /* next box */

/*
 * Function call return (i.e. goto <variable>)
 *
 */
__humeReturn:
	switch(pc) {
		default:
			throwException(InternalError);
	}

/*
 * box mult2
 * - rules=1
 * - nInputs=2 nOutputs=3
 *
 */
mult2:
	/* -------------- 2: Label "mult2" */
	if(box[1].state != RunnableIn)
		goto __humeSuperstep; /* exit run */
	currentBox = box + 1;
	hp = heap;
	sp = 0;
	fp = 0;
	slp = 0;
	hpSoftLimit = heap + 30;
	spSoftLimit = 12;
	/* -------------- 3: Extension.CopyAllInputs */
	stack[inc_sp()].hp = wireRead(wire+3); /* wire from itermult */
	stack[inc_sp()].hp = wireRead(wire+4); /* wire from <internal> */
	/* -------------- 5: Push 3 */
	inc_spn(3);
	/* -------------- 6: CreateFrame 2 */
	if(slp == 0) slp = sp + 1;
	stack[inc_sp()].sp = fp;
	fp = sp;
	inc_spn(2);
	rp = sp;

mult2_0:
	/* -------------- 10: Extension.AvailSet 0 1 */
	if(!wire[4].availableFlag || !wire[3].availableFlag) {
			box[1].state = BlockedIn;
			goto __humeSuperstep; /* exit run */
	}
	/* -------------- 9: MatchRule */
	mp = ARG(0)+1;
	sp = rp;
	/* -------------- 11: MatchVar 0 */
	stack[VAR(0)] = stack[--mp];
	/* -------------- 13: MatchVar 1 */
	stack[VAR(1)] = stack[--mp];
	/* -------------- 16: MatchedRule */
	sp = rp;
	/* -------------- 14: Extension.ConsumeSet 0 1 */
	#ifdef SUPPORT_DEBUG
	updateCurrentTime();
	#endif /*SUPPORT_DEBUG*/
	wireConsume(wire+4);
	wireConsume(wire+3);
	box[0].blockPattern &= 0xfffd;
	if(!box[0].blockPattern) box[0].state = RunnableOut;
	#ifdef SUPPORT_DEBUG
	boxBegin(box+1);
	#endif /*SUPPORT_DEBUG*/
	/* -------------- 17: MkString "\n" */
	stack[inc_sp()].hp = mkString((char*)str_0);
	/* -------------- 18: PushVar 1 */
	stack[inc_sp()] = stack[VAR(1)];
	/* -------------- 19: CallPrim "show" */
	{	HEAP *arg = stack[--sp].hp;
		stack[sp++].hp = show(arg);
	}
	/* -------------- 20: CallPrim "++" */
	{	HEAP *arg1 = stack[--sp].hp;
		HEAP *arg2 = stack[--sp].hp;
		if(arg1->tag == TSTR && arg2->tag == TSTR) {
			char *s1 = (char *) (arg1+1);
			char *s2 = (char *) (arg2+1);
			stack[sp++].hp = mkCatString(s1, s2);
		} else if(arg1->tag >= TCON && arg2->tag >= TCON) { /* assume they are lists */
			HEAP *p = arg1;
			HEAP *r;
			HEAP **u = &r;
			while(p[1].lv == 2) { /* assume size=2 is a cons, copy the cons ':' cells of the 1st list */
				*u = mkCon(1, 2);
				setHeap(p[2].hp);
				u = (HEAP**)(hp++);
				p = p[3].hp;
			}
			*u = stack[sp].hp; /* set the tail of last to the 2nd list */
			stack[sp++].hp = r;
		} else if(arg1->tag == TVECTOR && arg2->tag == TVECTOR) {
			HEAP *vec = mkVector((arg1+1)->lv + (arg2+1)->lv);
			hp += (arg1+1)->lv + (arg2+1)->lv;
			memcpy(vec+2,              arg1+2, sizeof(HEAP)*((arg1+1)->lv));	/* copy the first vector body */
			memcpy(vec+2+(arg1+1)->lv, arg2+2, sizeof(HEAP)*((arg2+1)->lv));	/* copy the second vector body */
			stack[sp++].hp = vec;
		}
		#ifndef NDEBUG
		else throwException(InternalError);
		#endif /*NDEBUG*/

	}
	/* -------------- 21: PushVar 0 */
	stack[inc_sp()] = stack[VAR(0)];
	/* -------------- 22: PushVar 0 */
	stack[inc_sp()] = stack[VAR(0)];
	/* -------------- 23: MkInt 0 */
	stack[inc_sp()].hp = mkInt(0);
	/* -------------- 24: MkTuple 3 */
	{	HEAP *tup = mkTup(3);
		setHeap(stack[--sp].hp);
		setHeap(stack[--sp].hp);
		setHeap(stack[--sp].hp);
		stack[inc_sp()].hp = tup;
	}
	/* -------------- 25: MkInt 1 */
	stack[inc_sp()].hp = mkInt(1);
	/* -------------- 26: PushVar 0 */
	stack[inc_sp()] = stack[VAR(0)];
	/* -------------- 27: CallPrim "+" */
	{	HEAP *arg1 = stack[--sp].hp;
		HEAP *arg2 = stack[--sp].hp;
		if(arg1->tag == TINT && arg2->tag == TINT) {
			stack[sp++].hp = mkInt((arg1+1)->lv + (arg2+1)->lv);
		#ifdef SUPPORT_FLOAT
		} else if (arg1->tag == TFLOAT && arg2->tag == TFLOAT) {
			stack[sp++].hp = mkFloat((arg1+1)->fv + (arg2+1)->fv);
		#endif /*SUPPORT_FLOAT*/
		}
		#ifndef NDEBUG
		else throwException(InternalError);
		#endif /*NDEBUG*/
	}

mult2_exit:
	/* -------------- 30: CheckOutputs */
	updateCurrentTime();
	boxEnd(box+1);
	wireSetPending(wire+4, 2, stack[sp-1].hp); /* wire to <internal> */
	wireSetPending(wire+1, 11, stack[sp-2].hp); /* wire to itermult */
	wireSetPending(wire+2, 50, stack[sp-3].hp); /* wire to output */
	box[1].state = RunnableOut;
	goto __humeSuperstep; /* exit run */

/*
 * box itermult
 * - rules=2
 * - nInputs=2 nOutputs=2
 *
 */
itermult:
	/* -------------- 36: Label "itermult" */
	if(box[0].state != RunnableIn)
		goto mult2; /* next box */
	currentBox = box + 0;
	hp = heap;
	sp = 0;
	fp = 0;
	slp = 0;
	hpSoftLimit = heap + 42;
	spSoftLimit = 18;
	/* -------------- 37: Extension.CopyAllInputs */
	stack[inc_sp()].hp = wireRead(wire+0); /* wire from <internal> */
	stack[inc_sp()].hp = wireRead(wire+1); /* wire from mult2 */
	/* -------------- 39: Push 3 */
	inc_spn(3);
	/* -------------- 40: CreateFrame 3 */
	if(slp == 0) slp = sp + 1;
	stack[inc_sp()].sp = fp;
	fp = sp;
	inc_spn(3);
	rp = sp;

itermult_0:
	/* -------------- 44: Extension.AvailSet 0 */
	if(!wire[1].availableFlag) {
			goto itermult_1; /* next rule */
	}
	/* -------------- 43: MatchRule */
	mp = ARG(0)+1;
	sp = rp;
	/* -------------- 45: MatchTuple 3 */
	--mp;
	/* -------------- 46: MatchNone */
	--mp;
	/* -------------- 47: CopyArg 0 */
	stack[inc_sp()] = stack[ARG(0)];
	/* -------------- 48: Unpack */
	{	HEAP *nodep  = stack[--sp].hp;
		uint32 i;
		uint32 size = nodep[1].lv;
		for (i = 0; i < size; i++)
			stack[inc_sp()].hp = nodep[1+size-i].hp;
		mp = sp;
	}
	/* -------------- 49: MatchVar 0 */
	stack[VAR(0)] = stack[--mp];
	/* -------------- 50: MatchVar 1 */
	stack[VAR(1)] = stack[--mp];
	/* -------------- 51: MatchVar 2 */
	stack[VAR(2)] = stack[--mp];
	/* -------------- 54: MatchedRule */
	sp = rp;
	/* -------------- 53: Extension.ConsumeSet 0 */
	#ifdef SUPPORT_DEBUG
	updateCurrentTime();
	#endif /*SUPPORT_DEBUG*/
	wireConsume(wire+1);
	box[1].blockPattern &= 0xfffd;
	if(!box[1].blockPattern) box[1].state = RunnableOut;
	#ifdef SUPPORT_DEBUG
	boxBegin(box+0);
	#endif /*SUPPORT_DEBUG*/
	/* -------------- 55: MkNone */
	stack[inc_sp()].hp = NULL;
	/* -------------- 56: PushVar 2 */
	stack[inc_sp()] = stack[VAR(2)];
	/* -------------- 57: PushVar 1 */
	stack[inc_sp()] = stack[VAR(1)];
	/* -------------- 58: PushVar 0 */
	stack[inc_sp()] = stack[VAR(0)];
	/* -------------- 59: MkTuple 3 */
	{	HEAP *tup = mkTup(3);
		setHeap(stack[--sp].hp);
		setHeap(stack[--sp].hp);
		setHeap(stack[--sp].hp);
		stack[inc_sp()].hp = tup;
	}

itermult_exit:
	/* -------------- 62: CheckOutputs */
	updateCurrentTime();
	boxEnd(box+0);
	wireSetPending(wire+0, 11, stack[sp-1].hp); /* wire to <internal> */
	wireSetPending(wire+3, 2, stack[sp-2].hp); /* wire to mult2 */
	box[0].state = RunnableOut;
	goto mult2; /* next box */

itermult_1:
	/* -------------- 70: Extension.AvailSet 1 */
	if(!wire[0].availableFlag) {
			box[0].state = BlockedIn;
			goto mult2; /* next box */
	}
	/* -------------- 68: MatchRule */
	mp = ARG(0)+1;
	sp = rp;
	/* -------------- 69: MatchNone */
	--mp;
	/* -------------- 71: MatchTuple 3 */
	--mp;
	/* -------------- 72: CopyArg 1 */
	stack[inc_sp()] = stack[ARG(1)];
	/* -------------- 73: Unpack */
	{	HEAP *nodep  = stack[--sp].hp;
		uint32 i;
		uint32 size = nodep[1].lv;
		for (i = 0; i < size; i++)
			stack[inc_sp()].hp = nodep[1+size-i].hp;
		mp = sp;
	}
	/* -------------- 74: MatchVar 0 */
	stack[VAR(0)] = stack[--mp];
	/* -------------- 75: MatchVar 1 */
	stack[VAR(1)] = stack[--mp];
	/* -------------- 76: MatchVar 2 */
	stack[VAR(2)] = stack[--mp];
	/* -------------- 79: MatchedRule */
	sp = rp;
	/* -------------- 78: Extension.ConsumeSet 1 */
	#ifdef SUPPORT_DEBUG
	updateCurrentTime();
	#endif /*SUPPORT_DEBUG*/
	wireConsume(wire+0);
	#ifdef SUPPORT_DEBUG
	boxBegin(box+0);
	#endif /*SUPPORT_DEBUG*/
	/* -------------- 80: MkInt 0 */
	stack[inc_sp()].hp = mkInt(0);
	/* -------------- 81: PushVar 2 */
	stack[inc_sp()] = stack[VAR(2)];
	/* -------------- 82: CallPrim "==" */
	{	HEAP *arg1 = stack[--sp].hp;
		HEAP *arg2 = stack[--sp].hp;
		stack[sp++].hp = mkBool(cmpPrim(arg1, arg2, TRUE, FALSE));
	}
	/* -------------- 83: If "t_itermult_1_0" */
	if(stack[--sp].hp[1].lv == TRUE)
		goto t_itermult_1_0;
	/* -------------- 84: MkNone */
	stack[inc_sp()].hp = NULL;
	/* -------------- 85: MkInt 1 */
	stack[inc_sp()].hp = mkInt(1);
	/* -------------- 86: PushVar 2 */
	stack[inc_sp()] = stack[VAR(2)];
	/* -------------- 87: CallPrim "-" */
	{	HEAP *arg1 = stack[--sp].hp;
		HEAP *arg2 = stack[--sp].hp;
		if(arg1->tag == TINT && arg2->tag == TINT) {
			stack[sp++].hp = mkInt((arg1+1)->lv - (arg2+1)->lv);
		#ifdef SUPPORT_FLOAT
		} else if (arg1->tag == TFLOAT && arg2->tag == TFLOAT) {
			stack[sp++].hp = mkFloat((arg1+1)->fv - (arg2+1)->fv);
		#endif /*SUPPORT_FLOAT*/
		}
		#ifndef NDEBUG
		else throwException(InternalError);
		#endif /*NDEBUG*/
	}
	/* -------------- 88: PushVar 1 */
	stack[inc_sp()] = stack[VAR(1)];
	/* -------------- 89: PushVar 1 */
	stack[inc_sp()] = stack[VAR(1)];
	/* -------------- 90: PushVar 0 */
	stack[inc_sp()] = stack[VAR(0)];
	/* -------------- 91: CallPrim "+" */
	{	HEAP *arg1 = stack[--sp].hp;
		HEAP *arg2 = stack[--sp].hp;
		if(arg1->tag == TINT && arg2->tag == TINT) {
			stack[sp++].hp = mkInt((arg1+1)->lv + (arg2+1)->lv);
		#ifdef SUPPORT_FLOAT
		} else if (arg1->tag == TFLOAT && arg2->tag == TFLOAT) {
			stack[sp++].hp = mkFloat((arg1+1)->fv + (arg2+1)->fv);
		#endif /*SUPPORT_FLOAT*/
		}
		#ifndef NDEBUG
		else throwException(InternalError);
		#endif /*NDEBUG*/
	}
	/* -------------- 92: MkTuple 3 */
	{	HEAP *tup = mkTup(3);
		setHeap(stack[--sp].hp);
		setHeap(stack[--sp].hp);
		setHeap(stack[--sp].hp);
		stack[inc_sp()].hp = tup;
	}
	/* -------------- 93: MkTuple 2 */
	{	HEAP *tup = mkTup(2);
		setHeap(stack[--sp].hp);
		setHeap(stack[--sp].hp);
		stack[inc_sp()].hp = tup;
	}
	/* -------------- 94: Goto "n_itermult_1_0" */
	goto n_itermult_1_0;

t_itermult_1_0:
	/* -------------- 97: PushVar 0 */
	stack[inc_sp()] = stack[VAR(0)];
	/* -------------- 98: MkNone */
	stack[inc_sp()].hp = NULL;
	/* -------------- 99: MkTuple 2 */
	{	HEAP *tup = mkTup(2);
		setHeap(stack[--sp].hp);
		setHeap(stack[--sp].hp);
		stack[inc_sp()].hp = tup;
	}

n_itermult_1_0:
	/* -------------- 102: Unpack */
	{	HEAP *nodep  = stack[--sp].hp;
		uint32 i;
		uint32 size = nodep[1].lv;
		for (i = 0; i < size; i++)
			stack[inc_sp()].hp = nodep[1+size-i].hp;
		mp = sp;
	}
	/* -------------- 103: Goto "itermult_exit" */
	goto itermult_exit;

/*
 * init itermult_init
 * - nOutputs=2
 *
 */
itermult_init:
	/* -------------- 117: Label "itermult_init" */
	currentBox = box + 0;
	hp = heap;
	sp = 0;
	fp = 0;
	slp = 0;
	hpSoftLimit = heap + 42;
	spSoftLimit = 18;
	/* -------------- 118: MkInt 0 */
	stack[inc_sp()].hp = mkInt(0);
	/* -------------- 119: Write 1 */
	wireSetPending(wire+3, 2, stack[--sp].hp); /* wire to mult2 */
	/* -------------- 120: Schedule */
	box[0].state = RunnableOut;
	goto mult2_init; /* next init */

/*
 * init mult2_init
 * - nOutputs=3
 *
 */
mult2_init:
	/* -------------- 122: Label "mult2_init" */
	currentBox = box + 1;
	hp = heap;
	sp = 0;
	fp = 0;
	slp = 0;
	hpSoftLimit = heap + 30;
	spSoftLimit = 12;
	/* -------------- 123: MkInt 0 */
	stack[inc_sp()].hp = mkInt(0);
	/* -------------- 124: Write 0 */
	wireSetPending(wire+4, 2, stack[--sp].hp); /* wire to <internal> */
	/* -------------- 125: Schedule */
	box[1].state = RunnableOut;
	goto __humeSuperstep; /* exit init */
}

int main2(int argc, char *argv[]) {
	arguments = argv; /* only useful for boxes that read main_args */
	exitCode = 0; /* only useful for boxes that write to main_exit */
	engineInit();
	#ifdef SUPPORT_DEBUG
	debugInit("/tmp_mnt/3/staff/greg/EmBounded/EmBounded/local/hwu/Budapest/itermult.ham");
	#endif /*SUPPORT_DEBUG*/
	engineRun();
	#ifdef SUPPORT_DEBUG
	{	uint16 i;
		dumpState(stderr);
		for(i=0;i<nBoxes;i++)
			dumpBox(stderr, box+i);
		for(i=0;i<nWires;i++)
			dumpWire(stderr, wire+i);
	}
	#endif /*SUPPORT_DEBUG*/
	return exitCode;
}
