/******************************************************************************
 * Copyright (c) 2005 The Regents of the University of California.
 * This material was produced under U.S. Government contract W-7405-ENG-36
 * for Los Alamos National Laboratory, which is operated by the University
 * of California for the U.S. Department of Energy. The U.S. Government has
 * rights to use, reproduce, and distribute this software. NEITHER THE
 * GOVERNMENT NOR THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR
 * ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified
 * to produce derivative works, such modified software should be clearly
 * marked, so as not to confuse it with the version available from LANL.
 *
 * Additionally, this program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * LA-CC 04-115
 ******************************************************************************/

#include "config.h"

#include <getopt.h>
#include <stdlib.h>
#include <stdarg.h>

#include "backend.h"
#include "proxy.h"
#include "proxy_tcp.h"
#include "runtime.h"

#define DEFAULT_BACKEND	"gdb-mi"
#define DEFAULT_PROXY	"tcp"

extern void client(int, int, char *, char *, int);
extern void server(int, int, int, dbg_backend *);

static struct option longopts[] = {
	{"debugger",		required_argument,	NULL,	'b'},
	{"debugger_path",	required_argument,	NULL, 	'e'}, 
	{"proxy",			required_argument,	NULL, 	'P'}, 
	{"port",			required_argument,	NULL, 	'p'}, 
	{"host",			required_argument,	NULL, 	'h'}, 
	{"jobid",			required_argument,	NULL, 	'j'},
#ifdef DEBUG
	{"debug",			optional_argument,	NULL, 	'd'},
#endif /* DEBUG */
	{NULL,				0,					NULL,	0}
};

#ifdef DEBUG
static char * shortopts = "b:e:P:p:h:d::";
#else /* DEBUG */
static char * shortopts = "b:e:P:p:h:";
#endif /* DEBUG */

static int	fatal_error = 0;
static char *error_str = NULL;

void
error_msg(char *fmt, ...)
{
	va_list	ap;
	
	va_start(ap, fmt);
	vasprintf(&error_str, fmt, ap);
	va_end(ap);
	
	fatal_error = 1;
}

void
print_error_msg()
{
	if (error_str != NULL) {
		fprintf(stderr, "%s", error_str);
		fflush(stderr);
	}
}

/*
 * Main entry point for MPI parallel debugger.
 * 
 * @arg	-b debugger	select backend debugger
 * @arg	-e path		set backend debugger path
 * @arg	-p port		port number to listen on/connect to
 * @arg	-h host		host to connect to
 * @arg	-P proxy		type of proxy connection to use
 * @arg	-j jobid		jobid of application being debugged
 */
int
main(int argc, char *argv[])
{
	int 			rank;
	int 			size;
	int				ch;
	int				jobid = 0;
	int				port = PROXY_TCP_PORT;
	char *			host = NULL;
	char *			debugger_str = DEFAULT_BACKEND;
	char *			proxy_str = DEFAULT_PROXY;
	char *			path = NULL;
	proxy *			p;
	dbg_backend *	d;

	while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1)
	switch (ch) {
	case 'b':
		debugger_str = optarg;
		break;
	case 'e':
		path = optarg;
		break;
	case 'P':
		proxy_str = optarg;
		break;
	case 'p':
		port = (int)strtol(optarg, NULL, 10);
		break;
	case 'h':
		host = optarg;
		break;
	case 'j':
		jobid = (int)strtol(optarg, NULL, 10);
		break;
#ifdef DEBUG
	case 'd':
		if (optarg == NULL)
			debug_level = DEBUG_LEVEL_ALL;
		else
			debug_level = (int)strtol(optarg, NULL, 10);
		break;
#endif /* DEBUG */
	default:
		error_msg(
			"sdm [--debugger=value] [--debugger_path=path]\n"
			"    [--proxy=proxy]\n"
			"    [--host=host_name] [--port=port]\n"
			"    [--jobid=jobid]\n"
#ifdef DEBUG
			"    [--debug[=level]]\n"
#endif /* DEBUG */
		);
		goto error_out;
	}
	
	argc -= optind;
	argv += optind;
	
	if (find_dbg_backend(debugger_str, &d) < 0) {
		error_msg("No such backend: \"%s\"\n", debugger_str);
		goto error_out;
	}
	
	if (path != NULL)
		backend_set_path(d, path);
	
	if (find_proxy(proxy_str, &p) < 0) {
		error_msg("No such proxy: \"%s\"\n", proxy_str);
		goto error_out;
	}
	
error_out:
	DEBUG_PRINTS(DEBUG_LEVEL_STARTUP, "starting MPI\n");
	
	runtime_init(&size, &rank);
	
	DEBUG_SET_RANK(rank);
	
	DEBUG_PRINTF(DEBUG_LEVEL_STARTUP, "size=%d rank=%d\n", size, rank);

	if (size < 2) {
		error_msg("Debugger requires at least 2 processes\n");
	}
	
	if (fatal_error) {
		if (rank == 0)
			print_error_msg();
		runtime_finalize();
		return 1;
	}
		
	// MPI_Comm_create_errhandler(handle_fatal_errors, &err_handler);
	// MPI_Comm_set_errhandler(MPI_COMM_WORLD, err_handler);
	
	/*
	 * The client process *must* always have task id 'size' - 1 since it
	 * does not control a debugged process.
	 */
	if (rank == size-1) {
		DEBUG_PRINTS(DEBUG_LEVEL_STARTUP, "starting client\n");
		client(size, rank, proxy_str, host, port);
	} else {
		DEBUG_PRINTF(DEBUG_LEVEL_STARTUP, "starting task %d\n", rank);
		server(size, rank, jobid, d);
	}

	DEBUG_PRINTS(DEBUG_LEVEL_STARTUP, "all finished\n");
	
	runtime_finalize();
	
	return 0;
}
