#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"

#include <slurm/slurm.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include "slurm-perl.h"

#include "const-c.inc"

/* these declaration are not in slurm.h */
extern void slurm_api_set_conf_file(char *pathname);
extern void slurm_api_clear_config(void);

#define xfree(__p) \
        slurm_xfree((void **)&(__p), __FILE__, __LINE__, "")
extern void slurm_xfree(void **, const char *, int, const char *);

struct slurm {
	node_info_msg_t *node_info_msg;
	partition_info_msg_t *part_info_msg;
	reserve_info_msg_t *reserve_info_msg;
	slurm_ctl_conf_t *ctl_conf;
	job_info_msg_t *job_info_msg;
	job_step_info_response_msg_t *job_step_info_msg;
};
typedef struct slurm * slurm_t;
static struct slurm slurm;
#define SINGLETON (&slurm)

static void
free_slurm(void)
{
	if(slurm.node_info_msg) {
		slurm_free_node_info_msg(slurm.node_info_msg);
		slurm.node_info_msg = NULL;
	}
	if(slurm.part_info_msg) {
		slurm_free_partition_info_msg(slurm.part_info_msg);
		slurm.part_info_msg = NULL;
	}
	if(slurm.reserve_info_msg) {
		slurm_free_reservation_info_msg(slurm.reserve_info_msg);
		slurm.reserve_info_msg = NULL;
	}
	if(slurm.ctl_conf) {
		slurm_free_ctl_conf(slurm.ctl_conf);
		slurm.ctl_conf = NULL;
	}
	if(slurm.job_info_msg) {
		slurm_free_job_info_msg(slurm.job_info_msg);
		slurm.job_info_msg = NULL;
	}
	if(slurm.job_step_info_msg) {
		slurm_free_job_step_info_response_msg(slurm.job_step_info_msg);
		slurm.job_step_info_msg = NULL;
	}
}

typedef uint16_t signo_t;
static signo_t
signame_to_no(char* signame)
{
	int i = 0;
	struct {
		char * name;
		signo_t no;
	} map [] = {
		{"SIGHUP", SIGHUP}, {"SIGINT", SIGINT}, {"SIGQUIT", SIGQUIT}, {"SIGILL", SIGILL}, {"SIGTRAP", SIGTRAP},
		{"SIGABRT", SIGABRT}, {"SIGBUS", SIGBUS}, {"SIGFPE", SIGFPE}, {"SIGKILL", SIGKILL}, {"SIGUSR1", SIGUSR1},
		{"SIGSEGV", SIGSEGV}, {"SIGUSR2", SIGUSR2}, {"SIGPIPE", SIGPIPE}, {"SIGALRM", SIGALRM}, {"SIGTERM", SIGTERM},
		{"SIGCHLD", SIGCHLD}, {"SIGCONT", SIGCONT}, {"SIGSTOP", SIGSTOP}, {"SIGTSTP", SIGTSTP}, {"SIGTTIN", SIGTTIN},
		{"SIGTTOU", SIGTTOU}, {"SIGURG", SIGURG}, {"SIGXCPU", SIGXCPU}, {"SIGXFSZ", SIGXFSZ}, {"SIGVTALRM", SIGVTALRM},
		{"SIGPROF", SIGPROF}, {"SIGWINCH", SIGWINCH}, {"SIGIO", SIGIO}, {"SIGPWR", SIGPWR}, {"SIGSYS", SIGSYS},
		{NULL, 0}
	};

	for( i = 0; map[i].name != NULL; i ++) {
		if (strcasecmp (map[i].name, signame) == 0)
			return map[i].no;
	}
	return 0;
}


static SV* sarb_cb_sv = NULL;
static void
sarb_cb(uint32_t jobid)
{
	int count;
	dSP;

	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	XPUSHs(sv_2mortal(newSVuv(jobid)));
	PUTBACK;

	count = call_sv(sarb_cb_sv, G_VOID);

	SPAGAIN;
	/*
	 * The callback should return nothing, but if there is no return statement
	 * the value of the last expression will be returned, even in context of G_VOID.
	 */
	//if (count != 0) {
	//	fprintf(stderr, "Callback returned %d values\n", count);
	//	Perl_croak (aTHX_ "Callback should not return values");
	//}
	FREETMPS;
	LEAVE;
}

static SV* task_start_cb_sv = NULL;
static SV* task_finish_cb_sv = NULL;
static void
task_start_cb(launch_tasks_response_msg_t *resp)
{
	/* TODO */
}
static void
task_finish_cb(task_exit_msg_t *exit)
{
	/* TODO */
}

/********************************************************************/

MODULE = Slurm		PACKAGE = Slurm		PREFIX=slurm_
INCLUDE: const-xs.inc
PROTOTYPES: ENABLE

