#include <stdarg.h>
#include <string.h>
#include <epp.h>

void __prolog_halt_hook(int i, void *unused) {
    EPP_ProcessUnblockAll();
}

int pl_init_from_state(const char *statefile) {
    char *argv[] = { (char*)statefile, "-q", "-f", "none" };
    int retval;

    retval = PL_initialise(sizeof(argv)/sizeof(argv[0]), argv);

    if (!retval)
	PL_cleanup(1);

    PL_install_readline();
    PL_on_halt(__prolog_halt_hook, NULL);
    
    return(retval);
}

int pl_shutdown(void) {
    return(PL_cleanup(0));
}

int pl_console(void) {
    return(PL_toplevel());
}

int __swi_put_params(const char *p, term_t t0, va_list vl) {
    int retval = 0;
    va_list arg;
    void *ptr;

    va_copy(arg, vl);

    while (p && *p) {
        switch(*p) {
            case 'l':
        	PL_put_integer(t0, va_arg(arg, long));
    		break;

	    case 'L':
		ptr = va_arg(arg, long*);
		PL_put_variable(t0);
		break;
		
	    case 's':
		PL_put_string_chars(t0, va_arg(arg, const char*));
		break;

	    case 'S':
		ptr = va_arg(arg, const char**);
		PL_put_variable(t0);
		break;
		
	    case 'f':
		PL_put_float(t0, va_arg(arg, double));
		break;

	    case 'F':
		ptr = va_arg(arg, double*);
		PL_put_variable(t0);
		break;

	    case 'p':
		PL_put_pointer(t0, va_arg(arg, void*));
		break;

	    case 'P':
		ptr = va_arg(arg, void**);
		PL_put_variable(t0);
		break;

	    case '_':
		PL_put_variable(t0);
		break;

	    default:
		retval = PL_ERROR_PAL;
		goto error;
	}
	    
	p++;
	t0 = t0 + 1;
    }

error:
    va_end(arg);
    return(retval);
}

int __swi_get_params(const char *p, term_t t0, va_list vl) {
    char *tmp;
    int retval = 0;
    va_list arg;
    const char *s;
    double d;
    long l;

    va_copy(arg, vl);

    while (p && *p) {
        switch(*p) {

            case 'l':
        	l = va_arg(arg, long);
    		break;

            case 'L':
        	if (PL_get_long(t0, va_arg(arg, long*)) == PL_FALSE) { retval = PL_ERROR_PAL; goto error; }
    		break;

	    case 's':
        	s = va_arg(arg, const char*);
		break;
		
	    case 'S':
		tmp = NULL;
        	if (PL_get_chars(t0, &tmp, CVT_ALL | BUF_DISCARDABLE) == PL_FALSE) {
		    retval = PL_ERROR_PAL; goto error;
		} else {
		    *va_arg(arg, const char**) = tmp ? strdup(tmp) : NULL;
		}
		break;

	    case 'f':
        	d = va_arg(arg, double);
		break;

	    case 'F':
        	if (PL_get_float(t0, va_arg(arg, double*)) == PL_FALSE) { retval = PL_ERROR_PAL; goto error; };
		break;

	    case 'p':
        	s = (const char*)va_arg(arg, void*);
		break;

	    case 'P':
        	if (PL_get_pointer(t0, va_arg(arg, void**)) == PL_FALSE) { retval = PL_ERROR_PAL; goto error; };
		break;

	    case '_':
		break;

	    default:
		retval = PL_ERROR_PAL;
		goto error;
	}
	    
	p++;
	t0 = t0 + 1;
    }

error:    
    va_end(arg);
    return(retval);
}
