//-----------------------------------------------------------------------------
#ifndef CaptureThreadBaslerH
#define CaptureThreadBaslerH CaptureThreadBaslerH
//-----------------------------------------------------------------------------
#include "baslercamera.h"
#include <QThread>
#include <QMutex>
#include <QTimer>

#include "SyncQueue.h"
#include "ASyncQueue.h"
#include "common.h"
#include <opencv2/opencv.hpp>

extern bool g_debug_mode;
extern SingleCamInfoStruct SingleCamInfo[NumberOfSupportedCameras];
enum MyEvents
{
    eMyFrameBurstStartEvent = 100,
    Line1RisingEdge = 200,
    Line1FallingEdge = 300
    // More events can be added here.
};

class CaptureThreadBasler_Func
{
public:
    Pylon::CBaslerUniversalInstantCamera* pDev__;
    ASyncQueue<bool> *p_result_queue_;
    ASyncQueue<bool> *p_double_queue_;
    QTimer *m_IOTimer_ = NULL;
    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;
            }
        }
#ifndef USB_BASLER_NEW_FW
        if (send_ng) {
            pDev__->UserOutputSelector.SetValue(Basler_UniversalCameraParams::UserOutputSelector_UserOutput3);
            pDev__->UserOutputValue.SetValue(true);
        }
        else if (send_ok) {
            pDev__->UserOutputSelector.SetValue(Basler_UniversalCameraParams::UserOutputSelector_UserOutput1);
            pDev__->UserOutputValue.SetValue(true);
        }
        m_IOTimer_->start(StrobeLineTime / 2000);
#else
        if (send_ng) {
            pDev__->UserOutputSelector.SetValue(Basler_UniversalCameraParams::UserOutputSelector_UserOutput3);
            pDev__->UserOutputValue.SetValue(false);
            pDev__->UserOutputValue.SetValue(true);
        }
        else if (send_ok) {
            pDev__->UserOutputSelector.SetValue(Basler_UniversalCameraParams::UserOutputSelector_UserOutput1);
            pDev__->UserOutputValue.SetValue(false);
            pDev__->UserOutputValue.SetValue(true);
        }
#endif
    }
};

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

public:
    explicit CaptureThreadBasler(Pylon::CBaslerUniversalInstantCamera* pCurrDev, bool boTerminated,int Num,int shoot);
    CaptureThreadBasler::~CaptureThreadBasler(void);
    void terminate( void )
    {
        boTerminated_ = true;
    }

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);
    void ioTimeout(void);

public:
    int Local_Num;
    int Shoot_Num;
	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;
    QTimer *m_Timer = NULL,*m_IOTimer = NULL;
    uint64_t m_cntGrabbedImages = 0;
    uint64_t m_cntLastGrabbedImages = 0;
    bool Ready = false;
    CaptureThreadBasler_Func m_threadFunc;
private:
    Pylon::CBaslerUniversalInstantCamera* pDev_;
    volatile bool boTerminated_;
    QMutex lock_;
};
//-----------------------------------------------------------------------------
class CSampleConfigurationEventHandler : public Pylon::CBaslerUniversalConfigurationEventHandler //CConfigurationEventHandler
{
public:
    int channel_;
    // This method is called from a different thread when the camera device removal has been detected.
    void OnCameraDeviceRemoved( Pylon::CBaslerUniversalInstantCamera& camera )
    {
        SingleCamInfo[channel_].OffLine = true;
    }
};