######################################################################
# 	MISC FUNCTIONS
######################################################################
slurm_t
get_slurm(slurm_t self = NULL, char *conf_file=NULL)
		slurm_t	RETVAL = NULL;
	CODE:
		if(conf_file) {
			slurm_api_set_conf_file(conf_file);
			free_slurm();
		}
		RETVAL = self;
	OUTPUT:
		RETVAL

void
DESTROY(slurm_t self)
	CODE:
		free_slurm();

void
slurm_set_config_file(slurm_t self = NO_INIT, char *conf_file=NULL)
	CODE:
		if(conf_file) {
			slurm_api_set_conf_file(conf_file);
			free_slurm();
		}

void
slurm_clear_config(slurm_t self = NO_INIT)
	CODE:
		slurm_api_clear_config();

int
slurm_get_errno(slurm_t self)
	C_ARGS:

char*
slurm_strerror(slurm_t self, int errnum = 0)
	C_ARGS:
		(errnum ? errnum : slurm_get_errno())

######################################################################
# 	RESOURCE ALLOCATION FUNCTIONS
######################################################################
HV*
slurm_allocate_resources(slurm_t self, HV* job_req = NULL)
	PREINIT:
		job_desc_msg_t job_desc_msg;
		resource_allocation_response_msg_t* resp_msg = NULL;
		int rc;
	CODE:
		if (hv_to_job_desc_msg(job_req, &job_desc_msg) < 0) {
			XSRETURN_UNDEF;
		}
		rc = slurm_allocate_resources(&job_desc_msg, &resp_msg);
		free_job_desc_msg_memory(&job_desc_msg);
		if(rc != SLURM_SUCCESS) {
			slurm_free_resource_allocation_response_msg(resp_msg);
			XSRETURN_UNDEF;
		}
		RETVAL = newHV();
		sv_2mortal((SV*)RETVAL);
		resource_allocation_response_msg_to_hv(resp_msg, RETVAL);
		slurm_free_resource_allocation_response_msg(resp_msg);
	OUTPUT:
		RETVAL

HV*
slurm_allocate_resources_blocking(slurm_t self, HV* job_req = NULL, time_t timeout = 0, SV* callback = NULL)
	PREINIT:
		job_desc_msg_t job_desc_msg;
		resource_allocation_response_msg_t *resp_msg = NULL;
	CODE:
		if (hv_to_job_desc_msg(job_req, &job_desc_msg) < 0) {
			XSRETURN_UNDEF;
		}
		sarb_cb_sv = callback;
		resp_msg = slurm_allocate_resources_blocking(&job_desc_msg, timeout,
				callback == NULL ? NULL : sarb_cb);
		free_job_desc_msg_memory(&job_desc_msg);
		if (resp_msg != NULL) {
			RETVAL = newHV();
			sv_2mortal((SV*)RETVAL);
			resource_allocation_response_msg_to_hv(resp_msg, RETVAL);
			slurm_free_resource_allocation_response_msg(resp_msg);
		}
		else {
			XSRETURN_UNDEF;
		}
	OUTPUT:
		RETVAL


HV*
slurm_allocation_lookup(slurm_t self, U32 job_id)
	PREINIT:
		job_alloc_info_response_msg_t* resp_msg = NULL;
		int rc;
	CODE:
		rc = slurm_allocation_lookup(job_id, &resp_msg);
		if(rc != SLURM_SUCCESS) {
			slurm_free_job_alloc_info_response_msg(resp_msg);
			XSRETURN_UNDEF;
		}
		RETVAL = newHV();
		sv_2mortal((SV*)RETVAL);
		job_alloc_info_response_msg_to_hv(resp_msg, RETVAL);
		slurm_free_job_alloc_info_response_msg(resp_msg);
	OUTPUT:
		RETVAL

HV*
slurm_allocation_lookup_lite(slurm_t self, U32 job_id)
	PREINIT:
		resource_allocation_response_msg_t* resp_msg = NULL;
		int rc;
	CODE:
		rc = slurm_allocation_lookup_lite(job_id, &resp_msg);
		if(rc != SLURM_SUCCESS) {
			slurm_free_resource_allocation_response_msg(resp_msg);
			XSRETURN_UNDEF;
		}
		RETVAL = newHV();
		sv_2mortal((SV*)RETVAL);
		resource_allocation_response_msg_to_hv(resp_msg, RETVAL);
		slurm_free_resource_allocation_response_msg(resp_msg);
	OUTPUT:
		RETVAL

char*
slurm_read_hostfile(slurm_t self, char* filename, int n)
	PREINIT:
		char* hostlist;
	CODE:
		hostlist = slurm_read_hostfile(filename, n);
		if(hostlist == NULL) {
			XSRETURN_UNDEF;
		} else {
			New(0, RETVAL, strlen(hostlist) + 1, char);
			Copy(hostlist, RETVAL, strlen(hostlist) + 1, char);
			free(hostlist);
		}
	OUTPUT:
		RETVAL

