/*
 * msg.h - prototypes of msg-hv converting functions
 */

#ifndef _MSG_H
#define _MSG_H

#include <EXTERN.h>
#include <stdint.h>
#include <perl.h>
#include <XSUB.h>
#include <slurm/slurm.h>

typedef char* charp;

/*
 * store an uint16_t into AV
 */
inline static int av_store_uint16_t(AV* av, int index, uint16_t val)
{
	SV* sv = NULL;
	/* Perl has a hard time figuring out the an unsigned int is
	   equal to INFINITE or NO_VAL since they are treated as
	   signed ints so we will handle this here. */
	if(val == INFINITE16)
		sv = newSViv(INFINITE);
	else if(val == NO_VAL16)
		sv = newSViv(NO_VAL);
	else
		sv = newSViv(val);

	if (av_store(av, (I32)index, sv) == NULL) {
		SvREFCNT_dec(sv);
		return -1;
	}
	return 0;
}

/*
 * store an uint32_t into AV
 */
inline static int av_store_uint32_t(AV* av, int index, uint32_t val)
{
	SV* sv = NULL;
	/* Perl has a hard time figuring out the an unsigned int is
	   equal to INFINITE or NO_VAL since they are treated as
	   signed ints so we will handle this here. */
	if(val == INFINITE)
		sv = newSViv(INFINITE);
	else if(val == NO_VAL)
		sv = newSViv(NO_VAL);
	else
		sv = newSViv(val);

	if (av_store(av, (I32)index, sv) == NULL) {
		SvREFCNT_dec(sv);
		return -1;
	}
	return 0;
}

/*
 * store an int into AV
 */
inline static int av_store_int(AV* av, int index, int val)
{
	SV* sv = newSViv(val);

	if (av_store(av, (I32)index, sv) == NULL) {
		SvREFCNT_dec(sv);
		return -1;
	}
	return 0;
}

/*
 * store an uint32_t into AV
 */
inline static int av_store_int32_t(AV* av, int index, int32_t val)
{
	return av_store_int(av, index, val);
}

/*
 * store a string into HV
 */
inline static int hv_store_charp(HV* hv, const char *key, charp val)
{
	SV* sv = NULL;

	if (val) {
		sv = newSVpv(val, 0);

		if (!key || hv_store(hv, key, (I32)strlen(key), sv, 0) == NULL) {
			SvREFCNT_dec(sv);
			return -1;
		}
	}
	return 0;
}

/*
 * store an unsigned 64b int into HV
 */
inline static int hv_store_uint64_t(HV* hv, const char *key, uint64_t val)
{
	SV* sv = NULL;
	/* Perl has a hard time figuring out the an unsigned int is
	   equal to INFINITE or NO_VAL since they are treated as
	   signed ints so we will handle this here. */
	if(val == (uint64_t)INFINITE)
		sv = newSViv(INFINITE);
	else if(val == (uint64_t)NO_VAL)
		sv = newSViv(NO_VAL);
	else
		sv = newSVuv(val);

	if (!key || hv_store(hv, key, (I32)strlen(key), sv, 0) == NULL) {
		SvREFCNT_dec(sv);
		return -1;
	}
	return 0;
}

/*
 * store an unsigned 32b int into HV
 */
inline static int hv_store_uint32_t(HV* hv, const char *key, uint32_t val)
{
	SV* sv = NULL;
	/* Perl has a hard time figuring out the an unsigned int is
	   equal to INFINITE or NO_VAL since they are treated as
	   signed ints so we will handle this here. */
	if(val == INFINITE)
		sv = newSViv(INFINITE);
	else if(val == NO_VAL)
		sv = newSViv(NO_VAL);
	else
		sv = newSVuv(val);

	if (!key || hv_store(hv, key, (I32)strlen(key), sv, 0) == NULL) {
		SvREFCNT_dec(sv);
		return -1;
	}
	return 0;
}

/*
 * store an unsigned 16b int into HV
 */
