IPCWindow::IPCWindow()
{
    setStyleSheet("background-color: black;");
    setFixedSize(640, 480);

    _label = new QLabel(this);
    _label->setGeometry(QRect(0, 0, 640, 480));

    QSize img_sz = _gesture_handler.image_size();
    if (img_sz.width() && img_sz.height())
    {
        setFixedSize(img_sz.width(), img_sz.height());
        _label->setGeometry(QRect(0, 0, img_sz.width(), img_sz.height()));
    }

    _status_label = new QLabel(statusBar());
    _status_label->setAlignment(Qt::AlignCenter);
    _status_label->setStyleSheet("color: orange; background-color: black;");
    QFont sb_font("Arial", 11);
    sb_font.setBold(true);
    _status_label->setFont(sb_font);
    _status_label->setText("Running...");
    statusBar()->addWidget(_status_label, 1);

    QObject::connect(&_gesture_handler, SIGNAL(notify_gesture(PXCGesture::Gesture)),
                                  this, SLOT(_gesture_evt(PXCGesture::Gesture)));

    QObject::connect(&_gesture_handler, SIGNAL(notify_image(QPixmap)),
                                  this, SLOT(_image_evt(QPixmap)));

    if (!_gesture_handler.start())
    {
        _status_label->setText("!!! Failed to start gesture handler.");
        qApp->quit();
    }
}
/*
 *_stream() reads a single frame
 */
void GestureHandler::_stream()
{
    if (!_pipeline.AcquireFrame(true))    // true - blocking
        return;

    // Retrieve QueryImage() as PXCSmartPtr<PXCImage> will crash later on
    PXCImage* image = _pipeline.QueryImage(PXCImage::IMAGE_TYPE_COLOR);

    PXCImage::ImageData image_data;
    pxcStatus sts = image->AcquireAccess(PXCImage::ACCESS_READ,
                                         PXCImage::COLOR_FORMAT_RGB24,
                                         &image_data);
    if (sts < PXC_STATUS_NO_ERROR)
    {
        std::cout << "!!! Failed AcquireAccess." << std::endl;
        _pipeline.ReleaseFrame();
        return;
    }

    PXCImage::ImageInfo image_info;
    image->QueryInfo(&image_info);
    if (sts < PXC_STATUS_NO_ERROR)
    {
        std::cout << "!!! Failed QueryInfo." << std::endl;
        _pipeline.ReleaseFrame();
        return;
    }

    if (image_info.format == PXCImage::COLOR_FORMAT_RGB24)
    {   // BGR layout on little-endian machine
        unsigned char* bgr_img_data = (unsigned char*)image_data.planes[0];

        QImage temp(bgr_img_data,
                    image_info.width,
                    image_info.height,
                    QImage::Format_RGB888);

        // Convert data from BGR (PXCImage format) to RGB (QPixmap)
        QPixmap pixmap = QPixmap::fromImage(temp.rgbSwapped());
        emit notify_image(pixmap);
    }
    else
    {
        std::cout << "!!! TODO: support other image formats besides RGB24." << std::endl;
        _pipeline.ReleaseFrame();
        return;
    }

    image->ReleaseAccess(&image_data);
    if (sts < PXC_STATUS_NO_ERROR)
    {
        std::cout << "!!! Failed ReleaseAccess." << std::endl;
        _pipeline.ReleaseFrame();
        return;
    }

    /* Detect gestures */

    PXCGesture* gesture = _pipeline.QueryGesture();
    if (gesture)
    {
        // get the node data for the whole primary hand
        // (the one that shows up in the view first)
        PXCGesture::GeoNode node;
        sts = gesture->QueryNodeData(0, PXCGesture::GeoNode::LABEL_BODY_HAND_PRIMARY, &node);
        if (sts == PXC_STATUS_ITEM_UNAVAILABLE)
        {
            //std::cout << "!!! QueryNodeData: PXC_STATUS_ITEM_UNAVAILABLE" << std::endl;
            _pipeline.ReleaseFrame();
            return;
        }

        //std::cout << "* Hand at " << node.positionWorld.x << "x" << node.positionWorld.y << " z = " << node.positionWorld.z << std::endl;

        PXCGesture::Gesture gest;
        sts = gesture->QueryGestureData(0, PXCGesture::GeoNode::LABEL_BODY_HAND_PRIMARY, 0, &gest);
        if (sts == PXC_STATUS_ITEM_UNAVAILABLE)
        {
            //std::cout << "!!! QueryGestureData: PXC_STATUS_ITEM_UNAVAILABLE" << std::endl;
            _pipeline.ReleaseFrame();
            return;
        }

        if (gest.active)
        {
            // how many ms has passed since last gesture detection
            qint64 elapsed = _elapsed_timer.elapsed();

            switch (gest.label)
            {
                case PXCGesture::Gesture::LABEL_HAND_WAVE:
                case PXCGesture::Gesture::LABEL_NAV_SWIPE_LEFT:
                case PXCGesture::Gesture::LABEL_NAV_SWIPE_RIGHT:
                case PXCGesture::Gesture::LABEL_NAV_SWIPE_UP:
                case PXCGesture::Gesture::LABEL_NAV_SWIPE_DOWN:
                case PXCGesture::Gesture::LABEL_HAND_CIRCLE:
                default:
                    emit notify_gesture(gest);
                break;

                case PXCGesture::Gesture::LABEL_POSE_THUMB_UP:
                    if ( (_last_gesture == PXCGesture::Gesture::LABEL_POSE_THUMB_UP && elapsed > 1000) ||
                          (_last_gesture != PXCGesture::Gesture::LABEL_POSE_THUMB_UP) )
                        emit notify_gesture(gest);
                break;

                case PXCGesture::Gesture::LABEL_POSE_THUMB_DOWN:
                    if ( (_last_gesture == PXCGesture::Gesture::LABEL_POSE_THUMB_DOWN && elapsed > 1000) ||
                        (_last_gesture != PXCGesture::Gesture::LABEL_POSE_THUMB_DOWN) )
                        emit notify_gesture(gest);
                break;

                case PXCGesture::Gesture::LABEL_POSE_PEACE:
                    if ( (_last_gesture == PXCGesture::Gesture::LABEL_POSE_PEACE && elapsed > 1000) ||
                         (_last_gesture != PXCGesture::Gesture::LABEL_POSE_PEACE) )
                        emit notify_gesture(gest);
                break;

                case PXCGesture::Gesture::LABEL_POSE_BIG5:
                    if ( (_last_gesture == PXCGesture::Gesture::LABEL_POSE_BIG5 && elapsed > 1000) ||
                        (_last_gesture != PXCGesture::Gesture::LABEL_POSE_BIG5) )
                        emit notify_gesture(gest);
                break;
            }
            _last_gesture = gest.label;
            if (elapsed > 1000)
                _elapsed_timer.restart();
        }
    }

    _pipeline.ReleaseFrame();
}