HV*
slurm_submit_batch_job(slurm_t self, HV* job_req = NULL)
	PREINIT:
		job_desc_msg_t job_desc_msg;
		submit_response_msg_t* resp_msg = NULL;
		int rc;
	CODE:
		if(hv_to_job_desc_msg(job_req, &job_desc_msg) < 0) {
			XSRETURN_UNDEF;
		}
		rc = slurm_submit_batch_job(&job_desc_msg, &resp_msg);
		free_job_desc_msg_memory(&job_desc_msg);
		if(rc != SLURM_SUCCESS) {
			slurm_free_submit_response_response_msg(resp_msg);
			XSRETURN_UNDEF;
		}
		RETVAL = newHV();
		sv_2mortal((SV*)RETVAL);
		submit_response_msg_to_hv(resp_msg, RETVAL);
		slurm_free_submit_response_response_msg(resp_msg);
	OUTPUT:
		RETVAL

int
slurm_job_will_run(slurm_t self, HV* job_req = NULL)
	PREINIT:
		job_desc_msg_t job_desc_msg;
	CODE:
		if (hv_to_job_desc_msg(job_req, &job_desc_msg) < 0) {
			XSRETURN_UNDEF;
		}
		RETVAL = slurm_job_will_run(&job_desc_msg);
		free_job_desc_msg_memory(&job_desc_msg);
	OUTPUT:
		RETVAL

######################################################################
#	JOB/STEP SIGNALING FUNCTIONS
######################################################################
int
slurm_kill_job(slurm_t self, U32 jobid, signo_t signal = 0, U16 batch_flag = 0)
	C_ARGS:
                jobid, signal, batch_flag

int
slurm_kill_job_step(slurm_t self, U32 jobid, U32 stepid, signo_t signal = 0)
	C_ARGS:
		jobid, stepid, signal

int
slurm_signal_job(slurm_t self, U32 jobid, signo_t signal = 0)
	C_ARGS:
		jobid, signal

int
slurm_signal_job_step(slurm_t self, U32 jobid, U32 stepid, signo_t signal = 0)
	C_ARGS:
		jobid, stepid, signal


######################################################################
#	JOB/STEP COMPLETION FUNCTIONS
######################################################################
int
slurm_complete_job(slurm_t self, U32 jobid, U32 job_rc = 0)
	C_ARGS:
		jobid, job_rc

int
slurm_terminate_job(slurm_t self, U32 jobid)
	C_ARGS:
		jobid

int
slurm_terminate_job_step(slurm_t self, U32 jobid, U32 stepid)
	C_ARGS:
		jobid, stepid


######################################################################
#	SLURM CONTROL CONFIGURATION READ/PRINT/UPDATE FUNCTIONS
######################################################################
MODULE=Slurm PACKAGE=Slurm PREFIX=slurm_
void
slurm_api_version(slurm_t self, OUTLIST int major, OUTLIST int minor, OUTLIST int micro)
	PREINIT:
		long version;
	CODE:
		version = slurm_api_version();
		major = SLURM_VERSION_MAJOR(version);
		minor = SLURM_VERSION_MINOR(version);
		micro = SLURM_VERSION_MICRO(version);

HV*
slurm_load_ctl_conf(slurm_t self = NULL)
	PREINIT:
		slurm_ctl_conf_t *new_ctl_conf;
		int rc;
	CODE:
                rc = slurm_load_ctl_conf(
			self->ctl_conf ? self->ctl_conf->last_update : 0,
			&new_ctl_conf);
		if(rc == SLURM_SUCCESS) {
			slurm_free_ctl_conf(self->ctl_conf);
			self->ctl_conf = new_ctl_conf;
		} else if(slurm_get_errno() == SLURM_NO_CHANGE_IN_DATA) {
			/* nothing to do */
		} else {
			XSRETURN_UNDEF;
		}
		RETVAL = newHV();
		sv_2mortal((SV*)RETVAL);
                slurm_ctl_conf_to_hv(self->ctl_conf, RETVAL);
	OUTPUT:
		RETVAL

# To be implemented in perl code
#void
#slurm_print_ctl_conf(slurm_t self, HV* conf)

######################################################################
#	SLURM RESERVATION CONFIGURATION READ/PRINT/UPDATE FUNCTIONS
######################################################################
HV*
slurm_load_reservations(slurm_t self = NULL)
	PREINIT:
		reserve_info_msg_t* new_reserve_info_msg = NULL;
		int rc;
	CODE:
                rc = slurm_load_reservations(
			self->reserve_info_msg ?
			self->reserve_info_msg->last_update : 0,
			&new_reserve_info_msg);
		if(rc == SLURM_SUCCESS) {
			slurm_free_reservation_info_msg(self->reserve_info_msg);
			self->reserve_info_msg = new_reserve_info_msg;
		} else if(slurm_get_errno() == SLURM_NO_CHANGE_IN_DATA) {
			/* nothing to do */
		} else {
			XSRETURN_UNDEF;
		}
		RETVAL = newHV();
		sv_2mortal((SV*)RETVAL);
                reserve_info_msg_to_hv(self->reserve_info_msg, RETVAL);
	OUTPUT:
		RETVAL

