/******************************************************************************
 * 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 <stdio.h>
#include <stdlib.h>

#include "dbg.h"
#include "proxy.h"
#include "proxy_tcp.h"
#include "bitset.h"
#include "itimer.h"

int 		completed;
int			fatal;
bitset *	procs_outstanding = NULL;
bitset *	sess_procs = NULL;

void
event_callback(dbg_event *e, void *data)
{
	stackframe *	f;
	char *		s;
	
	printf("in event callback\n");
	
	if (e == NULL) {
		printf("got null event!\n");
		return;
	}
	
	if (e->procs != NULL) {
		if (sess_procs == NULL)
			sess_procs = bitset_dup(e->procs);
		s = bitset_to_set(e->procs);
		printf("%s ", s);
		free(s);
	}
		
	printf("-> ");
	
	switch (e->event_id) {
	case DBGEV_ERROR:
		printf("error: %s\n", e->dbg_event_u.error_event.error_msg);
		switch (e->dbg_event_u.error_event.error_code) {
		case DBGERR_NOBACKEND:
		case DBGERR_NOFILEDIR:
			fatal++;
			break;
		default:
			break;
		}
		break;
	case DBGEV_OK:
		printf("command ok\n");
		break;
	case DBGEV_OUTPUT:
		printf("output: %s\n", e->dbg_event_u.output);
		break;
	case DBGEV_SUSPEND:
		switch (e->dbg_event_u.suspend_event.reason) {
		case DBGEV_SUSPEND_BPHIT:
			printf("hit breakpoint %d\n", e->dbg_event_u.suspend_event.ev_u.bpid);
			break;
		case DBGEV_SUSPEND_INT:
			printf("suspend completed\n");
			break;
		case DBGEV_SUSPEND_STEP:
			printf("step completed\n");
			break;
		case DBGEV_SUSPEND_SIGNAL:
			printf("received signal %s\n", e->dbg_event_u.suspend_event.ev_u.sig->name);
			break;
		}
		break;
	case DBGEV_BPSET:
		printf("breakpoint set\n");
		break;
	case DBGEV_EXIT:
		switch (e->dbg_event_u.suspend_event.reason) {
		case DBGEV_EXIT_NORMAL:
			printf("exited with status %d\n", e->dbg_event_u.exit_event.ev_u.exit_status);
			break;
		case DBGEV_EXIT_SIGNAL:
			printf("exited with signal %s\n", e->dbg_event_u.exit_event.ev_u.sig->name);
			break;
		}
		break;
	case DBGEV_DATA:
		printf("data is "); AIFPrint(stdout, 0, e->dbg_event_u.data_event.data); printf("\n");
		break;
	case DBGEV_FRAMES:
		printf("got frames:\n");
		for (SetList(e->dbg_event_u.list); (f = (stackframe *)GetListElement(e->dbg_event_u.list)) != NULL; ) {
			printf(" #%d %s() at %s:%d\n", f->level, f->loc.func, f->loc.file, f->loc.line);
		}
		break;
	default:
		printf("got event %d\n", e->event_id);
		break;
	}

	if (e->procs != NULL && procs_outstanding != NULL) {
		bitset_invert(e->procs);
		bitset_andeq(procs_outstanding, e->procs);
		if (!bitset_isempty(procs_outstanding))
			return;
	}
	
	completed++;
}

int
wait_for_event(session *s, bitset *p)
{
	completed = 0;
	fatal = 0;

	printf("entering wait_for_event...\n");
	
	if (p != NULL)
		procs_outstanding = bitset_dup(p);
		
	while (!completed) {
		if (DbgProgress(s) < 0) {
			fprintf(stderr, "error: %s\n", DbgGetErrorStr());
			exit(1);
		}
	}
	
	if (p != NULL) {
		bitset_free(procs_outstanding);
		procs_outstanding = NULL;
	}
		
	/*
	 * Check a fatal error hasn't ocurred
	 */
	return fatal ? -1 : 0;
}

/*
 * Tell server to quit
 */
void
cleanup_and_exit(session *s, bitset *p)
{
	DbgQuit(s);
	wait_for_event(s, p);
	exit(0);
}

int
do_test(session *s, char *dir, char *exe)
{
	bitset *		p1;
	int				bpid = 54;
	itimer *		t;
	
	t = itimer_new("debug");
	itimer_start(t);
	
	if (DbgStartSession(s, exe, dir, NULL) < 0) {
		fprintf(stderr, "error: %s\n", DbgGetErrorStr());
		return 1;
	}
	if (wait_for_event(s, NULL) < 0) {
		cleanup_and_exit(s, NULL);
	}
	
	itimer_mark(t, "launch");
	
	p1 = sess_procs;
		
	if (DbgSetLineBreakpoint(s, p1, bpid++, "xxx.c", 17) < 0) {
		fprintf(stderr, "error: %s\n", DbgGetErrorStr());
		return 1;
	}
	wait_for_event(s, p1);
	
	itimer_mark(t, "breakpoint");
	
	if (DbgGo(s, p1) < 0) {
		fprintf(stderr, "error: %s\n", DbgGetErrorStr());
		return 1;
	}
	wait_for_event(s, p1);
	
	itimer_mark(t, "suspend");
		
	if (DbgListStackframes(s, p1, 0, 1) < 0) {
		fprintf(stderr, "error: %s\n", DbgGetErrorStr());
		return 1;
	}
	wait_for_event(s, p1);

	itimer_mark(t, "where");
	
	if (DbgStep(s, p1, 1, 0) < 0) {
		fprintf(stderr, "error: %s\n", DbgGetErrorStr());
		return 1;
	}
	wait_for_event(s, p1);
	
	itimer_mark(t, "step");

	if (DbgStep(s, p1, 1, 0) < 0) {
		fprintf(stderr, "error: %s\n", DbgGetErrorStr());
		return 1;
	}
	wait_for_event(s, p1);
	
	itimer_mark(t, "step");

	if (DbgEvaluateExpression(s, p1, "a") < 0) {
		fprintf(stderr, "error: %s\n", DbgGetErrorStr());
		return 1;
	}
	wait_for_event(s, p1);
	
	itimer_mark(t, "evaluate");

	if (DbgListStackframes(s, p1, 1, 1) < 0) {
		fprintf(stderr, "error: %s\n", DbgGetErrorStr());
		return 1;
	}
	wait_for_event(s, p1);
	
	itimer_mark(t, "where");

	if (DbgQuit(s) < 0) {
		fprintf(stderr, "error: %s\n", DbgGetErrorStr());
		return 1;
	}
	wait_for_event(s, p1);

	itimer_stop(t);
	itimer_print(t);
	itimer_free(t);
	
	exit(0);
}

int
main(int argc, char *argv[])
{
	session *	s;
	char *		dir;
	char *		exe;

	if (argc < 2) {
		fprintf(stderr, "usage: sdm_test [dir] exe\n");
		return 1;
	}

	if (argc == 2) {
		dir = getcwd(NULL, 0);
		exe = argv[1];
	} else {
		dir = argv[1];
		exe = argv[2];
	}

	debug_level = 3;

	if (DbgInit(&s, argc, argv) < 0) {
		fprintf(stderr, "DbgInit failed\n");
		exit(1);
	}

	DbgRegisterEventHandler(s, event_callback, NULL);

	return do_test(s, dir, exe);
}
