/*
 * step.c - convert data between step related messages and perl HVs
 */

#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>
#include <slurm/slurm.h>
#include "ppport.h"

#include "slurm-perl.h"

/*
 * convert job_step_info_t to perl HV
 */
int
job_step_info_to_hv(job_step_info_t *step_info, HV *hv)
{
	int j;
	AV *av;
	HV *step_id_hv = (HV*)sv_2mortal((SV*)newHV());

	step_id_to_hv(&step_info->step_id, step_id_hv);
	hv_store_sv(hv, "step_id", newRV((SV*)step_id_hv));

	STORE_FIELD(hv, step_info, array_job_id, uint32_t);
	STORE_FIELD(hv, step_info, array_task_id, uint32_t);
	if (step_info->tres_per_node)
		STORE_FIELD(hv, step_info, tres_per_node, charp);

	if (step_info->name)
		STORE_FIELD(hv, step_info, name, charp);
	if (step_info->network)
		STORE_FIELD(hv, step_info, network, charp);
	if (step_info->nodes)
		STORE_FIELD(hv, step_info, nodes, charp);
	av = newAV();
	for (j = 0; ; j += 2) {
		if(step_info->node_inx[j] == -1)
			break;
		av_store_int(av, j, step_info->node_inx[j]);
		av_store_int(av, j+1, step_info->node_inx[j+1]);
	}
	hv_store_sv(hv, "node_inx", newRV_noinc((SV*)av));
	STORE_FIELD(hv, step_info, num_cpus, uint32_t);
	STORE_FIELD(hv, step_info, num_tasks, uint32_t);
	if(step_info->partition)
		STORE_FIELD(hv, step_info, partition, charp);
	if(step_info->resv_ports)
		STORE_FIELD(hv, step_info, resv_ports, charp);
	STORE_FIELD(hv, step_info, run_time, time_t);
	STORE_FIELD(hv, step_info, start_time, time_t);
	STORE_FIELD(hv, step_info, time_limit, uint32_t);
	STORE_FIELD(hv, step_info, user_id, uint32_t);
	STORE_FIELD(hv, step_info, state, uint32_t);

	return 0;
}

/* 
 * convert perl HV to job_step_info_t
 */
int
hv_to_job_step_info(HV *hv, job_step_info_t *step_info)
{
	SV **svp;
	AV *av;
	int i, n;
	HV *step_id_hv;

	svp = hv_fetch(hv, "step_id", 7, FALSE);
	if (svp && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV) {
		step_id_hv = (HV*)SvRV(*svp);
	} else {
		step_id_hv = (HV*)sv_2mortal((SV*)newHV());
	}

	memset(step_info, 0, sizeof(job_step_info_t));

	hv_to_step_id(&step_info->step_id, step_id_hv);

	FETCH_FIELD(hv, step_info, array_job_id, uint32_t, TRUE);
	FETCH_FIELD(hv, step_info, array_task_id, uint32_t, TRUE);
	FETCH_FIELD(hv, step_info, tres_per_node, charp, FALSE);
	FETCH_FIELD(hv, step_info, name, charp, FALSE);
	FETCH_FIELD(hv, step_info, network, charp, FALSE);
	FETCH_FIELD(hv, step_info, nodes, charp, FALSE);

	svp = hv_fetch(hv, "node_inx", 8, FALSE);
	if (svp && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVAV) {
		av = (AV*)SvRV(*svp);
		n = av_len(av) + 2; /* for trailing -1 */
		step_info->node_inx = xmalloc(n * sizeof(int));
		for (i = 0 ; i < n-1; i += 2) {
			step_info->node_inx[i] = (int)SvIV(*(av_fetch(av, i ,FALSE)));
			step_info->node_inx[i+1] = (int)SvIV(*(av_fetch(av, i+1 ,FALSE)));
		}
		step_info->node_inx[n-1] = -1;
	} else {
		/* nothing to do */
	}

	FETCH_FIELD(hv, step_info, num_cpus, uint32_t, TRUE);
	FETCH_FIELD(hv, step_info, num_tasks, uint32_t, TRUE);
	FETCH_FIELD(hv, step_info, partition, charp, FALSE);
	FETCH_FIELD(hv, step_info, resv_ports, charp, FALSE);
	FETCH_FIELD(hv, step_info, run_time, time_t, TRUE);
	FETCH_FIELD(hv, step_info, start_time, time_t, TRUE);
	FETCH_FIELD(hv, step_info, time_limit, uint32_t, TRUE);
	FETCH_FIELD(hv, step_info, user_id, uint32_t, TRUE);
	FETCH_FIELD(hv, step_info, state, uint32_t, TRUE);

	return 0;
}