# To be implemented in perl code
# slurm_print_reservation_info_msg
# slurm_print_reservation_info
# slurm_sprint_reservation_info


######################################################################
#	SLURM JOB CONTROL CONFIGURATION READ/PRINT/UPDATE FUNCTIONS
######################################################################
HV*
slurm_load_jobs(slurm_t self = NULL, U16 show_flags = 0)
	PREINIT:
		job_info_msg_t* new_job_info_msg = NULL;
		int rc;
	CODE:
		rc = slurm_load_jobs(
			self->job_info_msg
			? self->job_info_msg->last_update : 0,
			&new_job_info_msg, show_flags);
		if(rc == SLURM_SUCCESS) {
			slurm_free_job_info_msg(self->job_info_msg);
			self->job_info_msg = new_job_info_msg;
		} else if(slurm_get_errno() == SLURM_NO_CHANGE_IN_DATA) {
			/* nothing to do */
		} else {
			XSRETURN_UNDEF;
		}
		RETVAL = newHV();
		sv_2mortal((SV*)RETVAL);
                job_info_msg_to_hv(self->job_info_msg, RETVAL);
	OUTPUT:
		RETVAL

# To be implemented in perl code
#slurm_print_job_info_msg()
#slurm_print_job_info()
#slurm_sprint_job_info()

time_t
slurm_get_end_time(slurm_t self, U32 jobid)
	PREINIT:
		time_t end_time;
		int rc;
	CODE:
rc = slurm_get_end_time(jobid, &end_time);
		if(rc != SLURM_SUCCESS) {
			XSRETURN_UNDEF;
		} else
			RETVAL = end_time;
	OUTPUT:
		RETVAL

long
slurm_get_rem_time(slurm_t self, U32 jobid)
	C_ARGS:
                jobid

U32
slurm_pid2jobid(slurm_t self, U32 pid)
	PREINIT:
		int rc;
		uint32_t job_id;
	CODE:
		rc = slurm_pid2jobid(pid, &job_id);
		if(rc != SLURM_SUCCESS) {
			XSRETURN_UNDEF;
		} else
			RETVAL = job_id;
	OUTPUT:
		RETVAL

int
slurm_update_job(slurm_t self, HV* job_info = NULL)
	PREINIT:
		job_desc_msg_t job_msg;
	CODE:
		if(hv_to_job_desc_msg(job_info, &job_msg) < 0) {
			XSRETURN_UNDEF;
		}
                RETVAL = slurm_update_job(&job_msg);
		free_job_desc_msg_memory(&job_msg);
	OUTPUT:
		RETVAL

int
slurm_get_select_jobinfo_int_type(slurm_t self, SV* jobinfo = NULL, int data_type)
	CODE:
		if(jobinfo) {
			uint32_t data;
			RETVAL = slurm_get_select_jobinfo(
				(dynamic_plugin_data_t *)SV2ptr(jobinfo),
				 data_type, &data);
			RETVAL = data;
		} else {
			RETVAL = -1;
		}
	OUTPUT:
		RETVAL

char *
slurm_get_select_jobinfo_char_type(slurm_t self, SV* jobinfo = NULL, int data_type)
	CODE:
		if(jobinfo) {
			char *data = NULL;
			slurm_get_select_jobinfo(
				(dynamic_plugin_data_t *)SV2ptr(jobinfo),
				data_type, &data);
			RETVAL = data;
		} else {
			RETVAL = NULL;
		}
	OUTPUT:
		RETVAL

U16
slurm_get_select_nodeinfo_subcnt(slurm_t self, SV *nodeinfo, int state)
	CODE:
		RETVAL = -1;
		if(nodeinfo) {
	                uint16_t tmp16;
			int rc = slurm_get_select_nodeinfo(
			   	(dynamic_plugin_data_t *)SV2ptr(nodeinfo),
				SELECT_NODEDATA_SUBCNT, state, &tmp16);
			if(rc != -1) {
				RETVAL = tmp16;
			}
		}
	OUTPUT:
		RETVAL

U16
slurm_get_select_nodeinfo_subgrp_size(slurm_t self, SV *nodeinfo)
	CODE:
		RETVAL = -1;
		if(nodeinfo) {
			uint16_t tmp16;
			int rc = slurm_get_select_nodeinfo(
				(dynamic_plugin_data_t *)SV2ptr(nodeinfo),
				SELECT_NODEDATA_SUBGRP_SIZE, 0, &tmp16);
			if(rc != -1) {
				RETVAL = tmp16;
			}
		}
	OUTPUT:
		RETVAL

