#include <lua.h>                               
#include <lauxlib.h>                           
#include <lualib.h>
                     
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include <slurm/slurm.h>
#include <slurm/slurm_errno.h>

#define SLURM_EXTRALIB_SUCESS 0
#define SLURM_EXTRALIB_ERROR 1

#define SGA_SLURM_ERRMSG_SIZE 30

static int __slurm_get_nodes (node_info_msg_t **node_buffer_ptr, char* errmsg)
{	
	int i;

	if (slurm_load_node((time_t) NULL, node_buffer_ptr, SHOW_ALL))
	{
		strcpy(errmsg, "Error loading SLURM nodes."); //26
		return SLURM_EXTRALIB_ERROR;
	}
	return SLURM_EXTRALIB_SUCESS;
}

static int __slurm_get_jobs (job_info_msg_t** job_buffer_ptr, char* errmsg)
{
	if ( slurm_load_jobs ((time_t) NULL, job_buffer_ptr, SHOW_ALL) ) {
		strcpy(errmsg, "Error loading SLURM jobs."); //25
		return SLURM_EXTRALIB_ERROR;
	}
	return SLURM_EXTRALIB_SUCESS;
}

int get_all_machines (lua_State *L)
{
	int errnum = SLURM_EXTRALIB_SUCESS;
	node_info_msg_t *node_buffer_ptr = NULL;
	char errmsg[SGA_SLURM_ERRMSG_SIZE];
	errnum = __slurm_get_nodes (&node_buffer_ptr, errmsg);
	if (errnum)
	{
		lua_pushnil(L);
		lua_pushstring(L, errmsg);
		return 2;
	}
	int nodeNum = 0;
	if (node_buffer_ptr!=NULL)
		nodeNum = node_buffer_ptr->record_count;
	// array de nós
	lua_createtable(L, nodeNum, 0);
	int i;
	for (i=1; i<=nodeNum; i++)
	{
		// mapa de informações
		lua_createtable(L, 0, 23);

		// name
		lua_pushstring(L, "Name");
		lua_pushstring(L, node_buffer_ptr->node_array[i-1].name);
		lua_rawset(L, -3);

		//cpus
		lua_pushstring(L, "CPUs");
		lua_pushnumber(L, node_buffer_ptr->node_array[i-1].cpus);
		lua_rawset(L, -3);

		//architecture
		lua_pushstring(L, "Architecture");
		lua_pushstring(L, node_buffer_ptr->node_array[i-1].arch);
		lua_rawset(L, -3);

		//boards
		lua_pushstring(L, "Boards");
		lua_pushnumber(L, node_buffer_ptr->node_array[i-1].boards);
		lua_rawset(L, -3);
	
		//boot time
		lua_pushstring(L, "BootTime");
		lua_pushnumber(L, node_buffer_ptr->node_array[i-1].boot_time);
		lua_rawset(L, -3);

		//cores
		lua_pushstring(L, "Cores");
		lua_pushnumber(L, node_buffer_ptr->node_array[i-1].cores);
		lua_rawset(L, -3);

		//features
		lua_pushstring(L, "Features");
		lua_pushstring(L, node_buffer_ptr->node_array[i-1].features);
		lua_rawset(L, -3);

		//generic resources
		lua_pushstring(L, "GenericResources");
		lua_pushstring(L, node_buffer_ptr->node_array[i-1].gres);
		lua_rawset(L, -3);

		//cpu load
		lua_pushstring(L, "CPULoad");
		lua_pushnumber(L, node_buffer_ptr->node_array[i-1].cpu_load);
		lua_rawset(L, -3);

		//node address
		lua_pushstring(L, "NodeAddress");
		lua_pushstring(L, node_buffer_ptr->node_array[i-1].node_addr);
		lua_rawset(L, -3);

		//node hostname
		lua_pushstring(L, "NodeHostname");
		lua_pushstring(L, node_buffer_ptr->node_array[i-1].node_hostname);
		lua_rawset(L, -3);

		//node state
		lua_pushstring(L, "State");
		int nodeState = node_buffer_ptr->node_array[i-1].node_state;
		switch (nodeState)
		{
			case NODE_STATE_DOWN:
				lua_pushstring(L, "DOWN"); break;
			case NODE_STATE_IDLE:
				lua_pushstring(L, "IDLE"); break;
			case NODE_STATE_ALLOCATED:
				lua_pushstring(L, "ALLOCATED"); break;
			case NODE_STATE_ERROR:
				lua_pushstring(L, "ERROR"); break;	
			case NODE_STATE_MIXED:
				lua_pushstring(L, "MIXED"); break;
			case NODE_STATE_FUTURE:
				lua_pushstring(L, "FUTURE"); break;
			case NODE_STATE_END:
				lua_pushstring(L, "END"); break;
			case NODE_STATE_UNKNOWN:
			default:
				lua_pushstring(L, "UNKNOWN"); break;
		}
		lua_rawset(L, -3);

		//OS
		lua_pushstring(L, "OS");
		lua_pushstring(L, node_buffer_ptr->node_array[i-1].os);
		lua_rawset(L, -3);

		//Real Memory
		lua_pushstring(L, "RealMemory");
		lua_pushnumber(L, node_buffer_ptr->node_array[i-1].real_memory);
		lua_rawset(L, -3);

		//Reason
		lua_pushstring(L, "Reason");
		lua_pushstring(L, node_buffer_ptr->node_array[i-1].reason);
		lua_rawset(L, -3);

		//Reason Time
		lua_pushstring(L, "ReasonTime");
		lua_pushnumber(L, node_buffer_ptr->node_array[i-1].reason_time);
		lua_rawset(L, -3);

		//Reason uid
		lua_pushstring(L, "ReasonUID");
		lua_pushnumber(L, node_buffer_ptr->node_array[i-1].reason_uid);
		lua_rawset(L, -3);

		//Slurmd Start Time 
		lua_pushstring(L, "ExecutionDaemonStartTime");
		lua_pushnumber(L, node_buffer_ptr->node_array[i-1].slurmd_start_time);
		lua_rawset(L, -3);

		//Sockets
		lua_pushstring(L, "Sockets");
		lua_pushnumber(L, node_buffer_ptr->node_array[i-1].sockets);
		lua_rawset(L, -3);

		//Threads
		lua_pushstring(L, "Threads");
		lua_pushnumber(L, node_buffer_ptr->node_array[i-1].threads);
		lua_rawset(L, -3);

		//Temporary Disk 
		lua_pushstring(L, "TmpDisk");
		lua_pushnumber(L, node_buffer_ptr->node_array[i-1].tmp_disk);
		lua_rawset(L, -3);

		//Weight
		lua_pushstring(L, "Weight");
		lua_pushnumber(L, node_buffer_ptr->node_array[i-1].weight);
		lua_rawset(L, -3);
	
		//Version
		lua_pushstring(L, "Version");
		lua_pushstring(L, node_buffer_ptr->node_array[i-1].version);
		lua_rawset(L, -3);

		//coloca o mapa na lista de nós
		lua_rawseti(L, -2, i);
	}
	slurm_free_node_info_msg(node_buffer_ptr);
	return 1;
}

