/* standard Net-SNMP includes */
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>

/* include our parent header */
//#include "ifTable.h"
//#include <currentAlarmTable.h>
//#include <currentAlarmTrapRegistry.h>
//#include <emsSysInfo.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdlib.h>
#define AGENTX_ADDRESS		"tcp:localhost:706"
#define PORT 9034 // port we’re listening on for alarms

static int keep_running;

void who_broke_the_rattle( int fd, void *data );
void periodicStatus();
long getTimeForSleep();
void waitTime(long timeForSleepInSec);
void getHrAndMin(int *hr, int *min, int *sec);


static RETSIGTYPE stop_server(int a) {
    keep_running = 0;

	/* Close the socket with EMS */
	//closeEmsCommunicationSocket();

	/* Close the thread for fetching data from DB*/
}




/****************************************************************
 *
 * FUNCTION NAME: main
 *
 * DESCRIPTION: The main method for the snmpV3 agent. It performs
 * initialization of agent, snmp and startup activities.
 *
 * NOTES: 
 *
 * RETURNS:
 ****************************************************************/
int main()
{
	int agentx_subagent = 1;
	fd_set master; // master file descriptor list
	fd_set read_fds; // temp file descriptor list for select()

	struct sockaddr_in myaddr; // server address
	struct sockaddr_in remoteaddr; // client address
	int addrlen;

	int fdmax; // maximum file descriptor number
	int listener_sockfd; // listening socket descriptor
	int newfd; // newly accept()ed socket descriptor
	int block ;

	int count = 0;
	int count_snmp = 0;
	int i;

	struct timeval  timeout;
	struct timeval *tvp = &timeout;

	//Periodic Status thread id
    pthread_t statusThreadId;

	netsnmp_log_handler *logh;
	int             priority = LOG_DEBUG;
	int             pri_max  = LOG_EMERG;

	char buffer[14];

	/* Logs error output from the SNMP agent to the standard error stream.*/
	//snmp_enable_stderrlog();

    debug_register_tokens("snmpV3Agent,currentAlarmTable");
    snmp_set_do_debugging(1);

	/* Logging to a different file */
	logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_FILE, priority);
	if (logh) 
	{
		logh->pri_max = pri_max;
		logh->token   = strdup("/root/AGENT/Agent.log");
		netsnmp_enable_filelog(logh,
							   netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
													  NETSNMP_DS_LIB_APPEND_LOGFILES));	
	}

	/* If We're an agentx subagent? */
	if (agentx_subagent) 
	{
		DEBUGMSGTL(("snmpV3Agent", "Using as Agentx SubAgent\n"));
		netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, 
				NETSNMP_DS_AGENT_ROLE, 1);
		/*
		 * AGENTX_ADDRESS is initialized to default port for AgentX.
		 */
		netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID,
				NETSNMP_DS_AGENT_X_SOCKET, AGENTX_ADDRESS);

		DEBUGMSGTL(("snmpV3Agent","Agent starting as AgentX Subagent.\n"));

	}

	/* Initialize the agent library and set the application configuration file
	 * in the appropriate default storage space , NETSNMP_DS_LIB_APPTYPE. Should
	 * always be called before calling init_snmp
	 * snmpV3Agent name is used to dictate what  .conf  file to read when
     * init_snmp() is called later.
	 */
	init_agent("snmpV3Agent");

	/* Initialize the Mib modules */
	DEBUGMSG(("STARTING INITIALISATION OF THE MIBS","\n"));
	DEBUGMSGTL(("snmpV3Agent", "Starting Initialization Of The Mibs\n"));


	init_currentAlarmTable();
	DEBUGMSGTL(("snmpV3Agent","CurrentAlarmTable Initialized Successfully\n"));


	/* Calls the functions to read the config file in order to configure the
	 * application and mib module parsing in the correct order
	 */
	init_snmp("snmpV3Agent");


	/*
     * If we're going to be a snmp master agent, initialize the ports 
	 * Initializing  the  master  agent causes it to listen for SNMP
     * requests on its default UDP port of 161.
     */
    if (!agentx_subagent)
	{
        init_master_agent();
	}

	/* In case we recevie a request to stop (kill -TERM or kill -INT) */
	keep_running = 1;

	signal(SIGTERM, stop_server);
	signal(SIGINT, stop_server);



	/* get the listener */
	if ((listener_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
	{
		perror("socket");
		exit(1);
	}


	/* bind */
	myaddr.sin_family = AF_INET;
	myaddr.sin_addr.s_addr = INADDR_ANY;
	myaddr.sin_port = htons(PORT);
	//memset(&(myaddr.sin_zero), ’\0’, 8);
	
	if (bind(listener_sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr)) == -1) 
	{
		perror("bind");
		exit(1);
	}

	/* listen */
	if (listen(listener_sockfd, 10) == -1) 
	{
		perror("listen");
		exit(1);
	}


	/* Create a thread which will sleep for configurabel amount of time and after that fetch the data from DB*/
	//pthread_create(&statusThreadId, NULL,periodicStatus, NULL);


	/* register our listening socket */
	//register_readfd(listener_sockfd, &who_broke_the_rattle, NULL);

	/* Main loop execution */
	while(keep_running) 
	{
		int numfds = 0;
		fd_set fdset;
		int block = 0;  //No blocking
		int count = 0;

		struct timeval timeout = { LONG_MAX,0}, *tvp = &timeout;

		/* Call snmp_select_info to retrieve the fdset of the snmp . 
		 * This will return number of active session but will FD_SET(fd , fdset)
		 */
		FD_ZERO(&fdset);
		snmp_select_info(&numfds, &fdset, tvp, &block);	

		/* increment the numfd to check for */
		numfds = numfds + listener_sockfd;

		FD_SET(listener_sockfd, &fdset);


		/*Blocking SELECT call for these fds */
		count = select(numfds,&fdset,0,0,NULL);

		/* Check whether the FIFO for alarms/events is ready to read */
		if(FD_ISSET(listener_sockfd, &fdset))
		{
			printf(" Normal Request Recieved \n");
			printf("listener_sockfd = %d is ready to be accepted and read \n",listener_sockfd);

			//TODO:Read for the messages on the queue

			/* Accept the connection */
			addrlen = sizeof(remoteaddr);
			if ((newfd = accept(listener_sockfd, (struct sockaddr *)&remoteaddr,&addrlen)) == -1) 
			{
				perror("accept");
			} 
			else 
			{
				FD_SET(newfd, &fdset); // add to master set
				if (newfd > fdmax) 
				{ // keep track of the maximum
					fdmax = newfd;
				}
				
				printf("selectserver: new connection from %s on "
					"socket %d\n", inet_ntoa(remoteaddr.sin_addr), newfd);
			}

			/* Read the data from socket into buffer */
			read(newfd , buffer , 14); 

			printf("[SERVER]: The lenth of data read :%d\n",strlen(buffer));

			write(1,buffer,14);

			//FD_CLR(listener_sockfd, &fdset);
			printf("\n");
		}
		else
		{

			/* This checks for packets arriving on the SNMP port and  processes
			* them if some are found.  If block is non-zero, the function call
			* will block until a packet arrives or an alarm must be  run  
			*/
			printf(" SNMP Request Recieved \n");
			agent_check_and_process(0);
		}
	}

    /*
     * at shutdown time 
     */
    snmp_shutdown("snmpV3Agent");
    exit(0);
}



