/**
 * Name: stream_burst.c
 * Desc: Shows how to stream from a device and stop after a given number of
 *       scans.
**/

#include <stdio.h>
#include <string.h>

#include "LabJackM.h"

#include "LJM_StreamUtilities.h"

void StreamBurst(int handle, int numChannels, const char ** channelNames,
	double scanRate, int scansPerRead, int numScans);


/**
 * Global values to quickly configure some of this program's behavior
**/

// Output style
typedef enum {
	SCAN_OUTPUT_NONE,
	SCAN_OUTPUT_FINAL_READ,
	SCAN_OUTPUT_ALL_READS
} ScanReadPrintStyle;
const ScanReadPrintStyle READ_OUTPUT = SCAN_OUTPUT_ALL_READS;

enum { NUM_CHANNELS = 2 };
const char * POS_NAMES[] = {"AIN0",  "FIO_STATE"};

int main(int argc, char * argv[])
{
	int handle;
	int scansPerRead = 0;
	int numScans = 0;

	// How fast to stream in Hz.
	double scanRate = 2000;

	if (argc != 2) {
		printf("Usage: %s STREAM_NUM_SCANS\n", argv[0]);
		WaitForUserIfWindows();
		exit(1);
	}

	numScans = strtol(argv[1], NULL, 10);
	if (numScans == 0) {
		printf("(numScans == 0). A STREAM_NUM_SCANS of 0 will stream indefinitely.\n");
		WaitForUser();
	}

	scansPerRead = numScans / 2;

	// Add one scan per read for odd STREAM_NUM_SCANS
	if (numScans % 2 != 0) {
		scansPerRead += 1;
	}

	if (scansPerRead <= 0) {
		scansPerRead = 1;
	}

	// GetAndPrintConfigValue is defined in LJM_Utilities.h
	GetAndPrintConfigValue(LJM_LIBRARY_VERSION);
	GetAndPrintConfigValue(LJM_STREAM_TRANSFERS_PER_SECOND);

	// This line should be commented out for high stream speeds, but debug
	// logging may help debug errors.
	// SetupStreamDebugLogging();

	DisplayDebugLoggingConfigurations();

	handle = OpenOrDie(LJM_dtT7, LJM_ctUSB, "LJM_idANY");
	printf("\n");
	PrintDeviceInfoFromHandle(handle);
	GetAndPrint(handle, "FIRMWARE_VERSION");
	printf("\n");

	DisableStreamIfEnabled(handle);

	StreamBurst(handle, NUM_CHANNELS, POS_NAMES, scanRate, scansPerRead,
		numScans);

	CloseOrDie(handle);

	WaitForUserIfWindows();

	return LJME_NOERROR;
}

void StreamBurst(int handle, int numChannels, const char ** channelNames,
	double scanRate, int scansPerRead, int numScans)
{
	int err, i;
	int totalSkippedScans = 0;
	int deviceScanBacklog = 0;
	int LJMScanBacklog = 0;

	int * aScanList = malloc(sizeof(int) * numChannels);

	unsigned int aDataSize = numChannels * scansPerRead;
	double * aData = malloc(sizeof(double) * aDataSize);
	unsigned int timeStart, timeEnd;

	const int LJM_WARNING_SCANS_NUM = scansPerRead * numChannels * 2;
	int deviceWarningScansNum = 10;
	double deviceBufferBytes = 0;

	static const int STREAM_BURST_COMPLETE = 2944;

	err = LJM_NamesToAddresses(numChannels, POS_NAMES, aScanList, NULL);
	ErrorCheck(err, "Getting positive channel addresses");

	printf("channels:\n");
	for (i=0; i<numChannels; i++) {
		printf("    %s (%d)\n", POS_NAMES[i], aScanList[i]);
	}
	printf("\n");

	printf("Writing %d to STREAM_NUM_SCANS to limit the number of scans performed\n",
		numScans);
	WriteNameOrDie(handle, "STREAM_NUM_SCANS",  numScans);

	printf("Starting stream:\n");
	printf("    scan rate: %.02f Hz (%.02f sample rate)\n",
		scanRate, scanRate * numChannels);
	printf("    scansPerRead: %d\n", scansPerRead);
	printf("    ");GetAndPrint(handle, "STREAM_RESOLUTION_INDEX");

	err = LJM_eStreamStart(handle, scansPerRead, numChannels, aScanList,
		&scanRate);
	ErrorCheck(err, "LJM_eStreamStart");

	printf("Stream started. Actual scanRate: %f\n", scanRate);
	printf("\n");

	err = LJM_eReadName(handle, "STREAM_BUFFER_SIZE_BYTES", &deviceBufferBytes);
	ErrorCheck(err, "Reading STREAM_BUFFER_SIZE_BYTES");
	deviceWarningScansNum = deviceBufferBytes / numChannels / 2 / 8; // 1/8th of the total scans
	printf("STREAM_BUFFER_SIZE_BYTES: %f, deviceWarningScansNum: %d\n", deviceBufferBytes,
		deviceWarningScansNum);
	printf("LJM_WARNING_SCANS_NUM: %d\n", LJM_WARNING_SCANS_NUM);

	// Read the scans
	printf("Now performing reads until the device is done\n");
	timeStart = GetCurrentTimeMS();
	err = LJME_NOERROR;
	i = 0;
	while (err == LJME_NOERROR) {
		if (META_OUTPUT == META_OUTPUT_ALL) {
			printf("\n");
		}

		// Zero out the data array
		memset(aData, 0, sizeof(double) * aDataSize);

		err = LJM_eStreamRead(handle, aData, &deviceScanBacklog, &LJMScanBacklog);
		if (err == STREAM_BURST_COMPLETE) {
			printf("STREAM_BURST_COMPLETE\n");
			++i;
			break;
		}
		PrintErrorIfError(err, "LJM_eStreamRead");

		if (META_OUTPUT == META_OUTPUT_ALL) {
			OutputStreamIterationInfo(i, deviceScanBacklog, deviceWarningScansNum,
				LJMScanBacklog, LJM_WARNING_SCANS_NUM);
		}

		if (READ_OUTPUT == SCAN_OUTPUT_ALL_READS) {
			PrintScans(scansPerRead, numChannels, POS_NAMES, aScanList,
				deviceScanBacklog, LJMScanBacklog, i, aData);
		}

		if (META_OUTPUT == META_OUTPUT_ALL) {
			totalSkippedScans += CountAndOutputNumSkippedScans(numChannels, scansPerRead, aData);
		}

		++i;
	}
	timeEnd = GetCurrentTimeMS();

	if (META_OUTPUT == META_OUTPUT_ALL) {
		PrintStreamConclusion(timeStart, timeEnd, i, scansPerRead, numChannels,
			totalSkippedScans);
	}

	err = LJM_eStreamStop(handle);
	ErrorCheck(err, "Stopping stream");

	if (READ_OUTPUT == SCAN_OUTPUT_FINAL_READ || READ_OUTPUT == SCAN_OUTPUT_ALL_READS) {
		printf("Final values:\n");
		PrintScans(scansPerRead, numChannels, POS_NAMES, aScanList, deviceScanBacklog,
			LJMScanBacklog, i, aData);
	}

	free(aData);
	free(aScanList);
}
