# 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 swap;		/* are objects to be swapped out? */
static bool dump;		/* is the program to dump? */
bool intr;			/* received an interrupt? */
static bool stop;		/* is the program to terminate? */

/*
 * 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);
	}
	dindex = driver->index;
	dcount = driver->count;
    }
    if (!i_call(f, driver, func, strlen(func), TRUE, narg)) {
	fatal("missing function in driver object: %s", func);
    }
    return TRUE;
}

/*
 * NAME:	swapout()
 * DESCRIPTION:	indicate that objects are to be swapped out
 */
void swapout()
{
    swap = TRUE;
}

/*
 * NAME:	dump_state()
 * DESCRIPTION:	indicate that the state must be dumped
 */
void dump_state()
{
    dump = TRUE;
}

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

/*
 * NAME:	finish()
 * DESCRIPTION:	indicate that the program must finish
 */
void finish()
{
    stop = 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();

    if (fragment != 0) {
	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;
    }

    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;
{
    bool swrebuild;
    Uint timeout;
    unsigned short mtime;

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

    /* initialize */
    dindex = UINDEX_MAX;
    swap = dump = intr = stop = FALSE;
    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)) {
		endthread();
	    } else {
		call_driver_object(cframe, "interrupt", 0);
		i_del_value(cframe->sp++);
		ec_pop();
		endthread();
	    }
	}

	/* rebuild swapfile */
	swrebuild = sw_copy();

	/* handle user input */
	timeout = co_delay(&mtime);
	if (swrebuild &&
	    (mtime == 0xffff || timeout > 1 || (timeout == 1 && mtime != 0))) {
	    /*
	     * wait no longer than one second if the swapfile has to be
	     * rebuilt
	     */
	    timeout = 1;
	    mtime = 0;
	}
	comm_receive(cframe, timeout, mtime);

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