char *
slurm_get_select_nodeinfo_cpuids(slurm_t self, SV *nodeinfo)
	CODE:
		if(nodeinfo) {
			char *tmp_char = NULL;
			slurm_get_select_nodeinfo(
				(dynamic_plugin_data_t *)SV2ptr(nodeinfo),
				SELECT_NODEDATA_STR, 0, &tmp_char);
			RETVAL = tmp_char;
                } else {
			RETVAL = NULL;
		}
	OUTPUT:
		RETVAL

int
slurm_job_cpus_allocated_on_node_id(slurm_t self, SV* job_res = NULL, int node_id)
	CODE:
		if(job_res) {
			RETVAL = slurm_job_cpus_allocated_on_node_id(
				(job_resources_t *)SV2ptr(job_res), node_id);
		} else {
			RETVAL = 0;
		}
	OUTPUT:
		RETVAL

int
slurm_job_cpus_allocated_on_node(slurm_t self, SV* job_res = NULL, char *node_name)
	CODE:
		if(job_res) {
			RETVAL = slurm_job_cpus_allocated_on_node(
				(job_resources_t *)SV2ptr(job_res), node_name);
		} else {
			RETVAL = 0;
		}
	OUTPUT:
		RETVAL


######################################################################
#	SLURM JOB STEP CONFIGURATION READ/PRINT/UPDATE FUNCTIONS
######################################################################
HV*
slurm_get_job_steps(slurm_t self = NULL, U16 show_flags = 0, U32 jobid = 0, U32 stepid = 0)
	PREINIT:
		int rc;
		job_step_info_response_msg_t* resp_msg;
	CODE:
                rc = slurm_get_job_steps(
			self->job_step_info_msg ?
			self->job_step_info_msg->last_update : 0,
			jobid, stepid, &resp_msg, show_flags);
		if(rc == SLURM_SUCCESS) {
			slurm_free_job_step_info_response_msg(
				self->job_step_info_msg);
			self->job_step_info_msg = resp_msg;
		} else if(slurm_get_errno() == SLURM_NO_CHANGE_IN_DATA) {
			/* nothing to do */
		} else {
			XSRETURN_UNDEF;
		}
		RETVAL = newHV();
		sv_2mortal((SV*)RETVAL);
		job_step_info_response_msg_to_hv(self->job_step_info_msg, RETVAL);
	OUTPUT:
		RETVAL

# To be implemented in perl code
# slurm_print_job_step_info_response_msg
# slurm_print_job_step_info_msg
# slurm_sprint_job_step_info

HV*
slurm_job_step_layout_get(slurm_t self, U32 jobid, U32 stepid)
	PREINIT:
		slurm_step_layout_t *layout;
	CODE:
                layout = slurm_job_step_layout_get(jobid, stepid);
		if(layout == NULL) {
			XSRETURN_UNDEF;
		} else {
			RETVAL = newHV();
			sv_2mortal((SV*)RETVAL);
			slurm_step_layout_to_hv(layout, RETVAL);
			slurm_job_step_layout_free(layout);
		}
	OUTPUT:
		RETVAL



######################################################################
#	SLURM NODE CONFIGURATION READ/PRINT/UPDATE FUNCTIONS
######################################################################
HV*
slurm_load_node(slurm_t self = NULL, U16 show_flags = 0)
	PREINIT:
		node_info_msg_t* new_node_info_msg = NULL;
		int rc;
	CODE:
		rc = slurm_load_node(
			self->node_info_msg
			? self->node_info_msg->last_update : 0,
			&new_node_info_msg, show_flags);
		if(rc == SLURM_SUCCESS) {
			slurm_free_node_info_msg(self->node_info_msg);
			self->node_info_msg = new_node_info_msg;
		} else if(slurm_get_errno() == SLURM_NO_CHANGE_IN_DATA) {
			/* nothing to do */
		} else {
			XSRETURN_UNDEF;
		}
		RETVAL = newHV();
		sv_2mortal((SV*)RETVAL);
                node_info_msg_to_hv(self->node_info_msg, RETVAL);
	OUTPUT:
		RETVAL

# To be implemented in perl code
# slurm_print_node_info_msg
# slurm_print_node_table
# slurm_sprint_node_table

int
slurm_update_node(slurm_t self, HV* update_req = NULL)
	PREINIT:
		update_node_msg_t node_msg;
	INIT:
		if(hv_to_update_node_msg(update_req, &node_msg) < 0) {
			XSRETURN_UNDEF;
		}
	C_ARGS:
                &node_msg

######################################################################
#	SLURM PARTITION CONFIGURATION READ/PRINT/UPDATE FUNCTIONS
######################################################################
HV*
slurm_load_partitions(slurm_t self = NULL, U16 show_flags = 0)
	PREINIT:
		partition_info_msg_t* new_part_info_msg;
		int rc;
	CODE:
                rc = slurm_load_partitions(
			self->part_info_msg ?
			self->part_info_msg->last_update : 0,
			&new_part_info_msg, show_flags);
		if(rc == SLURM_SUCCESS) {
			slurm_free_partition_info_msg(self->part_info_msg);
			self->part_info_msg = new_part_info_msg;
		} else if(slurm_get_errno() == SLURM_NO_CHANGE_IN_DATA) {
			/* nothing to do */
		} else {
			XSRETURN_UNDEF;
		}
		RETVAL = newHV();
		sv_2mortal((SV*)RETVAL);
                partition_info_msg_to_hv(self->part_info_msg, RETVAL);
	OUTPUT:
		RETVAL

