#include <stdlib.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


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

#define NBR_CHANNELS	4

volatile int stop = 0;

void signal_handler(int signum, siginfo_t *info, void *context)
{
	if (signum == SIGINT)
		stop = 1;
}

int main(int argc, char *argv[])
{
	char buf[256] = {0};
	int i = 0;
	int ret = 0;
	int boardFds[5] = {0};
	struct sigaction act;
	uint32_t *data = NULL;
	uint8_t *data8 = NULL;
	uint8_t clockDivisor = 1;

	str_InitAnalogInputModuleCyclicAcquisitionArg_t acquisitionInitArg;
	str_InitBoardArg_t config;
	InitDigitalInputExternalTrigger_t InputExternalTrigger;

	InputExternalTrigger.b_TriggerLevel = 1;
	InputExternalTrigger.w_TriggerCountValue = 1;

	config.b_ClockGen1_Divisor = 16;
	config.b_ClockGen2_Divisor = 4;

	config.b_MasterSlaveMode = 0;
	config.b_SamplingClock1_Selection = 0;
	config.b_SamplingClock2_Selection = 0;
	config.b_SlaveSamplingClock1_Selection = 0;
	config.b_SlaveSamplingClock2_Selection = 0;
	config.b_Chronometer_MasterClock_Divisor = 2;

	/* Initialize the acquisition */
	acquisitionInitArg.dw_OnBoardBufferSize = 15; /* 32 MB */
	acquisitionInitArg.dw_CompareValue = 32000;
	acquisitionInitArg.dw_Mode = 1;              /* Ring buffer mode for continuous acquisition */

	/* Allocate memory to get the data + the data header */
	data = (uint32_t *) malloc (((acquisitionInitArg.dw_CompareValue + 1) * sizeof (uint32_t)) * 2);
	if (data == NULL)
	{
		perror ("malloc: ");
		return 0;
	}

	/* Will be used to get data */
	data8 = (uint8_t *)data;

	/* Setup a signal handler to catch SIGINT */
	memset(&act, 0, sizeof(act));
	act.sa_sigaction = &signal_handler;
	if (sigaction(SIGINT, &act, NULL) < 0)
		perror("sigaction");

	/* Open the main node */
	boardFds[0] = open("/dev/apci3600/0", O_RDWR);
	if (boardFds[0] < 0)
	{
		printf("open board %d failed", 0);
		goto end0;
	}

	/* Open channel nodes */
	for (i = 0; i < NBR_CHANNELS; i++)
	{
		/* Set the device node name to open */
		sprintf (buf, "/dev/apci3600/%i", (APCI3600_DEV_MINOR_CHRONO_0 + i));
		boardFds[(1+i)] = open(buf, O_RDWR);
		if (boardFds[(1+i)] < 0)
		{
			printf("open chrono %i failed\n", i);
			goto end0;
		}
	}

	/* Initialize the board */
	ret = ioctl(boardFds[0], CMD_APCI3600_InitBoard, &config);
	if (ret != 0)
	{
		printf("CMD_APCI3600_InitBoard failed with error %d!\n", ret);
		goto end0;
	}

	ret = ioctl(boardFds[1], CMD_APCI3600_InitChronometerModule, clockDivisor);
	if (ret != 0)
	{
		printf("CMD_APCI3600_InitChronometerModule failed with error %d!\n", ret);
		goto end0;
	}

	ioctl(boardFds[1], CMD_APCI3600_StopChronometerModuleAcquisition, &config);
	ret = ioctl(boardFds[1], CMD_APCI3600_StartChronometerModule, &config);
	if (ret != 0)
	{
		printf("CMD_APCI3600_StartChronometerModule failed with error %d!\n", ret);
		goto end0;
	}

	do
	{
		uint8_t b_Status = 0;
		do {
			ret = ioctl(boardFds[1], CMD_APCI3600_GetChronometerModuleFIFOStatus, &b_Status);
			if (ret != 0) {
				printf("CMD_APCI3600_GetChronometerModuleFIFOStatus failed with error %d!\n", ret);
				goto end0;
			}
			printf("\rStatus = %x", b_Status);
		} while (b_Status == 0 && stop != 1);

		if ((b_Status & 0x1) != 0)
		{
			uint32_t value = 0;
			ret = ioctl(boardFds[1], CMD_APCI3600_ReadChronometerModuleValue, &value);
			if (ret != 0)
			{ printf("i_PCI3600_ReadChronometerModuleValue Error:%d\n", ret); goto end0; }

			printf("Value = %u\n",value);
		}
		else
		{
			if ((b_Status & 0x2) != 0) {
				printf("FIFO Error Overflow\n");
				goto end0;
			}
		}
	} while (stop != 1);
end0:
	ret = ioctl(boardFds[1], CMD_APCI3600_StopChronometerModuleAcquisition, &config);
	if (ret != 0)
	{
		printf("CMD_APCI3600_StopChronometerModule failed with error %d!\n", ret);
	}
	if (data)
		free (data);

	return 0;
}
