# include "dgd.h"
# include "str.h"
# include "array.h"
# include "object.h"
# include "xfloat.h"
# include "interpret.h"
# include "data.h"
# include "editor.h"
# include "call_out.h"
# include "comm.h"
# include "node.h"
# include "compile.h"

static uindex dindex;		/* driver object index */
static Uint dcount;		/* driver object count */
static sector fragment;		/* swap fragment parameter */
static bool rebuild;		/* rebuild swapfile? */
bool intr;			/* received an interrupt? */

/*
 * NAME:	call_driver_object()
 * DESCRIPTION:	call a function in the driver object
 */
bool call_driver_object(f, func, narg)
frame *f;
char *func;
int narg;
{
    object *driver;
    char *driver_name;

    if (dindex == UINDEX_MAX || dcount != (driver=OBJR(dindex))->count ||
	!(driver->flags & O_DRIVER)) {
	driver_name = conf_driver();
	driver = o_find(driver_name, OACC_READ);
	if (driver == (object *) NULL) {
	    driver = c_compile(f, driver_name, (object *) NULL,
			       (string **) NULL, 0, FALSE);
	}
	dindex = driver->index;
	dcount = driver->count;
    }
    if (!i_call(f, driver, (array *) NULL, func, strlen(func), TRUE, narg)) {
	fatal("missing function in driver object: %s", func);
    }
    return TRUE;
}

/*
 * NAME:	interrupt()
 * DESCRIPTION:	register an interrupt
 */
void interrupt()
{
    intr = TRUE;
}

/*
 * NAME:	endthread()
 * DESCRIPTION:	clean up after a thread has terminated
 */
void endthread()
{
    comm_flush();
    if (ext_cleanup != (void (*) P((void))) NULL) {
	(*ext_cleanup)();
    }
    d_export();
    o_clean();
    i_clear();
    ed_clear();
    ec_clear();

    co_swapcount(d_swapout(fragment));

    if (stop) {
	if (ext_finish != (void (*) P((void))) NULL) {
	    (*ext_finish)();
	}
	comm_finish();
	ed_finish();
# ifdef DEBUG
	swap = 1;
# endif
    }

    if (swap || !m_check()) {
	/*
	 * swap out everything and possibly extend the static memory area
	 */
	d_swapout(1);
	arr_freeall();
	m_purge();
	swap = FALSE;
    }

    if (dump) {
	/*
	 * create a state dump
	 */
	d_swapsync();
	conf_dump();
	dump = FALSE;
	rebuild = TRUE;
    }

    if (stop) {
	sw_finish();
	m_finish();
	exit(0);
    }
}

/*
 * NAME:	errhandler()
 * DESCRIPTION:	default error handler
 */
void errhandler(f, depth)
frame *f;
Int depth;
{
    i_runtime_error(f, (Int) 0);
}

/*
 * NAME:	dgd_main()
 * DESCRIPTION:	the main loop of DGD
 */
int dgd_main(argc, argv)
int argc;
char **argv;
{
    Uint rtime, timeout;
    unsigned short rmtime, mtime;

    if (argc < 2 || argc > 3) {
	P_message("Usage: dgd config_file [dump_file]\012");	/* LF */
	return 2;
    }

    /* initialize */
    dindex = UINDEX_MAX;
    swap = dump = rebuild = intr = stop = FALSE;
    rtime = 0;
    if (!conf_init(argv[1], (argc == 3) ? argv[2] : (char *) NULL, &fragment)) {
	return 2;	/* initialization failed */
    }

    for (;;) {
	/* interrupts */
	if (intr) {
	    intr = FALSE;
	    if (!ec_push((ec_ftn) errhandler)) {
		call_driver_object(cframe, "interrupt", 0);
		i_del_value(cframe->sp++);
		ec_pop();
	    }
	    endthread();
	}

	/* rebuild swapfile */
	if (rebuild) {
	    timeout = co_time(&mtime);
	    if (timeout > rtime || (timeout == rtime && mtime >= rmtime)) {
		rebuild = sw_copy(timeout);
		if (rebuild) {
		    rtime = timeout + 1;
		    rmtime = mtime;
		} else {
		    rtime = 0;
		}
	    }
	}

	/* handle user input */
	timeout = co_delay(rtime, rmtime, &mtime);
	comm_receive(cframe, timeout, mtime);

	/* callouts */
	co_call(cframe);
    }
}