# To be implemented in perl code
# slurm_print_partition_info_msg
# slurm_print_partition_info
# slurm_sprint_partition_info

int
slurm_update_partition(slurm_t self, HV* part_info = NULL)
	PREINIT:
		update_part_msg_t update_msg;
	INIT:
		if(hv_to_update_part_msg(part_info, &update_msg) < 0) {
			XSRETURN_UNDEF;
		}
	C_ARGS:
                &update_msg

int
slurm_delete_partition(slurm_t self, char* part_name)
	PREINIT:
		delete_part_msg_t delete_msg;
	INIT:
		delete_msg.name = part_name;
	C_ARGS:
                &delete_msg


######################################################################
#	SLURM PING/RECONFIGURE/SHUTDOWN FUNCTIONS
######################################################################
int
slurm_ping(slurm_t self, U16 primary = 1)
	C_ARGS:
		primary

int
slurm_reconfigure(slurm_t self)
	C_ARGS:

int
slurm_shutdown(slurm_t self, U16 core = 0)
	C_ARGS:
		core



######################################################################
#	SLURM JOB SUSPEND FUNCTIONS
######################################################################
int
slurm_suspend(slurm_t self, U32 jobid)
	C_ARGS:
		jobid

int
slurm_resume(slurm_t self, U32 jobid)
	C_ARGS:
		jobid

int
slurm_requeue(slurm_t self, U32 jobid)
	C_ARGS:
		jobid


######################################################################
#	SLURM JOB CHECKPOINT FUNCTIONS
######################################################################
int
slurm_checkpoint_able(slurm_t self, U32 jobid, U32 stepid, OUT time_t start_time)
	C_ARGS:
		jobid, stepid, &start_time

int
slurm_checkpoint_disable(slurm_t self, U32 jobid, U32 stepid)
	C_ARGS:
		jobid, stepid

int
slurm_checkpoint_enable(slurm_t self, U32 jobid, U32 stepid)
	C_ARGS:
		jobid, stepid

int
slurm_checkpoint_create(slurm_t self, U32 jobid, U32 stepid, U16 max_wait, char* image_dir)
	C_ARGS:
		jobid, stepid, max_wait, image_dir

int
slurm_checkpoint_vacate(slurm_t self, U32 jobid, U32 stepid, U16 max_wait, char* image_dir)
	C_ARGS:
		jobid, stepid, max_wait, image_dir

int
slurm_checkpoint_restart(slurm_t self, U32 jobid, U32 stepid, U16 stick, char *image_dir)
	C_ARGS:
		jobid, stepid, stick, image_dir

int
slurm_checkpoint_complete(slurm_t self, U32 jobid, U32 stepid, time_t begin_time, U32 error_code, char* error_msg)
	C_ARGS:
		jobid, stepid, begin_time, error_code, error_msg

int
slurm_checkpoint_error(slurm_t self, U32 jobid, U32 stepid, OUTLIST U32 error_code, OUTLIST char* error_msg)
	PREINIT:
		char* err_msg = NULL;
	CODE:
		error_code = SLURM_SUCCESS;
		RETVAL = slurm_checkpoint_error(jobid, stepid, (uint32_t *)&error_code, &err_msg);
		Newz(0, error_msg, strlen(err_msg), char);
		Copy(err_msg, error_msg, strlen(err_msg), char);
		xfree(err_msg);
	OUTPUT:
		RETVAL

######################################################################
#	SLURM TRIGGER FUNCTIONS
######################################################################
int slurm_set_trigger(slurm_t self, HV* trigger_info = NULL)
	PREINIT:
		trigger_info_t trigger_set;
	INIT:
               if(hv_to_trigger_info(trigger_info, &trigger_set) < 0) {
			XSRETURN_UNDEF;
		}
	C_ARGS:
		&trigger_set

int slurm_clear_trigger(slurm_t self, HV* trigger_info = NULL)
	PREINIT:
		trigger_info_t trigger_clear;
	INIT:
		if(hv_to_trigger_info(trigger_info, &trigger_clear) < 0) {
			XSRETURN_UNDEF;
		}
	C_ARGS:
		&trigger_clear

HV*
slurm_get_triggers(slurm_t self)
	PREINIT:
		trigger_info_msg_t* trig_info_msg;
		int rc;
	CODE:
		rc = slurm_get_triggers(&trig_info_msg);
		if(rc == SLURM_SUCCESS) {
			RETVAL = newHV();
			sv_2mortal((SV*)RETVAL);
        	        trigger_info_msg_to_hv(trig_info_msg, RETVAL);
			slurm_free_trigger_msg(trig_info_msg);
		} else {
			XSRETURN_UNDEF;
		}
	OUTPUT:
		RETVAL

