//-----------------------------------------------------------------------------
#ifndef CaptureThreadH
#define CaptureThreadH CaptureThreadH
//-----------------------------------------------------------------------------
#include "balluffcamera.h"
#include <QThread>
#include <QMutex>
#include <QTimer>
#include <qdebug.h>

#include "SyncQueue.h"
#include "ASyncQueue.h"

#include "apps/Common/exampleHelper.h"
#include <apps/Common/qtIncludePrologue.h>
#include <apps/Common/qtIncludeEpilogue.h>

#include <opencv2/opencv.hpp>

#include <common.h>

extern SingleCamInfoStruct SingleCamInfo[NumberOfSupportedCameras];

class CaptureThread_Func
{
public:
    Device* m_pMVCamera;
    ASyncQueue<bool> *p_result_queue_;
    ASyncQueue<bool> *p_double_queue_;
    void SendFeedBack(int OpID)
    {
        bool send_ng=false;
        bool send_ok=false;
        if(OpID == EdgeEvent)
        {
#if defined DOUBLE_FEED_BACK
            if(p_double_queue_->count() > 0)
            {
                bool temp;
                p_double_queue_->take(temp);
                send_ng=true;
            }
#endif
        }
        if (p_result_queue_->count() > 0)
        {
            bool result;
            p_result_queue_->take(result);
            if (!result)
			{
#if defined DOUBLE_FEED_BACK
				p_double_queue_->put(true);
#endif
                send_ng=true;
            }
			else
			{
				send_ok=true;
			}
        }
		try
		{
			mvIMPACT::acquire::GenICam::DigitalIOControl mvDIOC(m_pMVCamera);
			if(send_ng){
				mvDIOC.userOutputSelector.writeS("UserOutput0");
				mvIMPACT::acquire::TBoolean tb = mvDIOC.userOutputValue.read();
				if (tb == bFalse)
					mvDIOC.userOutputValue.write(bTrue);
				else
					mvDIOC.userOutputValue.write(bFalse);
			}
			else if (send_ok)
			{
				mvDIOC.userOutputSelector.writeS("UserOutput1");
				mvIMPACT::acquire::TBoolean tb = mvDIOC.userOutputValue.read();
				if (tb == bFalse)
					mvDIOC.userOutputValue.write(bTrue);
				else
					mvDIOC.userOutputValue.write(bFalse);
			}
		}
		catch (const ImpactAcquireException& e)
		{
			std::cout<< "(error code: " << e.getErrorCodeAsString() << "). Press [ENTER] to end the application..." << std::endl;
		}
    }
};

class CaptureThread : public QObject
//-----------------------------------------------------------------------------
{
    Q_OBJECT

public:
    explicit CaptureThread( Device* pCurrDev, bool boTerminated, FunctionInterface* pFi ,int Num);
	CaptureThread::~CaptureThread(void);
    void terminate( void )
    {
        boTerminated_ = true;
    }
    int getPendingRequestNr();
	
signals:
    void error( QString err );
    void finished( void );
    void requestReady( void );
    void updateStatistics( const QString& data ,int Num);

private slots:
    void process( void );
	void fpsTimeout(void);

public:
	int Local_Num;
	QTimer* m_Timer;
	uint64_t m_cntGrabbedImages = 0;
	uint64_t m_cntLastGrabbedImages = 0;
	SyncQueue<std::pair<int, cv::Mat> > *p_image_queue;
	ASyncQueue<cv::Mat> *p_unit_queue;
	ASyncQueue<bool> *p_result_queue;
	ASyncQueue<bool> *p_result_wait_queue;
	ASyncQueue<bool> *p_double_queue;
    ASyncQueue<bool> *p_shooted_queue;
	SyncQueue<cv::Mat> *p_debug_queue;
    bool Ready = false;
	CaptureThread_Func m_threadFunc;
private:
    Device* pDev_;
    volatile bool boTerminated_;
    int requestPendingForDisplay_;
    FunctionInterface* pFI_;
    QMutex lock_;
};

//=============================================================================
//================= Camera's property callback ================================
//=============================================================================
class EventCallback : public mvIMPACT::acquire::ComponentCallback
{
public:
	ASyncQueue<cv::Mat> *p_unit_queue_;
	SyncQueue<std::pair<int, cv::Mat> > *p_image_queue_;
	ASyncQueue<bool> *p_result_wait_queue_;
	ASyncQueue<bool> *p_shooted_queue_;
	ASyncQueue<bool> *p_double_queue_;
	explicit EventCallback(void* pUserData = 0) : ComponentCallback(pUserData) {}
	Device* m_pMVCamera;
	CaptureThread* pCaptureThread = NULL;
	int count = 0;

	virtual void execute(Component& c, void* pUserData)
	{
		try
		{
			// re-generating the object/data previously attached to the callback object. This could now be used to call a certain member function e.g. to update a class instance about this event!
			mvIMPACT::acquire::GenICam::EventControl* ec = reinterpret_cast<mvIMPACT::acquire::GenICam::EventControl*>(pUserData);
			// Execute the followings if the component is a property.
			if (c.isProp())
			{
				mvIMPACT::acquire::GenICam::DigitalIOControl mvDIOC(m_pMVCamera);
				mvDIOC.lineSelector.writeS("Line5");
				if (mvDIOC.lineStatus.read())
				{
#ifdef IMM_FEED_BACK
					if (p_shooted_queue_->count() > 0)
					{
						bool temp;
						p_shooted_queue_->take(temp);
					}
#elif defined ONE_TIME_SHIFT
					if (
						p_shooted_queue_->count() > 0
#if defined DOUBLE_FEED_BACK
						|| p_double_queue_->count() >0
#endif
					)
					{
						if (p_shooted_queue_->count() > 0){
							bool temp;
							p_shooted_queue_->take(temp);
						}
						pCaptureThread->m_threadFunc.SendFeedBack(EdgeEvent);
					}
#else
					if (p_result_wait_queue_->count() > 0)
					{
						bool temp;
						p_result_wait_queue_->take(temp);
						pCaptureThread->m_threadFunc.SendFeedBack(EdgeEvent);
					}
					if (p_shooted_queue_->count() > 0)
					{
						bool temp;
						p_shooted_queue_->take(temp);
						p_result_wait_queue_->put(true);
					}
#endif
				}
				else
				{
					int unit_count = p_unit_queue_->count();
					if (unit_count > 0)
					{
						cv::Mat long_image;
						for (int i = 0; i < unit_count; i++)
						{
							cv::Mat image;
							p_unit_queue_->take(image);
							if (0 == i)
							{
								long_image = cv::Mat::zeros(image.rows * unit_count, image.cols, image.type());
							}
							cv::Rect r(0, i * image.rows, image.cols, image.rows);
							cv::Mat roi = long_image(r);
							image.copyTo(roi);
						}
						p_image_queue_->put(std::make_pair(unit_count, long_image));
						p_shooted_queue_->put(true);
					}
					p_unit_queue_->clear();
				}
			}
		}
		catch (const ImpactAcquireException& e)
		{
			qDebug() << "Error";
		}
	}
};

class CIwtCameraLostCallbackMV : public mvIMPACT::acquire::ComponentCallback
{
public:
	CIwtCameraLostCallbackMV() : ComponentCallback() {}
	int channel_;
	virtual void execute(mvIMPACT::acquire::Component& c, void* pDummy)
	{
		PropertyIDeviceState p(c);
		std::cout << p.name() << " = " << p.readS() << endl;
		SingleCamInfo[channel_].OffLine = true;
	}
};

//-----------------------------------------------------------------------------
#endif // CaptureThreadH
