#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <sys/sysmacros.h> /* major() minor() */
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <assert.h>
#include <pthread.h>

#include <apcie1532.h>
#include <apcie1532-events.h>

#include "findboards.h"
#include "input.h"

unsigned char stop;
unsigned char configured;

//------------------------------------------------------------------------------

/**@struct thread_arg_t
 *
 * This structure is used to transmit some parameters to threads.
 */
struct thread_arg_t
{
	int threadnbr;	/**< This is the minor ID of the board */
	int fd;			/**< File descriptor of the board */
};

struct timer_params
{
	uint8_t timerIndex;
	uint8_t timeBase;
	uint32_t reloadValue;
	uint8_t interruptFlag;
};

//------------------------------------------------------------------------------

/** this function look up for cards in the /proc/sys/apcie1532/ directory
* @param cards A pointer to the card area to fill
* @return The number of cards detected (may be zero if nor cards found).
*/
int apcie1532_find_cards(int ** cards)
{
	return apci_find_boards("apcie1532", cards);
}

//------------------------------------------------------------------------------

/** Exit callback function.
 *
 * This function is called on Ctrl+c key combination.
 * It set the stop flag status.
 */
static void v_ControlExit (int notused)
{
      stop = 0;
}

//------------------------------------------------------------------------------

void * fn_thread (void * arg)
{
	struct thread_arg_t *thread_args = (struct thread_arg_t *) arg;
	{
		configured = 0;
		{
			{
				if (ioctl (thread_args->fd,CMD_APCIE1532_EventEmpty,NULL) != 0)
					perror("ioctl CMD_APCIE1532_EventEmpty");
			}
			while(stop)
			{
				{
					apcie1532_event_t evt;
					if (ioctl (thread_args->fd,CMD_APCIE1532_EventWait, &evt) != 0)
						perror("ioctl CMD_APCIE1532_EventWait");
					printf("Interrupt happened. Source: %d param1: 0x%lX\n",evt.source, evt.param1);
				}
			}
		}
	}
	pthread_exit(NULL);
}


//------------------------------------------------------------------------------

int main(int argc, char** argv)
{
	int ret = 0;
	int i = 0;
	int *fd;
	int nbr_boards = 0;
	pthread_t *thread = NULL;
	struct thread_arg_t *thread_args = NULL;
	struct timer_params *timer_board_params = NULL;
	int user_input;

	stop = 1;
	configured = 1;

	/* Search for APCIE-1532 boards */
	if ((nbr_boards = apcie1532_find_cards(&fd)) == 0)
	{
		printf("No APCIE-1532 found\n");
		exit(EXIT_FAILURE);
	}

	thread = malloc (sizeof (pthread_t) * nbr_boards);
	if (thread == NULL)
	{
		printf("Can't allocate memory for thread\n");
		exit(EXIT_FAILURE);
	}

	thread_args = malloc (sizeof (struct thread_arg_t) * nbr_boards);
	if (thread_args == NULL)
	{
		printf("Can't allocate memory for thread_args\n");
		exit(EXIT_FAILURE);
	}

	timer_board_params = malloc(sizeof (struct timer_params) * nbr_boards);
	if (timer_board_params == NULL)
	{
		printf("Can't allocate memory for timer_board_params\n");
		exit(EXIT_FAILURE);
	}

	/* Set the v_ControlExit callback function on Ctrl+c key combination.
	 * Once Ctrl+C are pressed, the v_ControlExit function is called to end the
	 * sample.
	 */
	signal (SIGINT, v_ControlExit);

    for (i=0; i<nbr_boards; i++)
    {
		thread_args[i].threadnbr = i;	/**< This is the minor ID of the board */
		thread_args[i].fd = fd[i];		/**< File descriptor of the board */

		printf ("Enter the timer index value, watchdog must be 2:\n");
		timer_board_params[i].timerIndex = get_integer(0,2);

		printf ("Enter the timeBase value:\n");
		timer_board_params[i].timeBase = get_integer(0,2);
		printf ("Enter the TimerWatchdog reload value (1-4095):\n");
		timer_board_params[i].reloadValue = get_integer(1,4095);
		printf ("Enter 1 if interrupts need to be generated on timer time out:\n");
		timer_board_params[i].interruptFlag = get_integer(0,1);

		if (ioctl (thread_args->fd,CMD_APCIE1532_TimerWatchdogInitAndStart,&timer_board_params[i]) != 0)
			perror("ioctl CMD_APCIE1532_TimerWatchdogInitAndStart");

		if (ioctl (thread_args[i].fd,CMD_APCIE1532_TimerWatchdogTrigger,timer_board_params[i].timerIndex) != 0)
			perror("ioctl CMD_APCIE1532_TimerWatchdogTrigger");

		/* Create a thread for each board */
		if ((ret = pthread_create (&thread[i], NULL, fn_thread, &thread_args[i])) != 0)
		{
		   printf("%d : %s", i, strerror(ret));
		   exit(EXIT_FAILURE);
		}
		printf ("Create and start thread for board %d\n", i);
    }

	/* Wait Ctrl+c key combination */
	printf ("Usage:\n"
			"Use 'q' or ctrl+c, followed by Enter to quit the program.\n"
			"Use 't' followed with Enter to trigger the timer/watchdog."
				"\n\tIn case the watchdog is used, the program sets all output to on,"
				"\n\tthen when the watchdog triggers, outputs are reset to off.\n"
			"Use 'r' followed with Enter to read the current timer/watchdog value\n\n");
	{
		while (stop)
		{
			if (!configured)
			{
				user_input = getchar();
				if(user_input == 'r') {
					for (i=0; i<nbr_boards; i++)
					{
						struct {
							uint8_t timerIndex;
							uint32_t value;
						} params;
						params.timerIndex = timer_board_params[i].timerIndex;
						params.value = 0;
						if (ioctl (thread_args[i].fd,CMD_APCIE1532_ReadTimerWatchdogValue,&params) != 0)
							perror("ioctl CMD_APCIE1532_ReadTimerWatchdogValue");
						printf ("TimerWatchdog board %d = %i \n", i, params.value);
						fflush(stdout);
					}
				} else if(user_input == 't') {
					for (i=0; i<nbr_boards; i++)
					{
						if(timer_board_params[i].timerIndex == 2)
						{
							printf ("Testing watchdog function by setting all outputs\n");
							if ( ioctl (thread_args[i].fd,CMD_APCIE1532_Set16DigitalOutputsOn,0xFFFF) < 0 )
							{
								perror("ioctl CMD_APCIE1532_Set16DigitalOutputsOn");
							}
						}
						if (ioctl (thread_args[i].fd,CMD_APCIE1532_TimerWatchdogTrigger,timer_board_params[i].timerIndex) != 0)
							perror("ioctl CMD_APCIE1532_TimerWatchdogTrigger");
					}
				} else if(user_input == 'q') {
					break;
				}
			}
		}
	}

	/* Stop threads */
    for (i=0; i<nbr_boards; i++)
    {
		pthread_cancel (thread[i]);

		printf("Ending program\n");
		if (ioctl (thread_args[i].fd, CMD_APCIE1532_TimerWatchdogStop, timer_board_params[i].timerIndex) != 0)
			perror("ioctl CMD_APCIE1532_TimerWatchdogStop");
    }

    free (thread);
    free (thread_args);

	return 0;
}
