/*
    UL call demonstrated:        	  ulEnableEvent() and ulAInScanWait()

    Purpose:                          Use events to determine when data is available

    Demonstration:                    Use the a callback function to display the
    								  the data for a user-specified range of
    								  A/D channels

    Steps:
    1. Call ulGetDaqDeviceInventory() to get the list of available DAQ devices
    2. Call ulCreateDaqDevice() to to get a handle for the first DAQ device
    3. Verify the DAQ device has an analog input subsystem
    3. Verify the analog input subsystem has a hardware pacer
    4. Call ulConnectDaqDevice() to establish a UL connection to the DAQ device
    5. Initialize the ScanEventParameters structure used to pass parameters to the callback function
    6. Call ulEnableEvent() to enable the DE_ON_DATA_AVAILABLE event
    7. Call ulAInScan() to start the finite scan of A/D input channels
    8. Call ulAInScanWait() to wait for the finite scan to complete
    9. The callback is called each time 100 samples are available to allow the data to be displayed
    10. Call ulAinScanStop() to stop the background operation
    11. Call ulDisconnectDaqDevice and ulReleaseDaqDevice() before exiting the process
*/
#include <stdio.h>
#include <stdlib.h>
#include "uldaq.h"
#include "utility.h"

#define MAX_DEV_COUNT  100
#define MAX_STR_LENGTH 64
#define MAX_SCAN_OPTIONS_LENGTH 256

double rate = 1000;

void eventCallbackFunction(DaqDeviceHandle daqDeviceHandle, DaqEventType eventType, unsigned long long eventData, void* userData);

struct ScanEventParameters
{
	double* buffer;	// data buffer
	int lowChan;	// first channel in acquisition
	int highChan;	// last channel in acquisition
};
typedef struct ScanEventParameters ScanEventParameters;

int main(void)
{
	int descriptorIndex = 0;
	DaqDeviceDescriptor devDescriptors[MAX_DEV_COUNT];
	DaqDeviceInterface interfaceType = ANY_IFC;
	DaqDeviceHandle daqDeviceHandle = 0;
	unsigned int numDevs = MAX_DEV_COUNT;
	ScanEventParameters scanEventParameters;

	// set some variables that are used to acquire data
	int lowChan = 0;
	int highChan = 3;
	AiInputMode inputMode;
	Range range;
	int samplesPerChannel = 10000;
	AInScanFlag flags = AINSCAN_FF_DEFAULT;
	DaqEventType eventTypes = (DaqEventType) (DE_ON_DATA_AVAILABLE | DE_ON_INPUT_SCAN_ERROR | DE_ON_END_OF_INPUT_SCAN);

	// set the scan options for a FINITE scan ... to set the scan options for
	// a continuous scan, uncomment the line that or's the SO_CONTINUOUS option
	// into to the scanOptions variable
	//
	// if this is changed to a CONTINUOUS scan, then changes will need to be made
	// to the event handler (eventCallbackFunction) to account for the buffer wrap
	// around condition
	ScanOption scanOptions = SO_DEFAULTIO;
	//scanOptions |= SO_CONTINUOUS;

	int hasAI = 0;
	int hasPacer = 0;
	int numberOfChannels = 0;
	int availableSampeCount = 0;

	char inputModeStr[MAX_STR_LENGTH];
	char rangeStr[MAX_STR_LENGTH];
	char scanOptionsStr[MAX_SCAN_OPTIONS_LENGTH];

	// allocate a buffer to receive the data
	int chanCount = 0;
	double* buffer = NULL;
	UlError err = ERR_NO_ERROR;

	int i = 0;
	int __attribute__((unused)) ret;
	char c;

	// Get descriptors for all of the available DAQ devices
	err = ulGetDaqDeviceInventory(interfaceType, devDescriptors, &numDevs);

	if (err != ERR_NO_ERROR)
		goto end;

	// verify at least one DAQ device is detected
	if (numDevs == 0)
	{
		printf("No DAQ device is detected\n");
		goto end;
	}

	printf("Found %d DAQ device(s)\n", numDevs);
	for (i = 0; i < (int) numDevs; i++)
		printf("  %s: (%s)\n", devDescriptors[i].productName, devDescriptors[i].uniqueId);

	// get a handle to the DAQ device associated with the first descriptor
	daqDeviceHandle = ulCreateDaqDevice(devDescriptors[descriptorIndex]);

	if (daqDeviceHandle == 0)
	{
		printf ("\nUnable to create a handle to the specified DAQ device\n");
		goto end;
	}

	// verify the specified DAQ device supports analog input
	err = getDevInfoHasAi(daqDeviceHandle, &hasAI);
	if (!hasAI)
	{
		printf("\nThe specified DAQ device does not support analog input\n");
		goto end;
	}

	// verify the device supports hardware pacing for analog input
	err = getAiInfoHasPacer(daqDeviceHandle, &hasPacer);

	if (!hasPacer)
	{
		printf("The specified DAQ device does not support hardware paced analog input\n");
		goto end;
	}

	printf("\nConnecting to device %s - please wait ...\n", devDescriptors[descriptorIndex].devString);

	// establish a connection to the DAQ device
	err = ulConnectDaqDevice(daqDeviceHandle);

	if (err != ERR_NO_ERROR)
		goto end;

	// get the first supported analog input mode
	err = getAiInfoFirstSupportedInputMode(daqDeviceHandle, &numberOfChannels, &inputMode, inputModeStr);

	if (highChan >= numberOfChannels)
		highChan = numberOfChannels - 1;

	chanCount = highChan - lowChan + 1;

	// allocate a buffer to receive the data
	buffer = (double*) malloc(chanCount * samplesPerChannel * sizeof(double));

	if(buffer == 0)
	{
		printf("\nOut of memory, unable to create scan buffer\n");
		goto end;
	}

	// store the scan event parameters for use in the callback function
	scanEventParameters.buffer =  buffer;
	scanEventParameters.lowChan = lowChan;
	scanEventParameters.highChan = highChan;

	// enable the event to be notified every time 100 samples are available
	availableSampeCount = 100;
	err = ulEnableEvent(daqDeviceHandle, eventTypes, availableSampeCount, eventCallbackFunction, &scanEventParameters);

	// get the first supported analog input range
	err = getAiInfoFirstSupportedRange(daqDeviceHandle, inputMode, &range, rangeStr);

	ConvertScanOptionsToString(scanOptions, scanOptionsStr);

	printf("\n%s ready\n", devDescriptors[descriptorIndex].devString);
	printf("    Function demonstrated: ulEnableEvent()\n");
	printf("    Channels: %d - %d\n", lowChan, highChan);
	printf("    Input mode: %s\n", inputModeStr);
	printf("    Range: %s\n", rangeStr);
	printf("    Samples per channel: %d\n", samplesPerChannel);
	printf("    Rate: %f\n", rate);
	printf("    Scan options: %s\n", scanOptionsStr);
	printf("\nHit ENTER to continue\n");

	ret = scanf("%c", &c);

	// clear the display
	ret = system("clear");

	// start the finite acquisition
	err = ulAInScan(daqDeviceHandle, lowChan, highChan, inputMode, range, samplesPerChannel, &rate, scanOptions, flags, buffer);

	if(err == ERR_NO_ERROR)
	{
		// wait here until scan is done ... events will be handled in the event handler
		// (eventCallbackFunction) until the background scan completes
		int timeToWait = -1;
		err = ulAInScanWait(daqDeviceHandle, WAIT_UNTIL_DONE, 0, timeToWait);

		if (err != ERR_NO_ERROR)
		{
			printf ("ulAInScanWait error = %d\n", err);
		}

		err = ulAInScanStop(daqDeviceHandle);
	}

	// disable events
	ulDisableEvent(daqDeviceHandle, eventTypes);

	// disconnect from the DAQ device
	ulDisconnectDaqDevice(daqDeviceHandle);

end:

	// release the handle to the DAQ device
	if(daqDeviceHandle)
		ulReleaseDaqDevice(daqDeviceHandle);

	// release the scan buffer
	if(buffer)
		free(buffer);

	if(err != ERR_NO_ERROR)
	{
		char errMsg[ERR_MSG_LEN];
		ulGetErrMsg(err, errMsg);
		printf("Error Code: %d \n", err);
		printf("Error Message: %s \n", errMsg);
	}

	return 0;
}