inline static int hv_store_uint16_t(HV* hv, const char *key, uint16_t val)
{
	SV* sv = NULL;
	/* Perl has a hard time figuring out the an unsigned int is
	   equal to INFINITE or NO_VAL since they are treated as
	   signed ints so we will handle this here. */
	if(val == INFINITE16)
		sv = newSViv(INFINITE);
	else if(val == NO_VAL16)
		sv = newSViv(NO_VAL);
	else
		sv = newSVuv(val);

	if (!key || hv_store(hv, key, (I32)strlen(key), sv, 0) == NULL) {
		SvREFCNT_dec(sv);
		return -1;
	}
	return 0;
}

/*
 * store an unsigned 8b int into HV
 */
inline static int hv_store_uint8_t(HV* hv, const char *key, uint8_t val)
{
	SV* sv = NULL;
	/* Perl has a hard time figuring out the an unsigned int is
	   equal to INFINITE or NO_VAL since they are treated as
	   signed ints so we will handle this here. */
	if(val == INFINITE8)
		sv = newSViv(INFINITE);
	else if(val == NO_VAL8)
		sv = newSViv(NO_VAL);
	else
		sv = newSVuv(val);

	if (!key || hv_store(hv, key, (I32)strlen(key), sv, 0) == NULL) {
		SvREFCNT_dec(sv);
		return -1;
	}
	return 0;
}

/*
 * store a uid_t into HV
 */
inline static int hv_store_uid_t(HV* hv, const char *key, uid_t val)
{
	SV* sv = newSVuv(val);

	if (!key || hv_store(hv, key, (I32)strlen(key), sv, 0) == NULL) {
		SvREFCNT_dec(sv);
		return -1;
	}
	return 0;
}

/*
 * store a signed int into HV
 */
inline static int hv_store_int(HV* hv, const char *key, int val)
{
	SV* sv = newSViv(val);

	if (!key || hv_store(hv, key, (I32)strlen(key), sv, 0) == NULL) {
		SvREFCNT_dec(sv);
		return -1;
	}
	return 0;
}

/*
 * store a double
 */
inline static int hv_store_double(HV* hv, const char *key, double val)
{
	SV* sv = newSVnv(val);

	if (!key || hv_store(hv, key, (I32)strlen(key), sv, 0) == NULL) {
		SvREFCNT_dec(sv);
		return -1;
	}
	return 0;
}

/*
 * store a signed 32b int into HV
 */
inline static int hv_store_int32_t(HV* hv, const char *key, int32_t val)
{
	return hv_store_int(hv, key, val);
}

/*
 * store a bool into HV
 */
inline static int hv_store_bool(HV* hv, const char *key, bool val)
{
	if (!key || hv_store(hv, key, (I32)strlen(key), (val ? &PL_sv_yes : &PL_sv_no), 0) == NULL) {
		return -1;
	}
	return 0;
}

/*
 * store a time_t into HV
 */
inline static int hv_store_time_t(HV* hv, const char *key, time_t val)
{
	SV* sv = newSVuv(val);

	if (!key || hv_store(hv, key, (I32)strlen(key), sv, 0) == NULL) {
		SvREFCNT_dec(sv);
		return -1;
	}
	return 0;
}

/*
 * store a SV into HV
 */
inline static int hv_store_sv(HV* hv, const char *key, SV* sv)
{
	if (!key || hv_store(hv, key, (I32)strlen(key), sv, 0) == NULL) {
		return -1;
	}
	return 0;
}

/*
 * store a PTR into HV
 * set classname to Nullch to avoid blessing the created SV.
 */
inline static int hv_store_ptr(HV* hv, const char *key, void* ptr, const char *classname)
{
	SV* sv = NULL;

	/*
	 * if ptr == NULL and we call sv_setref_pv() and store the sv in hv,
	 * sv_isobject() will fail later when FETCH_PTR_FIELD.
	 */
	if(ptr) {
		sv = newSV(0);
		sv_setref_pv(sv, classname, ptr);
		if (!key || hv_store(hv, key, (I32)strlen(key), sv, 0) == NULL) {
			SvREFCNT_dec(sv);
			return -1;
		}
	}

	return 0;
}