##################################################################
#               HOSTLIST FUNCTIONS
##################################################################
MODULE=Slurm PACKAGE=Slurm::Hostlist PREFIX=slurm_hostlist_

int
slurm_hostlist_count(hostlist_t hl = NULL)
	OUTPUT:
		RETVAL

hostlist_t
slurm_hostlist_create(char* hostlist)

int
slurm_hostlist_find(hostlist_t hl = NULL, char* hostname)
	OUTPUT:
		RETVAL

int
slurm_hostlist_push(hostlist_t hl = NULL, char* hosts)
	OUTPUT:
		RETVAL

int
slurm_hostlist_push_host(hostlist_t hl = NULL, char* host)
	OUTPUT:
		RETVAL

char*
slurm_hostlist_ranged_string(hostlist_t hl = NULL)
	PREINIT:
		size_t size = 1024;
		int rc = 0;
	CODE:
		Newz(0, RETVAL, size, char);
		while((rc = slurm_hostlist_ranged_string(hl, size, RETVAL)) == -1) {
			size *= 2;
			Renew(RETVAL, size, char);
		}
	OUTPUT:
		RETVAL

char*
slurm_hostlist_shift(hostlist_t hl = NULL)
	PREINIT:
		char *host = NULL;
	CODE:
		host = slurm_hostlist_shift(hl);
		if (host == NULL) {
			XSRETURN_UNDEF;
		}
		Newz(0, RETVAL, strlen(host) + 1, char);
		Copy(host, RETVAL, strlen(host) + 1, char);
		free(host);
	OUTPUT:
		RETVAL

void
slurm_hostlist_uniq(hostlist_t hl = NULL)
        CODE:
		slurm_hostlist_uniq(hl);

void
DESTROY(hl)
		hostlist_t hl=NULL;
	CODE:
		slurm_hostlist_destroy(hl);




##############################################
#	SLURM TASK SPAWNING FUNCTIONS
# I did not try the following functions at all
##############################################
MODULE=Slurm PACKAGE=Slurm::Stepctx PREFIX=slurm_step_ctx_

slurm_step_ctx_t *
slurm_step_ctx_create(slurm_step_ctx_t *ctx = NO_INIT, HV* req = NULL)
	PREINIT:
		slurm_step_ctx_params_t params;
	CODE:
		if (hv_to_slurm_step_ctx_params(req, &params) < 0) {
			XSRETURN_UNDEF;
		}
		RETVAL = slurm_step_ctx_create(&params);
		if (RETVAL == NULL) {
			XSRETURN_UNDEF;
		}
	OUTPUT:
		RETVAL

# XXX: slurm_step_ctx_get is divided into the following methods
U32
slurm_step_ctx_get_jobid(slurm_step_ctx_t *ctx = NULL)
	CODE:
		if(slurm_step_ctx_get(ctx, SLURM_STEP_CTX_JOBID, &RETVAL) != SLURM_SUCCESS) {
			XSRETURN_UNDEF;
		}
	OUTPUT:
		RETVAL

U32
slurm_step_ctx_get_stepid(slurm_step_ctx_t *ctx = NULL)
	CODE:
		if(slurm_step_ctx_get(ctx, SLURM_STEP_CTX_STEPID, &RETVAL) != SLURM_SUCCESS) {
			XSRETURN_UNDEF;
		}
	OUTPUT:
		RETVAL

U32
slurm_step_ctx_get_num_hosts(slurm_step_ctx_t *ctx = NULL)
	PREINIT:
		uint32_t num_hosts;
	CODE:
		if(slurm_step_ctx_get(ctx, SLURM_STEP_CTX_NUM_HOSTS, &num_hosts) != SLURM_SUCCESS) {
			XSRETURN_UNDEF;
		}
		RETVAL = num_hosts;
	OUTPUT:
		RETVAL

AV*
slurm_step_ctx_get_tasks(slurm_step_ctx_t *ctx = NULL)
	PREINIT:
		int i;
		uint32_t num_hosts;
		uint16_t *tasks;
	CODE:
		if(slurm_step_ctx_get(ctx, SLURM_STEP_CTX_NUM_HOSTS, &num_hosts) != SLURM_SUCCESS) {
			XSRETURN_UNDEF;
		}
		if(slurm_step_ctx_get(ctx, SLURM_STEP_CTX_TASKS, &tasks) != SLURM_SUCCESS) {
			XSRETURN_UNDEF;
		}
		RETVAL = newAV();
		sv_2mortal((SV*)RETVAL);
		for(i = 0; i < num_hosts; i ++) {
			if(av_store_uint16_t(RETVAL, i, tasks[i]) < 0) {
				XSRETURN_UNDEF;
			}
		}
	OUTPUT:
		RETVAL