// Example handler for camera events.
class CSampleCameraEventHandler : public Pylon::CBaslerUniversalCameraEventHandler
{
public:
    QTimer * ioTimer;
    ASyncQueue<cv::Mat>* p_unit_queue_;
    ASyncQueue<bool> *p_result_wait_queue_;
    ASyncQueue<bool> *p_result_queue_;
    ASyncQueue<bool> *p_double_queue_;
    ASyncQueue<bool> *p_shooted_queue_;
    SyncQueue<std::pair<int, cv::Mat> >* p_image_queue_;
    Pylon::CBaslerUniversalInstantCamera* pDev__;
    CaptureThreadBasler* pCaptureThreadBasler = NULL;
    // Only very short processing tasks should be performed by this method. Otherwise, the event notification will block the
    // processing of images.
    virtual void OnCameraEvent(Pylon::CBaslerUniversalInstantCamera& camera, intptr_t userProvidedId, GenApi::INode*  pNode)
    {
        switch (userProvidedId)
        {
#ifndef USB_BASLER_NEW_FW
            case eMyFrameBurstStartEvent:
            {
#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);
                    }
                    pCaptureThreadBasler->m_threadFunc.SendFeedBack(EdgeEvent);
                }
#else
                if(p_unit_queue_->count() > 0){
                    int unit_count = p_unit_queue_->count();
                    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();
                }
                if(p_result_wait_queue_->count() > 0)
                {
                    bool temp;
                    p_result_wait_queue_->take(temp);
                    pCaptureThreadBasler->m_threadFunc.SendFeedBack(EdgeEvent);
                }
                if(p_shooted_queue_->count() > 0)
                {
                    bool temp;
                    p_shooted_queue_->take(temp);
                    p_result_wait_queue_->put(true);
                }
#endif
            break;
            }
#else
            case Line1RisingEdge:
            {
#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
				)
                {
                    bool temp;
                    p_shooted_queue_->take(temp);
                    pCaptureThreadBasler->m_threadFunc.SendFeedBack(EdgeEvent);
                }
#else
                if(p_result_wait_queue_->count() > 0)
                {
                    bool temp;
                    p_result_wait_queue_->take(temp);
                    pCaptureThreadBasler->m_threadFunc.SendFeedBack(EdgeEvent);
                }
                if(p_shooted_queue_->count() > 0)
                {
                    bool temp;
                    p_shooted_queue_->take(temp);
                    p_result_wait_queue_->put(true);
                }
#endif
                break;
            }
            case Line1FallingEdge:
            {
                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();
                break;
            }
#endif
        default:
            break;
        }
    }
};

class CSampleImageEventHandler : public Pylon::CBaslerUniversalImageEventHandler
{
public:
    ASyncQueue<cv::Mat>* p_unit_queue_;
    SyncQueue<std::pair<int, cv::Mat> >* p_image_queue_;
    SyncQueue<cv::Mat>* p_debug_queue_;
    ASyncQueue<bool> *p_shooted_queue_;
    Pylon::CBaslerUniversalInstantCamera* pDev__;
    uint64_t* m_cntGrabbedImages_;
    int Shoot_Num_;
    virtual void OnImageGrabbed(Pylon::CBaslerUniversalInstantCamera& camera, const Pylon::CBaslerUniversalGrabResultPtr& ptrGrabResult)
    {
        // Create a pylon ImageFormatConverter object.
        Pylon::CImageFormatConverter formatConverter;

        // Specify the output pixel format.
        formatConverter.OutputPixelFormat = Pylon::PixelType_Mono8;

        cv::Mat openCvImage;

        // Create a PylonImage that will be used to create OpenCV images later.
        Pylon::CPylonImage pylonImage;

        // Convert the grabbed buffer to a pylon image.
        formatConverter.Convert(pylonImage, ptrGrabResult);

        // Create an OpenCV image from a pylon image.
        openCvImage = cv::Mat(ptrGrabResult->GetHeight(), ptrGrabResult->GetWidth(), CV_8UC1, (uint8_t*)pylonImage.GetBuffer());
        cv::Mat image_clone = openCvImage.clone();

        if (!g_debug_mode)
        {
#ifdef IMM_PROCESS
            p_image_queue_->put(std::make_pair(1,image_clone));
#else
            p_unit_queue_->put(image_clone);
#endif
        }
        else
        {
            p_debug_queue_->put(image_clone);
        }
        (*m_cntGrabbedImages_)++;
#ifndef IMM_PROCESS
#ifndef USB_BASLER_NEW_FW
        int unit_count = p_unit_queue_->count();
        if (unit_count == Shoot_Num_)
        {
            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);
            DEBUG("    unit_count\n");
            p_unit_queue_->clear();
        }
#endif
#endif
    }
};

#endif // CaptureThreadBasler
