//-----------------------------------------------------------------------------
// NOTE: Sample only for linux side cause of the used select!
//-----------------------------------------------------------------------------
#ifndef linux
#	error Sample only for linux side!!
#endif	// linux
#include <stdio.h>
#include <unistd.h>
#include <iostream>
#include <apps/Common/exampleHelper.h>
#include <mvIMPACT_CPP/mvIMPACT_acquire.h>

#ifdef MALLOC_TRACE
#	include <mcheck.h>
#endif	// MALLOC_TRACE

#define PRESS_A_KEY				\
	cout << "Press a key..." << endl;	\
	getchar();

using namespace std;
using namespace mvIMPACT::acquire;

//-----------------------------------------------------------------------------
// returns 0 if timeout, else 1
unsigned waitForInput( int maxwait_sec, int fd )
//-----------------------------------------------------------------------------
{
	fd_set rfds;
	struct timeval tv;

	FD_ZERO(&rfds);
	FD_SET(fd, &rfds);

	tv.tv_sec = maxwait_sec ;
	tv.tv_usec = 0;

	return select( fd+1, &rfds, NULL, NULL, &tv );
}

//-----------------------------------------------------------------------------
unsigned int liveLoop( Device* pDev, const string& settingName = "" )
//-----------------------------------------------------------------------------
{
	unsigned int cnt = 0;

	cout << " == " << __FUNCTION__ << " - Initialising the device. This might take some time...." << endl;
	try
	{
		pDev->open();
	}
	catch( ImpactAcquireException& e )
	{
		// this e.g. might happen if the same device is already opened in another process...
		cout << "*** " << __FUNCTION__ << " - An error occurred while opening the device " << pDev->serial.read()
			 << "(error code: " << e.getErrorCode() << ", " << e.getErrorCodeAsString() << "). Press any key to end the application..." << endl;
		PRESS_A_KEY
		return 0;
	}

	cout << " == " << __FUNCTION__ << " - establish access to the statistic properties...." << endl;
	// establish access to the statistic properties
	Statistics statistics( pDev );
	cout << " == " << __FUNCTION__ << " - create an interface to the device found...." << endl;
	// create an interface to the device found
	FunctionInterface fi( pDev );

	if( !settingName.empty() )
	{
		cout << "Trying to load setting " << settingName << "..." << endl;
		int result = fi.loadSetting( settingName );
		if( result != DMR_NO_ERROR )
		{
			cout << "loadSetting( \"" << settingName << "\" ); call failed: " << ImpactAcquireException::getErrorCodeAsString( result ) << endl;
		}
	}

	int MAX_REQUESTS = 4;
	// make sure enough requests are available:
	SystemSettings ss(pDev);
	ss.requestCount.write( MAX_REQUESTS );
	// now we can work with at least MAX_REQUESTS requests

#if 0
	// if running mvBlueFOX on an embedded system (e.g. ARM) with USB 1.1 it may be necessary to change
	// a few settings and timeouts like this:

	// get other settings
	SettingsBlueFOX setting( pDev );

	// set request timeout higher because USB 1.1 on ARM is soooo slow
	setting.cameraSetting.imageRequestTimeout_ms.write( 5000 );
	// use on Demand mode
	setting.cameraSetting.triggerMode.write( ctmOnDemand );
	// use no image processing - just raw data
	setting.imageProcessing.colorProcessing.write( cpmRaw );
#endif

#if 0
	// this section contains special settings that might be interessting for mvBlueCOUGAR or mvBlueLYNX M7
	// related embedded devices
	CameraSettingsBlueCOUGAR cs(pDev);
	int maxWidth = cs.aoiWidth.read( plMaxValue );
	cs.aoiWidth.write( maxWidth );
// 	cs.autoGainControl.write( agcOff );
// 	cs.autoExposeControl.write( aecOff );
// 	cs.exposeMode.write( cemOverlapped );
// 	cs.pixelClock_KHz.write( cpc40000KHz );
// 	cs.expose_us.write( 5000 );

	// If this is color sensor, we will NOT convert the Bayer data into a RGB image as this
	// will cost a lot of time on an embedded system
	ImageProcessing ip(pDev);
	ip.colorProcessing.write( cpmRaw );

#endif

	// prefill the capture queue. There can be more then 1 queue, but for this sample
	// we will work with the default capture queue
	int requestResult = DMR_NO_ERROR;
	int requestCount = 0;
	while( ( requestResult = fi.imageRequestSingle() ) == DMR_NO_ERROR )
	{
		++requestCount;
	}

	cout << "Last result: "<< requestResult << ", " << requestCount << " buffers requested";
	if( ss.requestCount.isConstDefined( plMaxValue ) )
	{
		cout << ", max request count: " << ss.requestCount.read( plMaxValue );
	}
	cout << endl;
	cout << "Press <<ENTER>> to end the application!!" << endl;

	// run thread loop
	const Request* pRequest = 0;
	const unsigned int timeout_ms = 8000;	// USB 1.1 on an embedded system needs a large timeout for the first image
	int requestNr = -1;
	bool boLoopRunning = true;
	while( boLoopRunning )
	{
		// wait for results from the default capture queue
		requestNr = fi.imageRequestWaitFor( timeout_ms );

		if( fi.isRequestNrValid( requestNr ) )
		{
			pRequest = fi.getRequest( requestNr );
			if( fi.isRequestOK( pRequest ) )
			{
				++cnt;
				// here we can display some statistical information every 10th image
				if( cnt%10 == 0 )
				{
					cout << cnt << ": Info from " << pDev->serial.read()
						<< ": " << statistics.framesPerSecond << ", "
						<< statistics.errorCount << ", " << statistics.captureTime_s <<" Image count: " << cnt 
						<< " (dimensions: " << pRequest->imageWidth.read() << "x" << pRequest->imageHeight.read()
						<< ")" << endl;
				}
			}
			else
			{
				cout << "*** Error: " << pRequest->requestResult << endl;
			}

			// this image has been displayed thus the buffer is no longer needed...
			fi.imageRequestUnlock( requestNr );
			// send a new image request into the capture queue
			fi.imageRequestSingle( );
		}
		else
		{
			cout << "*** Error: There has been no request in the queue!" << endl;
		}

		boLoopRunning = waitForInput( 0, STDOUT_FILENO ) == 0 ? true : false; // break by STDIN
	}

	cout << " == " << __FUNCTION__ << " - free resources...." << endl;

	// free resources
	fi.imageRequestReset( 0, 0 );
	return 0;
}

//-----------------------------------------------------------------------------
int main( int argc, char* argv[] )
//-----------------------------------------------------------------------------
{
#ifdef MALLOC_TRACE
	mtrace();
#endif	// MALLOC_TRACE

	cout << " ++ start application...." << endl;

	DeviceManager devMgr;
	Device* pDev = getDeviceFromUserInput( devMgr );
	if( pDev == 0 )
	{
		cout << "Unable to continue!";
		cout << "Press [ENTER] to end the application" << endl;
		cin.get();
		return 0;
	}

	string settingName;
	if( argc > 1 )
	{
		// try to load this setting later on...
		settingName = string(argv[1]);
	}

	// create an interface to the first MATRIX VISION device with the serial number sDevSerial
	if( pDev )
	{
		cout << "Initialising the device: "<< pDev->serial.read() << ". This might take some time..." << endl;
		// start the execution of the 'live' loop.
		liveLoop( pDev, settingName );
		cout << " == Will exit...." << endl;
		pDev->close();
		// do NOT delete pDev here! It will be destroyed automatically when the device manager (devMgr) is destroyed
	}
	cout << " -- end application...." << endl;

	return 0;
}