AV*
slurm_step_ctx_get_tid(slurm_step_ctx_t *ctx = NULL, U32 index)
	PREINIT:
		int i;
		uint16_t *tasks;
		uint32_t *tids;
	CODE:
		if(slurm_step_ctx_get(ctx, SLURM_STEP_CTX_TASKS, &tasks) != SLURM_SUCCESS) {
			XSRETURN_UNDEF;
		}
		if(slurm_step_ctx_get(ctx, SLURM_STEP_CTX_TID, index, &tids) != SLURM_SUCCESS) {
			XSRETURN_UNDEF;
		}
		RETVAL = newAV();
		sv_2mortal((SV*)RETVAL);
		for(i = 0; i < tasks[index]; i ++) {
			if(av_store_uint32_t(RETVAL, i, tids[i]) < 0) {
				XSRETURN_UNDEF;
			}
		}
	OUTPUT:
		RETVAL

# XXX: should we return it as a HV* ?
#slurm_step_ctx_get_resp(slurm_step_ctx_t *ctx)

# XXX: the returned value is no longer valid if ctx goes away
slurm_cred_t *
slurm_step_ctx_get_cred(slurm_step_ctx_t *ctx = NULL)
	CODE:
		if(slurm_step_ctx_get(ctx, SLURM_STEP_CTX_CRED, &RETVAL) != SLURM_SUCCESS) {
			XSRETURN_UNDEF;
		}
	OUTPUT:
		RETVAL

# XXX: the returned value is no longer valid if ctx goes away
switch_jobinfo_t *
slurm_step_ctx_get_switch_job(slurm_step_ctx_t *ctx = NULL)
	CODE:
		if(slurm_step_ctx_get(ctx, SLURM_STEP_CTX_SWITCH_JOB, &RETVAL) != SLURM_SUCCESS) {
			XSRETURN_UNDEF;
		}
	OUTPUT:
		RETVAL

char*
slurm_step_ctx_get_host(slurm_step_ctx_t *ctx = NULL, U32 index)
	CODE:
		if(slurm_step_ctx_get(ctx, SLURM_STEP_CTX_HOST, index, &RETVAL) != SLURM_SUCCESS) {
			XSRETURN_UNDEF;
		}
	OUTPUT:
		RETVAL

AV*
slurm_step_ctx_get_user_managed_sockets(slurm_step_ctx_t *ctx = NULL)
	PREINIT:
		int i;
		int tasks_requested;
		int *sockets;
	CODE:
		if(slurm_step_ctx_get(ctx, SLURM_STEP_CTX_USER_MANAGED_SOCKETS, &tasks_requested, &sockets) != SLURM_SUCCESS) {
			XSRETURN_UNDEF;
		}
		RETVAL = newAV();
		sv_2mortal((SV*)RETVAL);
		for(i = 0; i < tasks_requested; i ++) {
			if(av_store_int(RETVAL, i, sockets[i]) < 0) {
				XSRETURN_UNDEF;
			}
		}
	OUTPUT:
		RETVAL


int
slurm_step_ctx_daemon_per_node_hack(slurm_step_ctx_t *ctx = NULL)
	C_ARGS:
		ctx

# TODO
#int
#slurm_jobinfo_ctx_get(slurm_t self, switch_jobinfo_t *job_info, int data_type, OUTPUT void* data)
#	C_ARGS:
#		job_info, data_type, data

void
DESTROY(slurm_step_ctx_t *ctx = NULL)
	CODE:
		if(slurm_step_ctx_destroy(ctx) != SLURM_SUCCESS) {
			Perl_croak(aTHX_ "Failed to destory slurm_step_ctx");
		}


MODULE=Slurm PACKAGE=Slurm::Stepctx PREFIX=slurm_step_
int
slurm_step_launch(slurm_step_ctx_t *ctx = NULL, HV* hv = NULL, SV* start_cb = NULL, SV* finish_cb = NULL)
	PREINIT:
		slurm_step_launch_callbacks_t callbacks = {NULL, NULL};
		slurm_step_launch_params_t params;
	CODE:
		if(finish_cb != NULL) {
			task_finish_cb_sv = finish_cb;
			callbacks.task_finish = task_finish_cb;
		}
		if(start_cb != NULL) {
			task_start_cb_sv = start_cb;
			callbacks.task_start = task_start_cb;
		}
		if(hv_to_slurm_step_launch_params(hv, &params) < 0) {
			RETVAL = SLURM_ERROR;
		} else {
			RETVAL = slurm_step_launch(ctx, &params, &callbacks);
		}
	OUTPUT:
		RETVAL


int
slurm_step_launch_wait_start(slurm_step_ctx_t *ctx = NULL)
	C_ARGS:
		ctx


void
slurm_step_launch_wait_finish(slurm_step_ctx_t *ctx = NULL)
	C_ARGS:
		ctx

void
slurm_step_launch_abort(slurm_step_ctx_t *ctx = NULL)
	C_ARGS:
		ctx