void who_broke_the_rattle( int fd, void *data )
{
	char buffer[14];
    char *he_did = (char *)data;

    printf("%s broke the rattle!\n", he_did );
}



/*************************************************************************
 * This methods fetches the record from DB at regular intervals
 *************************************************************************/
void periodicStatus()
{
	long timeForSleep = 0;
	while(1)
    {
		timeForSleep = getTimeForSleep();

		waitTime(timeForSleep);

		/* Fetch the records from DB */
		printf("About to fetch records from DB\n");
	}

}


/*************************************************************************
 * This calculates the time in seconds to sleep before fetching new data
 * starts.
 *************************************************************************/
long getTimeForSleep()
{
	 int hours = 0;
	 int min = 0;
	 int sec = 0;
	 int periodicTimerInMin = 0; 
	 long timeForSleep = 0;
	 int tempMultiplier = 0;
	 int periodicStatsIntrvl = 60;
	 
	 getHrAndMin(&hours, &min, &sec);
	 periodicTimerInMin = periodicStatsIntrvl/60;

	 timeForSleep = (periodicTimerInMin - (min%periodicTimerInMin) )*60 -sec;
	 timeForSleep = 9;

	 return timeForSleep;
}


/*************************************************************************
 * This lets the thread to sleep till the configured amount of time
 *
 *************************************************************************/
void waitTime(long timeForSleepInSec)
{
	struct timeval t1;
	t1.tv_sec = timeForSleepInSec;
	t1.tv_usec = 0;

	/* Here select command is used to sleep for some configured amount of time */
	//select(theStatsSocketFd + 1,&fds , NULL, NULL, &t1);
	select(0, NULL, NULL, NULL, &t1);

	return;


}


/*************************************************************************
 * hr - variable to return hour
 * min - variable reference to return min
 * sec - variable reference to return seconds
 *************************************************************************/
void getHrAndMin(int *hr, int *min, int *sec)
{
	struct timeval t;
	struct tm* temp;
	gettimeofday(&t, NULL);
	const time_t epoch = t.tv_sec;
	temp = gmtime(&epoch);
	*hr  = temp -> tm_hour;
	*min = temp -> tm_min;
	*sec = temp -> tm_sec;
}
