/*
 * Define a function once, use it repeatedly, deleting tcc states as
 * soon as possible.
 */

/* uncomment to enable diagnostic output */
//	#define DIAG(...) diag(__VA_ARGS__)

#include "test_setup.h"

int fib (int n);
char buffer[200];
int fib (int n) {
    if (n <= 2)
        return 1;
    else
        return fib(n-1) + fib(n-2);
}

char def_code[] =
"int fib(int n);\n"
"int fib_of_5() { return fib(5); }\n"
;

char first_code[] =
"int twice_fib_of_5()\n"
"{\n"
"    return 2 * fib_of_5();\n"
"}\n"
;

char second_code[] =
"int thrice_fib_of_5() {\n"
"    return 3 * fib_of_5();\n"
"}\n"
;

int main(int argc, char **argv) {

	/* ---- Compile the first code string and setup the callback data ---- */
	
	TCCState *s1 = tcc_new();
	SIMPLE_SETUP(s1);
	
	/* Code taken from test_setup.h and modified to work with my own relocation
	 * point. */
	tcc_save_extended_symtab(s1);
    if (tcc_compile_string(s1, def_code) == -1) return 1;
    tcc_add_symbol(s1, "fib", fib);
    int def_code_size = tcc_relocate(s1, 0);
    void * code = malloc(def_code_size);
    if (code == NULL) return 1;
	if (tcc_relocate(s1, code) == -1) return 1;
	extended_symtab_p my_symtab = tcc_get_extended_symbol_table(s1);
	pass("First code string compiled and relocated fine");
	
	/* ---- Get the fib_of_5 function and evaluate it ---- */
	
	int (*fib5)() = tcc_get_symbol(s1, "fib_of_5");
	if (fib5 == NULL) return -1;
	pass("Found fib_of_5");
	is_i(fib5(), 5, "Calling fib_of_5 from defining compiler context works");
	
	/* ---- Delete the original tcc state ---- */
	
	tcc_delete(s1);
	
	/* ---- Compile the second string ---- */
	
	SETUP_SECOND_CALLBACK_DATA();
	TCCState *s_first = tcc_new();
	setup_and_compile_second_state(s_first, first_code);
	relocate_second_state(s_first);
	
	/* ---- Check the functionality ---- */
	
	/* Retrieve twice_fib_of_5 */
	int (*fib5x2)() = tcc_get_symbol(s_first, "twice_fib_of_5");
	if (fib5x2 == NULL) return -1;
	pass("Found twice_fib_of_5 function pointer");
	is_i(fib5x2(), 10, "Dependent function call works");
	
	/* ---- Delete the first dependent state ---- */
	
	tcc_delete(s_first);
	
	/* ---- Compile the third string ---- */
	
	TCCState *s_second = tcc_new();
	setup_and_compile_second_state(s_second, second_code);
	relocate_second_state(s_second);
	
	/* ---- Check the functionality ---- */
	
	int (*fib5x3)() = tcc_get_symbol(s_second, "thrice_fib_of_5");
	if (fib5x3 == NULL) return -1;
	pass("Found thrice_fib_of_5 function pointer");
	is_i(fib5x3(), 15, "Second dependent fibonaci function call works");
	
	/* ---- Cleanup ---- */
	tcc_delete_extended_symbol_table(my_symtab);
	tcc_delete(s_second);
	free(code);
	pass("cleanup");
	
	done_testing();
	
	return 0;
}
