//-----------------------------------------------------------------------------
int main( int argc, char* argv[] )
//-----------------------------------------------------------------------------
{
    DeviceManager devMgr;
    cout << "This sample is meant for mvBlueLYNX-M7 devices only. Other devices might be installed" << endl
         << "but won't be recognized by the application." << endl
         << endl;

    int exposureTime_us = 20000;
    int frameRate_Hz = 5;
    int width = -1;
    int height = -1;
    int interPacketDelay = 25;

    // scan command line
    if( argc > 1 )
    {
        for( int i = 1; i < argc; i++ )
        {
            string param( argv[i] ), key, value;
            string::size_type keyEnd = param.find_first_of( "=" );
            if( ( keyEnd == string::npos ) || ( keyEnd == param.length() - 1 ) )
            {
                cout << "Invalid command line parameter: '" << param << "' (ignored)." << endl;
            }
            else
            {
                key = param.substr( 0, keyEnd );
                value = param.substr( keyEnd + 1 );
                if( ( key == "exposureTime" ) || ( key == "et" ) )
                {
                    exposureTime_us = static_cast<int>( atoi( value.c_str() ) );
                }
                else if( ( key == "frameRate" ) || ( key == "fr" ) )
                {
                    frameRate_Hz = static_cast<int>( atoi( value.c_str() ) );
                }
                else if( ( key == "width" ) || ( key == "w" ) )
                {
                    width = static_cast<int>( atoi( value.c_str() ) );
                }
                else if( ( key == "height" ) || ( key == "h" ) )
                {
                    height = static_cast<int>( atoi( value.c_str() ) );
                }
                else if( key == "scpd" )
                {
                    interPacketDelay = static_cast<int>( atoi( value.c_str() ) );
                }
                else
                {
                    cout << "Invalid command line parameter: '" << param << "' (ignored)." << endl;
                }
            }
        }
    }
    else
    {
        cout << "No command line parameters specified. Available parameters:" << endl
             << "  'frameRate' or 'fr' to specify the frame rate(frames per second per sensor head) of the resulting data stream" << endl
             << "  'exposureTime' or 'et' to specifiy the exposure time per frame in us" << endl
             << "  'width' or 'w' to specifiy the width of the AOI" << endl
             << "  'height' or 'h' to specifiy the height of the AOI" << endl
             << endl
             << "USAGE EXAMPLE:" << endl
             << "  SynchronousCaptureMultipleInputs et=5000 frameRate=5" << endl << endl;
    }

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

    try
    {
        cout << "Please note, that this sample (depending on the selected frame rate and resolution) might require a lot" << endl
             << "of network bandwidth, thus to achieve optimal results, it's crucial to have" << endl
             << "  - a good, reliable network controller (we recommed the Intel PRO/1000 series)" << endl
             << "  - the latest driver for the network controller installed" << endl
             << "  - jumbo frames enabled and the receive and transmit buffer for the network controller set to max. values" << endl
             << "  - the InterfaceMTU on the mvBlueLYNX-M7 set to its maximum value" << endl
             << endl
             << "In case of 'rrFrameIncomplete' errors the reason is most certainly to be found in one of"
             << "the requirments listed above not being met."
             << endl;

        cout << "Will try to synchronize sensor heads(" << frameRate_Hz << " fps per head with " << exposureTime_us << "us exposure time per frame) now" << endl
             << "During this operation the device will be initialised. This might take some time..." << endl;

        Connector connector( pDev );
        CameraSettingsBlueCOUGAR cs( pDev );
        try
        {
            if( width != -1 )
            {
                cs.aoiWidth.write( width );
            }
            if( height != -1 )
            {
                cs.aoiHeight.write( height );
            }
        }
        catch( const ImpactAcquireException& e )
        {
            cout << "Failed to set up AOI: " << e.getErrorString() << "(" << e.getErrorCodeAsString() << ")" << endl;
        }
        const int SENSOR_HEAD_COUNT = connector.videoChannel.read( plMaxValue ) + 1;
        setupHRTC( pDev, frameRate_Hz, exposureTime_us, SENSOR_HEAD_COUNT );

        // initialise display windows
        // IMPORTANT: It's NOT save to create multiple display windows in multiple threads!!!
        ThreadParameter threadParam( pDev, SENSOR_HEAD_COUNT );
        DeviceComponentLocator locator( pDev, dltSystemSettings );
        PropertyI64 gevStreamChannelSelector;
        locator.bindComponent( gevStreamChannelSelector, "GevStreamChannelSelector" );
        PropertyI64 gevSCPD;
        locator.bindComponent( gevSCPD, "GevSCPD" );
        for( int i = 0; i < SENSOR_HEAD_COUNT; i++ )
        {
            gevStreamChannelSelector.write( i );
            gevSCPD.write( interPacketDelay );
            MyWindow* p = createWindow( cs.aoiWidth.read(), cs.aoiHeight.read() );
            p->show();
            p->callback( windowCallback );
            threadParam.displayData.push_back( new DisplayInfo( p ) );
        }

        // start the execution of the 'live' thread.
        cout << "Close any of the display windows to end the application" << endl;
        liveLoop( &threadParam );
        vector<DisplayInfo*>::size_type displayCount = threadParam.displayData.size();
        for( vector<DisplayInfo*>::size_type i = 0; i < displayCount; i++ )
        {
            delete threadParam.displayData[i]->pDisp;
            delete threadParam.displayData[i];
            threadParam.displayData[i] = 0;
        }
    }
    catch( const ImpactAcquireException& e )
    {
        // this e.g. might happen if the same device is already opened in another process...
        cout << "An error occurred while configuring device " << pDev->serial.read()
             << "(error code: " << e.getErrorCodeAsString() << "). Press [ENTER] to end the application..." << endl;
        cin.get();
    }
    return 0;
}
//-----------------------------------------------------------------------------
unsigned int liveLoop( Device* pDev, bool boStoreFrames, const string& settingName, int iWidth, int iHeight, bool boSingleShotMode )
//-----------------------------------------------------------------------------
{
    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;
        }
    }

    // depending on the device and its sensor, we set an appropriate output format for displaying
    mvIMPACT::acquire::ImageDestination id( pDev );

    if( !std::string( "mvBlueFOX" ).compare( pDev->family.readS() ) )
    {
        mvIMPACT::acquire::InfoBlueFOX ibf( pDev );
        if( !std::string( "BayerMosaic" ).compare( ibf.sensorColorMode.readS() ) )
        {
            id.pixelFormat.writeS( "BGR888Packed" );
        }
    }
    else if( !std::string( "mvBlueCOUGAR" ).compare( pDev->family.readS() ) )
    {
        mvIMPACT::acquire::GenICam::ImageFormatControl ifc( pDev );
        if( s_boGreySensor )
        {
            ifc.pixelFormat.writeS( "Mono8" );
        }
        else
        {
            GenICamDeviceSetColorPixelFormat( pDev );
            id.pixelFormat.writeS( "BGR888Packed" );
        }
    }
    else if( !std::string( "mvBlueLYNX" ).compare( pDev->family.readS() ) )
    {
        mvIMPACT::acquire::GenICam::ImageFormatControl ifc( pDev );
        if( s_boGreySensor )
        {
            ifc.pixelFormat.writeS( "Mono8" );
        }
        else
        {
            GenICamDeviceSetColorPixelFormat( pDev );
            id.pixelFormat.writeS( "BGR888Packed" );
        }
    }

    // Pre-fill the capture queue with ALL buffers currently available. In case the acquisition engine is operated
    // manually, buffers can only be queued when they have been queued before the acquisition engine is started as well.
    // Even though there can be more than 1, for this sample we will work with the default capture queue
    int requestResult = DMR_NO_ERROR;
    int requestCount = 0;

    if( boSingleShotMode )
    {
        fi.imageRequestSingle();
        ++requestCount;
    }
    else
    {
        while( ( requestResult = fi.imageRequestSingle() ) == DMR_NO_ERROR )
        {
            ++requestCount;
        }
    }

    if( requestResult != DEV_NO_FREE_REQUEST_AVAILABLE )
    {
        cout << "Last result: " << requestResult << "(" << ImpactAcquireException::getErrorCodeAsString( requestResult ) << "), ";
    }
    cout << requestCount << " buffers requested";
    SystemSettings ss( pDev );
    if( ss.requestCount.hasMaxValue() )
    {
        cout << ", max request count: " << ss.requestCount.getMaxValue();
    }
    cout << endl;

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

    MyWindow* pWindow = createWindow( iWidth, iHeight );
    pWindow->show();
    pWindow->callback( windowCallback );

    manuallyStartAcquisitionIfNeeded( pDev, fi );
    // 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;
    unsigned int cnt = 0;
    while( boLoopRunning )
    {
        // wait for results from the default capture queue
        requestNr = fi.imageRequestWaitFor( timeout_ms );
        if( fi.isRequestNrValid( requestNr ) )
        {
            pRequest = fi.getRequest( requestNr );
            if( pRequest->isOK() )
            {
                ++cnt;
                // here we can display some statistical information every 20th image
                if( cnt % 20 == 0 )
                {

                    ostringstream out;
                    out << pDev->serial.read() << ": " << statistics.framesPerSecond.name() << ": " << statistics.framesPerSecond.readS();
                    pWindow->setOverlayString( out.str() );

                    cout << cnt << ": Info from " << pDev->serial.read()
                         << ": " << statistics.framesPerSecond.name() << ": " << statistics.framesPerSecond.readS()
                         << ", " << statistics.errorCount.name() << ": " << statistics.errorCount.readS()
                         << ", " << statistics.captureTime_s.name() << ": " << statistics.captureTime_s.readS()
                         << " Image count: " << cnt
                         << " (dimensions: " << pRequest->imageWidth.read() << "x" << pRequest->imageHeight.read() << ", format: " << pRequest->imagePixelFormat.readS();
                    if( pRequest->imageBayerMosaicParity.read() != bmpUndefined )
                    {
                        cout << ", " << pRequest->imageBayerMosaicParity.name() << ": " << pRequest->imageBayerMosaicParity.readS();
                    }
                    cout << ")" << endl;
                }
                // here we can store an image every 100th frame
                if( boStoreFrames && ( cnt % 100 == 0 ) )
                {
                    ostringstream oss;
                    oss << "Image" << cnt << "." << pRequest->imageWidth.read() << "x" << pRequest->imageHeight.read() << "." << pRequest->imagePixelFormat.readS();
                    if( pRequest->imageBayerMosaicParity.read() != bmpUndefined )
                    {
                        oss << "(BayerPattern=" << pRequest->imageBayerMosaicParity.readS() << ")";
                    }
                    oss << ".raw";
                    FILE* fp = fopen( oss.str().c_str(), "wb" );
                    if( fp )
                    {
                        unsigned char* pImageData = ( unsigned char* ) pRequest->imageData.read();
                        for( int h = 0; h < pRequest->imageHeight.read(); h++ )
                        {
                            // write one line
                            fwrite( pImageData, pRequest->imageWidth.read(), pRequest->imageBytesPerPixel.read(), fp );
                            // respect image line pitch
                            pImageData += pRequest->imageLinePitch.read();
                        }
                        fclose( fp );
                    }
                }

                // everything went well. Display the result
                pWindow->NewRequest( pRequest );
                pWindow->redraw();
                Fl::check();
            }
            else
            {
                cout << "*** Error: A request has been returned with the following result: " << 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();
            if( boSingleShotMode )
            {
                manuallyStartAcquisitionIfNeeded( pDev, fi );
            }
        }
        else
        {
            cout << "*** Error: Result of waiting for a finished request: " << requestNr << "("
                 << ImpactAcquireException::getErrorCodeAsString( requestNr ) << "). Timeout value too small?" << endl;
        }

        boLoopRunning = waitForInput( 0, STDOUT_FILENO ) == 0 ? true : false; // break by STDIN
        // Exit when window was cloesd
        if( s_boTerminated )
        {
            break;
        }
    }

    if( !boSingleShotMode )
    {
        manuallyStopAcquisitionIfNeeded( pDev, fi );
    }
    cout << " == " << __FUNCTION__ << " - free resources...." << endl;
    // free resources
    fi.imageRequestReset( 0, 0 );
    return 0;
}