void eventCallbackFunction(DaqDeviceHandle daqDeviceHandle, DaqEventType eventType, unsigned long long eventData, void* userData)
{
	char eventTypeStr[MAX_STR_LENGTH];
	UlError err = ERR_NO_ERROR;

	// reset the cursor to the top of the display and
	// show the termination message
	resetCursor();

	ConvertEventTypesToString(eventType, eventTypeStr);
	printf("eventType: %s \n", eventTypeStr);

	if (eventType == DE_ON_DATA_AVAILABLE)
	{
		ScanEventParameters* scanEventParameters = (ScanEventParameters*) userData;
		long long totalSamples = eventData;
		int i;
		long long index = 0;

		int chanCount = scanEventParameters->highChan  - scanEventParameters->lowChan + 1;;

		printf("eventData: %lld \n\n", eventData);

		printf("actual scan rate = %f\n\n", rate);

		// if this example is changed to a CONTINUOUS scan, then you will need
		// to maintain the index of where the data is being written to the buffer
		// to handle the buffer wrap around condition
		index = (totalSamples * chanCount) - chanCount;
		printf("currentIndex = %lld \n\n", index);

		for (i = 0; i < chanCount; i++)
		{
			printf("chan %d = %10.6f\n",
					i + scanEventParameters->lowChan,
					scanEventParameters->buffer[index + i]);
		}
	}

	if(eventType == DE_ON_INPUT_SCAN_ERROR)
	{
		err = (UlError) eventData;
		char errMsg[ERR_MSG_LEN];
		ulGetErrMsg(err, errMsg);
		printf("Error Code: %d \n", err);
		printf("Error Message: %s \n", errMsg);
	}


	if (eventType == DE_ON_END_OF_INPUT_SCAN)
	{
		printf("\nThe scan using device handle %lld is complete \n", daqDeviceHandle);
	}
}



