typedef struct act {
	UWord sp;
	UWord target;
	Char * fn;
} act;

static UWord stack_depth = 0;
static act * current = NULL; 
static act * tstack = NULL;

typedef enum jump_t { BB_INIT, CALL, RET, BBCALL, BBRET, BOR, BBBOR } jump_t;

static void print_stack(void) {
	UWord depth = stack_depth;
	act * c = current;
	while(stack_depth > 0) {
		
		VG_(printf)("[%lu] %s()\n", depth, c->fn);
		c--;
		depth--;
	}
}


static void function_enter(UWord target, char * name) {
	VG_(printf)("[%lu] Entered %s()\n", stack_depth, name);
}

static void function_exit(UWord target, char * name) {
	VG_(printf)("[%lu] Exit %s()\n", stack_depth, name);
}

static void init_stack(UWord csp, UWord target) {
	
	tstack = VG_(calloc)("thread_data", sizeof(act) * 4096, 1);
	current = tstack;
	/* Function name buffer */
	Char * fn = VG_(calloc)("fn name", 256, 1);
	/* valgrind call the init function "(below main)" */
	VG_(sprintf)(fn, "(below main)");
	current->fn = fn;
	current->sp = csp;
	current->target = 0; /* Fake target */
	
	/* Safety check: we never execute the first 
	 * instruction of this function */
	if (VG_(get_fnname_if_entry)(target, fn, 256)) failure("Wrong");
	
}

static VG_REGPARM(2) void call(UWord target, UWord type_op) {

	/* Obtain current stack pointer */
	UWord csp = (UWord) VG_(get_SP)(1);

	/* Init stack */
	if (tstack == NULL) {
		init_stack(csp, target);
		return;
	}

	/* Function name buffer */
	Char * fn = VG_(calloc)("fn name", 256, 1);
	/* Are we entering a new function? */
	Bool call_fn = VG_(get_fnname_if_entry)(target, fn, VG_(strlen)(fn));
	/* If not, ask valgrind where we are... */
	Bool act_fn = True;
	if (!call_fn)
		act_fn = VG_(get_fnname)(target, fn, VG_(strlen)(fn));
	
	if (!act_fn) {
		/* 
		 * Why is this happening?
		 * I don't know!
		 */
		VG_(printf)("Valgrind does not know where we are: %p\n", target);
	}
	

	if (call_fn) {
		
		/* Before the call, some functions are returned? */
		while(current->sp < csp && stack_depth > 0) {
			
			VG_(printf)("We miss a return :(\n");
			
			/* This function is returned */
			function_exit(current->target, current->fn);
			/* Adjust stack */
			stack_depth--;
			current--;
			
		}
		
		/* Register the new activation of a function */
		stack_depth++;
		current++;
		current->sp = csp;
		current->target = target;
		current->fn = fn;
		function_enter(current->target, current->fn);
		
	} else if (csp > current->sp) {
	
		/* One or more function returned */
		while(stack_depth > 0) {
			
			function_exit(current->target, current->fn);
			stack_depth--;
			current--;
			
			if (csp <= current->sp) {
				
				VG_(printf)("[%lu] Inside %s\n", stack_depth, current->fn);
				
				/* Safety check */
				if (act_fn && VG_(strcmp)(current->fn,fn) != 0) {
					VG_(printf)("Simulated stack says you are in %s but valgrind says %s\n", current->fn, fn);
					failure("Mismatch during return\n");
				}
				
				break;
			}
		}
		
	} 

}