#define SV2int(sv)      SvIV(sv)
#define SV2int32_t(sv)  SvIV(sv)
#define SV2uint64_t(sv) SvUV(sv)
#define SV2uint32_t(sv) SvUV(sv)
#define SV2uint16_t(sv) SvUV(sv)
#define SV2uint8_t(sv)  SvUV(sv)
#define SV2time_t(sv)   SvUV(sv)
#define SV2charp(sv)    SvPV_nolen(sv)
#define SV2bool(sv)     SvTRUE(sv)

#if 0
/* Error on some 32-bit systems */
#define SV2ptr(sv)      SvIV(SvRV(sv))
#else
static inline void * SV2ptr(SV *sv)
{
	void * ptr = (void *) ((intptr_t) SvIV(SvRV(sv)));
	return ptr;
}
#endif

#define FETCH_FIELD(hv, ptr, field, type, required) \
	do { \
		SV** svp; \
		if ( (svp = hv_fetch (hv, #field, strlen(#field), FALSE)) ) { \
			ptr->field = (type) (SV2##type (*svp)); \
		} else if (required) { \
			Perl_warn (aTHX_ "Required field \"" #field "\" missing in HV at %s:%d",__FILE__,__LINE__); \
			return -1; \
		} \
	} while (0)

#define FETCH_FIELD_NAME(hv, ptr, cfield, pfield, type, required) \
	do { \
		SV **svp; \
		if ((svp = hv_fetch(hv, #pfield, strlen(#pfield), FALSE))) { \
			ptr->cfield = (type) (SV2##type(*svp)); \
		} else if (required) { \
			Perl_warn(aTHX_ "Required field \"" #pfield \
					"\" missing in HV at %s:%d", \
				  __FILE__, __LINE__); \
			return -1; \
		} \
	} while (0)

#define FETCH_PTR_FIELD(hv, ptr, field, classname, required) \
	do { \
		SV** svp; \
		if ( (svp = hv_fetch (hv, #field, strlen(#field), FALSE)) ) { \
			if (classname) { \
				if (! ( sv_isobject(*svp) && \
				        SvTYPE(SvRV(*svp)) == SVt_PVMG && \
				        sv_derived_from(*svp, classname)) ) { \
					Perl_croak(aTHX_ "field %s is not an object of %s", #field, classname); \
				} \
			} \
			ptr->field = (typeof(ptr->field)) (SV2ptr (*svp)); \
		} else if (required) { \
			Perl_warn (aTHX_ "Required field \"" #field "\" missing in HV"); \
			return -1; \
		} \
	} while (0)

#define STORE_FIELD(hv, ptr, field, type) \
	do { \
		if (hv_store_##type(hv, #field, ptr->field)) { \
			Perl_warn (aTHX_ "Failed to store field \"" #field "\""); \
			return -1; \
		} \
	} while (0)

#define STORE_FIELD_NAME(hv, ptr, cfield, pfield, type) \
	do { \
		if (hv_store_##type(hv, #pfield, ptr->cfield)) { \
			Perl_warn(aTHX_ "Failed to store field \"" #pfield \
					"\""); \
			return -1; \
		} \
	} while (0)

#define STORE_PTR_FIELD(hv, ptr, field, classname) \
	do { \
		if (hv_store_ptr(hv, #field, ptr->field, classname)) { \
			Perl_warn (aTHX_ "Failed to store field \"" #field "\""); \
			return -1; \
		} \
	} while (0)

inline static int step_id_to_hv(slurm_step_id_t *step_id, HV *hv)
{
	STORE_FIELD(hv, step_id, job_id, uint32_t);
	STORE_FIELD(hv, step_id, step_het_comp, uint32_t);
	STORE_FIELD(hv, step_id, step_id, uint32_t);

	return 0;
}

inline static int hv_to_step_id(slurm_step_id_t *step_id, HV *hv)
{
	FETCH_FIELD(hv, step_id, job_id, uint32_t, TRUE);
	FETCH_FIELD(hv, step_id, step_het_comp, uint32_t, TRUE);
	FETCH_FIELD(hv, step_id, step_id, uint32_t, TRUE);

	return 0;
}

#endif /* _MSG_H */