/*
 * convert job_step_info_response_msg_t to perl HV
 */
int
job_step_info_response_msg_to_hv(
	job_step_info_response_msg_t *job_step_info_msg, HV *hv)
{
	int i;
	AV* av;
	HV* hv_info;

	STORE_FIELD(hv, job_step_info_msg, last_update, time_t);
	/* job_step_count implied in job_steps */
	av = newAV();
	for(i = 0; i < job_step_info_msg->job_step_count; i ++) {
		hv_info = newHV();
		if (job_step_info_to_hv(
			    job_step_info_msg->job_steps + i, hv_info) < 0) {
			SvREFCNT_dec(hv_info);
			SvREFCNT_dec(av);
			return -1;
		}
		av_store(av, i, newRV_noinc((SV*)hv_info));
	}
	hv_store_sv(hv, "job_steps", newRV_noinc((SV*)av));
	return 0;
}

/* 
 * convert perl HV to job_step_info_response_msg_t
 */
int
hv_to_job_step_info_response_msg(HV *hv,
		job_step_info_response_msg_t *step_info_msg)
{
	int i, n;
	SV **svp;
	AV *av;

	memset(step_info_msg, 0, sizeof(job_step_info_response_msg_t));

	FETCH_FIELD(hv, step_info_msg, last_update, time_t, TRUE);

	svp = hv_fetch(hv, "job_steps", 9, FALSE);
	if (! (svp && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVAV)) {
		Perl_warn (aTHX_ "job_steps is not an array reference in HV for job_step_info_response_msg_t");
		return -1;
	}

	av = (AV*)SvRV(*svp);
	n = av_len(av) + 1;
	step_info_msg->job_step_count = n;

	step_info_msg->job_steps = xmalloc(n * sizeof(job_step_info_t));
	for (i = 0; i < n; i ++) {
		svp = av_fetch(av, i, FALSE);
		if (! (svp && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV)) {
			Perl_warn (aTHX_ "element %d in job_steps is not valid", i);
			return -1;
		}
		if (hv_to_job_step_info((HV*)SvRV(*svp), &step_info_msg->job_steps[i]) < 0) {
			Perl_warn (aTHX_ "failed to convert element %d in job_steps", i);
			return -1;
		}
	}
	return 0;
}


/*
 * convert slurm_step_layout_t to perl HV
 */
int
slurm_step_layout_to_hv(slurm_step_layout_t *step_layout, HV *hv)
{
	AV* av, *av2;
	int i, j;

	if (step_layout->front_end)
		STORE_FIELD(hv, step_layout, front_end, charp);
	STORE_FIELD(hv, step_layout, node_cnt, uint16_t);
	if (step_layout->node_list)
		STORE_FIELD(hv, step_layout, node_list, charp);
	else {
		Perl_warn(aTHX_ "node_list missing in slurm_step_layout_t");
		return -1;
	}
	STORE_FIELD(hv, step_layout, plane_size, uint16_t);
	av = newAV();
	for (i = 0; i < step_layout->node_cnt; i ++)
		av_store_uint16_t(av, i, step_layout->tasks[i]);
	hv_store_sv(hv, "tasks", newRV_noinc((SV*)av));
	STORE_FIELD(hv, step_layout, task_cnt, uint32_t);
	STORE_FIELD(hv, step_layout, task_dist, uint16_t);
	av = newAV();
	for (i = 0; i < step_layout->node_cnt; i ++) {
		av2 = newAV();
		for (j = 0; j < step_layout->tasks[i]; j ++)
			av_store_uint32_t(av2, i, step_layout->tids[i][j]);
		av_store(av, i, newRV_noinc((SV*)av2));
	}
	hv_store_sv(hv, "tids", newRV_noinc((SV*)av));

	return 0;
}