int get_all_jobs (lua_State *L)
{
	int errnum = SLURM_EXTRALIB_SUCESS;
	job_info_msg_t* job_buffer_ptr = NULL;
	char errmsg[SGA_SLURM_ERRMSG_SIZE];
	errnum = __slurm_get_jobs (&job_buffer_ptr, errmsg);
	if (errnum)
	{
		lua_pushnil(L);
		lua_pushstring(L, errmsg);
		return 2;
	}
	int jobNum = 0;
	if (job_buffer_ptr!=NULL)
		jobNum = job_buffer_ptr->record_count;
	// array de nós
	lua_createtable(L, jobNum, 0);
	int i;
	for (i=1; i<=jobNum; i++)
	{
		// mapa de informações
		lua_createtable(L, 0, 21);

		// name
		lua_pushstring(L, "Name");
		lua_pushstring(L, job_buffer_ptr->job_array[i-1].name);
		lua_rawset(L, -3);

		// exit code
		lua_pushstring(L, "ExitCode");
		lua_pushnumber(L, job_buffer_ptr->job_array[i-1].exit_code);
		lua_rawset(L, -3);

		// job id
		lua_pushstring(L, "JobID");
		lua_pushnumber(L, job_buffer_ptr->job_array[i-1].job_id);
		lua_rawset(L, -3);

		// job state
		lua_pushstring(L, "State");
		int jobState = job_buffer_ptr->job_array[i-1].job_state;
		switch (jobState)
		{
			case JOB_PENDING:
				lua_pushstring(L, "PENDING"); break;
			case JOB_RUNNING:
				lua_pushstring(L, "RUNNING"); break;
			case JOB_SUSPENDED:
				lua_pushstring(L, "SUSPENDED"); break;
			case JOB_COMPLETE:
				lua_pushstring(L, "COMPLETE"); break;
			case JOB_CANCELLED:
				lua_pushstring(L, "CANCELLED"); break;
			case JOB_FAILED:
				lua_pushstring(L, "FAILED"); break;
			case JOB_TIMEOUT:
				lua_pushstring(L, "TIMEOUT"); break;
			case JOB_NODE_FAIL:
				lua_pushstring(L, "NODE FAIL"); break;
			case JOB_PREEMPTED:
				lua_pushstring(L, "PREEMPTED"); break;
			case JOB_BOOT_FAIL:
				lua_pushstring(L, "BOOT FAIL"); break;
			case JOB_END:
				lua_pushstring(L, "END"); break;
			default:
				lua_pushstring(L, "UNKNOWN"); break;
		}
		lua_rawset(L, -3);

		// eligible time
		lua_pushstring(L, "EligibleTime");
		lua_pushnumber(L, job_buffer_ptr->job_array[i-1].eligible_time);
		lua_rawset(L, -3);

		// end time
		lua_pushstring(L, "EndTime");
		lua_pushnumber(L, job_buffer_ptr->job_array[i-1].end_time);
		lua_rawset(L, -3);

		// groupID
		lua_pushstring(L, "GroupID");
		lua_pushnumber(L, job_buffer_ptr->job_array[i-1].group_id);
		lua_rawset(L, -3);

		// licenses
		lua_pushstring(L, "Licenses");
		lua_pushstring(L, job_buffer_ptr->job_array[i-1].licenses);
		lua_rawset(L, -3);

		// cpus -> max of cpus used
		lua_pushstring(L, "CPUs");
		lua_pushnumber(L, job_buffer_ptr->job_array[i-1].max_cpus);
		lua_rawset(L, -3);

		// partition
		lua_pushstring(L, "Queue");
		lua_pushstring(L, job_buffer_ptr->job_array[i-1].partition);
		lua_rawset(L, -3);

		// start time
		lua_pushstring(L, "StartTime");
		lua_pushnumber(L, job_buffer_ptr->job_array[i-1].start_time);
		lua_rawset(L, -3);

		// state description
		lua_pushstring(L, "StateDescription");
                lua_pushstring(L, job_buffer_ptr->job_array[i-1].state_desc);
                lua_rawset(L, -3);

		// standard error
		lua_pushstring(L, "StdErr");
                lua_pushstring(L, job_buffer_ptr->job_array[i-1].std_err);
                lua_rawset(L, -3);		

		// standard output
		lua_pushstring(L, "StdOut");
                lua_pushstring(L, job_buffer_ptr->job_array[i-1].std_err);
                lua_rawset(L, -3);

		// standard input
		lua_pushstring(L, "StdIn");
                lua_pushstring(L, job_buffer_ptr->job_array[i-1].std_err);
                lua_rawset(L, -3);

		// submit time
		lua_pushstring(L, "SubmitTime");
                lua_pushnumber(L, job_buffer_ptr->job_array[i-1].submit_time);
                lua_rawset(L, -3);

		// user ID
		lua_pushstring(L, "UserId");
                lua_pushnumber(L, job_buffer_ptr->job_array[i-1].user_id);
                lua_rawset(L, -3);

		// working directory
		lua_pushstring(L, "WorkDir");
                lua_pushstring(L, job_buffer_ptr->job_array[i-1].work_dir);
                lua_rawset(L, -3);

		lua_rawseti(L, -2, i);
	}
	printf("Reached end\n");
	slurm_free_job_info_msg (job_buffer_ptr);
	return 1;
}

int luaopen_extralib(lua_State *L)
{
	
	lua_register(L, "get_all_machines", get_all_machines);
	lua_register(L, "get_all_jobs", get_all_jobs);
	return 0;
}
