/* $Id$
 * Main interpreter program.
 *
 * Copyright (C) 2008-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#include <getopt.h>
#undef _GNU_SOURCE
#else /* _GNU_SOURCE already defined */
#include <getopt.h>
#endif /* _GNU_SOURCE not defined? */
#include "fauhdli_private.h"
#include "glue/glue-vhdl.h"
#include "glue/glue-log.h"
#include "glue/glue-main.h"

static int
run_fauhdli(
	const char *parse_file, 
	const char *top_entity, 
	const char *trace_file,
	struct slist *trace_ents,
	bool debug
)
{
	struct fauhdli *instance;
	int ret;
	void *fi;

	static const struct glue_vhdl_cb cb = {
		/* memory related (libc) */
		.malloc = malloc,
		.free = free,
		/* scheduler related (glue-main.c) */
		.time_virt = fauhdli_time_virt,
		.time_call_at = fauhdli_time_call_at,
		.time_call_delete = fauhdli_time_call_delete,
		.quit = fauhdli_simulation_quit,
		/* logging */
		.log = (void (*)(int, 
				const char *, 
				const char *, 
				const char *,
				...))fauhdli_log,
		/* foreign components related */
		.comp_create = glue_vhdl_comp_create,
		.arch_create = glue_vhdl_arch_create,
		.signal_create = glue_vhdl_create_signal,
		.arch_init = glue_vhdl_arch_init,
		.drv_set = glue_vhdl_set,
		.proc_set = glue_vhdl_proc_set,
		.proc_call = glue_vhdl_proc_call,
		.connect_out = glue_vhdl_connect_out,
		.connect_in = glue_vhdl_connect_in,
		.comp_init = glue_vhdl_comp_init,
		.comp_port_connect = glue_vhdl_comp_port_connect,
		.arch_port_connect = glue_vhdl_arch_port_connect,
		.comp_generic_nonarray_set = 
				glue_vhdl_comp_generic_nonarray_set,
		.arch_generic_nonarray_set = 
				glue_vhdl_arch_generic_nonarray_set,
		.comp_generic_array_set = glue_vhdl_comp_generic_array_set,
		.arch_generic_array_set = glue_vhdl_arch_generic_array_set
	};

	fi = glue_vhdl_create();

	instance = fauhdli_create(parse_file, trace_file, debug, &cb, fi);
	instance->trace_list = trace_ents;

	fauhdli_init(instance, top_entity);

	ret = fauhdli_main_event_loop();

	fauhdli_destroy(instance);
	glue_vhdl_destroy(fi);

	return ret;
}

static void
usage(void)
{
	fprintf(stderr, "fauhdli [options] FILE\n");
	fprintf(stderr, "  Interprete commands from FILE.\n");
	fprintf(stderr, "Options:\n");
	fprintf(stderr, "  -s ENTITY, --simulate=ENTITY   Start simulation "
			"with ENTITY as top entity.\n");
	fprintf(stderr, "  -o VCDFILE, --output=VCDFILE   Output trace to ");
	fprintf(stderr, "VCDFILE.\n");
	fprintf(stderr, "  -d, --debug                    enable debug "
			"output\n");
	fprintf(stderr, "  -t, --trace=ENTITY             Trace all signals "
			"in ENTITY\n");
	fprintf(stderr, "\n");
}

int
main(int argc, char **argv)
{
	const char *parse_file = NULL;
	const char *top_entity = NULL;
	const char *trace_file = NULL;
	int c;
	bool debug = false;
	int ret;
	struct slist *trace_ents;
	struct option l_opts[] = {
		{"simulate", 1, NULL, 's'},
		{"output", 1, NULL, 'o'},
		{"debug", 0, NULL, 'd'},
		{"trace", 1, NULL, 't'},
		{0, 0, 0, 0}
	};

	trace_ents = slist_create(malloc);

	for (;;) {
		c = getopt_long(argc, argv, "s:o:dt:", l_opts, NULL);
		if (c == -1) {
			break;
		}

		switch (c) {
		case 's':
			top_entity = optarg;
			break;

		case 'o':
			trace_file = optarg;
			break;

		case 'd':
			debug = true;
			break;

		case 't': 
			slist_add(trace_ents, (void *)optarg, malloc);
			break;
		    
		default:
			usage();
			return EXIT_FAILURE;
		}
	}

	if (argc - optind != 1) {
		usage();
		return EXIT_FAILURE;
	}

	if ((trace_file == NULL) && (trace_ents->first != NULL)) {
		usage();
		return EXIT_FAILURE;
	}

	parse_file = argv[optind];
	ret = run_fauhdli(
			parse_file, 
			top_entity, 
			trace_file, 
			trace_ents, 
			debug);

	slist_destroy(trace_ents, free);

	return ret;
}