/* convert job_step_pids_t to perl HV */
int
job_step_pids_to_hv(job_step_pids_t *pids, HV *hv)
{
	int i;
	AV *av;
	
	STORE_FIELD(hv, pids, node_name, charp);
	/* pid_cnt implied in pid array */
	av = newAV();
	for (i = 0; i < pids->pid_cnt; i ++) {
		av_store_uint32_t(av, i, pids->pid[i]);
	}
	hv_store_sv(hv, "pid", newRV_noinc((SV*)av));

	return 0;
}

/* convert job_step_pids_response_msg_t to HV */
int
job_step_pids_response_msg_to_hv(job_step_pids_response_msg_t *pids_msg, HV *hv)
{
	int i = 0;
	ListIterator itr;
	AV *av;
	HV *hv_pids;
	job_step_pids_t *pids;
	HV *step_id_hv = (HV*)sv_2mortal((SV*)newHV());

	step_id_to_hv(&pids_msg->step_id, step_id_hv);
	hv_store_sv(hv, "step_id", newRV((SV*)step_id_hv));

	av = newAV();
	itr = slurm_list_iterator_create(pids_msg->pid_list);
	while ((pids = (job_step_pids_t *)slurm_list_next(itr))) {
		hv_pids = newHV();
		if (job_step_pids_to_hv(pids, hv_pids) < 0) {
			Perl_warn(aTHX_ "failed to convert job_step_pids_t to hv for job_step_pids_response_msg_t");
			SvREFCNT_dec(hv_pids);
			SvREFCNT_dec(av);
			slurm_list_iterator_destroy(itr);
			return -1;
		}
		av_store(av, i++, newRV_noinc((SV*)hv_pids));
	}
	slurm_list_iterator_destroy(itr);
	hv_store_sv(hv, "pid_list", newRV_noinc((SV*)av));

	return 0;
}

/*
 * convert job_step_stat_t to perl HV
 */
int
job_step_stat_to_hv(job_step_stat_t *stat, HV *hv)
{
	HV *hv_pids;

	STORE_PTR_FIELD(hv, stat, jobacct, "Slurm::jobacctinfo_t");
	STORE_FIELD(hv, stat, num_tasks, uint32_t);
	STORE_FIELD(hv, stat, return_code, uint32_t);
	hv_pids = newHV();
	if (job_step_pids_to_hv(stat->step_pids, hv_pids) < 0) {
		Perl_warn(aTHX_ "failed to convert job_step_pids_t to hv for job_step_stat_t");
		SvREFCNT_dec(hv_pids);
		return -1;
	}
	hv_store_sv(hv, "step_pids", newRV_noinc((SV*)hv_pids));

	return 0;
}

/*
 * convert job_step_stat_response_msg_t to perl HV
 */
int
job_step_stat_response_msg_to_hv(job_step_stat_response_msg_t *stat_msg, HV *hv)
{
	int i = 0;
	ListIterator itr;
	job_step_stat_t *stat;
	AV *av;
	HV *hv_stat;
	HV *step_id_hv = (HV*)sv_2mortal((SV*)newHV());

	step_id_to_hv(&stat_msg->step_id, step_id_hv);
	hv_store_sv(hv, "step_id", newRV((SV*)step_id_hv));

	av = newAV();
	itr = slurm_list_iterator_create(stat_msg->stats_list);
	while ((stat = (job_step_stat_t *)slurm_list_next(itr))) {
		hv_stat = newHV();
		if(job_step_stat_to_hv(stat, hv_stat) < 0) {
			Perl_warn(aTHX_ "failed to convert job_step_stat_t to hv for job_step_stat_response_msg_t");
			SvREFCNT_dec(hv_stat);
			SvREFCNT_dec(av);
			slurm_list_iterator_destroy(itr);
			return -1;
		}
		av_store(av, i++, newRV_noinc((SV*)hv_stat));
	}
	slurm_list_iterator_destroy(itr);
	hv_store_sv(hv, "stats_list", newRV_noinc((SV*)av));

	return 0;
}
