//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifdef linux
	#include <stdio.h>
	#include <unistd.h>
#endif
#include <iostream>
#include <mvIMPACT_CPP/mvIMPACT_acquire.h>

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

#ifdef linux
#define PRESS_A_KEY				\
		cout << "Press a key..." << endl;	\
		getchar();
#else
#define PRESS_A_KEY				\
		cout << "Press a key..." << endl;	\
		char ch = getch();
#endif

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
using namespace std;
using namespace mvIMPACT::acquire;

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static bool g_bTerminated = true;
static bool g_bLoadSettings = false;
const unsigned int MAX_REQUESTS = 3;

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
unsigned WaitForInput( int maxwait_sec, int fd )
/* returns 0 if timeout, else 1 */
//-----------------------------------------------------------------------------
{
    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);
}

//-----------------------------------------------------------------------------
ostream& operator<<( std::ostream& out, const Property& prop )
//-----------------------------------------------------------------------------
{
	out << prop.name() << ": " << prop.readS();
	return out;
}

//-----------------------------------------------------------------------------
unsigned int liveLoop( Device* pDev )
//-----------------------------------------------------------------------------
{
	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.errCode() << "). 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( g_bLoadSettings )
	{
		try
		{
			cout << " == " << __FUNCTION__ << " - loadSetting...." << endl;
			fi.loadSetting( pDev->serial.read() + ".xml" );
		}
		catch( ImpactAcquireException& e )
		{
			cout << "***" << __FUNCTION__ << " " << e.errString().c_str() <<"("<< e.errCode() <<")"<< endl;
		}
	}

	// make sure enough requests are avaiable:
	fi.ensureRequests( MAX_REQUESTS );
	// now we can work with at least MAX_REQUESTS requests

	// prefill the capture queue. There can be more then 1 queue, but for this sample
	// we will work with the default capture queue
	for( unsigned int i = 0; i < MAX_REQUESTS; i++ )
	{
		try
		{
			fi.imageRequestSingle( );
		}
		catch( ImpactAcquireException& e )
		{
			cout << "***" << __FUNCTION__ << " " << e.errString().c_str() <<"("<< e.errCode() <<")"<< endl;
		}
	}

	cout << "Press <<ENTER>> to end the application!!" << endl;

	// run thread loop
	const Request* pRequest = 0;
	const unsigned int timeout_ms = 2000;
	int requestNr = -1;
	while( g_bTerminated )
	{
		// 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 << endl;
				}
			}
			else
			{
				cout << "*** Error: " << pRequest->getResult().result << 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
			try
			{
				fi.imageRequestSingle( );
			}
			catch( ImpactAcquireException& e )
			{
				cout << "***" << __FUNCTION__ << " " << e.errString().c_str() <<"("<< e.errCode() <<")"<< endl;
			}
		}
		else
		{
			cout << "*** Error: There has been no request in the queue!" << endl;
		}

		g_bTerminated = WaitForInput ( 0, STDIN_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[] )
//-----------------------------------------------------------------------------
{
	char DevSerial[80];
	memset( DevSerial, 0, 80 );
	sprintf( DevSerial, "SD*" );

#ifdef MALLOC_TRACE
	mtrace();
#endif	// MALLOC_TRACE

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

	if( argc > 1 )
	{
		sprintf( DevSerial, "%s", argv[1] );
		if( argc > 2 )
			g_bLoadSettings = atoi( argv[2] );
	}

	cout << "Device serial: "<< DevSerial << endl;
	unsigned int uiDevCount = 0;
	DeviceManager devMgr;
	if(( uiDevCount = devMgr.deviceCount()) == 0 )
	{
		cout << "No MATRIX VISION device found! Unable to continue!" << endl;
		return 0;
	}

	cout << "Have found " << uiDevCount << " devices on this platform!" << endl;

	for( unsigned i = 0; i < uiDevCount; i++ )
	{
		Device* pDevTmp = devMgr.getDevice( i );

		cout << " " << i << " Serial: " << pDevTmp->serial.read() << endl;
	}

	cout << "Initialising the device: "<< DevSerial << ". This might take some time..." << endl;
	// create an interface to the first MATRIX VISION device with the serial number pDevSerial
	Device* pDev = devMgr.getDeviceBySerial( DevSerial );

	// initialise display window
	// IMPORTANT: It's NOT safe to create multiple display windows in multiple threads!!!
	string windowTitle( "mvIMPACT_acquire sample, Device " + pDev->serial.read() );
	cout << "<<" << windowTitle << ">>" << endl;

	// start the execution of the 'live' loop.
	liveLoop( pDev );
	cout << " == " << __LINE__ << endl;

	pDev->close( );
	//no need to delete pDev here. It will be destroyed automatically when the device manager (devMgr) is destroyed

	cout << " -- end application...." << endl;

	return 0;
}

