#include "videowidget.h"
#include <QPainter>
#include <QPen>
#include <QBrush>
#include <QPoint>
#include <QResizeEvent>
extern "C" {
#include <gst/interfaces/xoverlay.h>
}

const QSize VideoWidget::SIZE_QCIF = QSize(176, 144);
const QSize VideoWidget::SIZE_CIF  = QSize(352, 288);
const QSize VideoWidget::SIZE_2CIF = QSize(704, 288);
const QSize VideoWidget::SIZE_4CIF = QSize(704, 576);

/*lint -e960 bus has been commented out to avoid compiler warnings*/
bool VideoWidget::handleMessage (GstBus * /* bus */, GstMessage *message, gpointer data)
/*lint +960*/
{
    VideoWidget *videoWidget = (VideoWidget *)data;
    switch (GST_MESSAGE_TYPE (message)) {
    case GST_MESSAGE_ELEMENT:
        {
            if (GST_MESSAGE_SRC (message) == GST_OBJECT(videoWidget->a_sink)) {
                gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (videoWidget->a_sink), videoWidget->a_winId);
            }
            break;
        }
        
    default:
        break;
    }
    return true;
}

VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent)
{
    /*lint -e923 Required by Gstreamer Xoverlay interface*/
    a_winId            = (unsigned long)winId();
    /*lint -e923*/
    a_sigId            = 0;
    a_mirror           = false;
    a_forceAspectRatio = true;
    a_cropLeft         = 0;
    a_cropRight        = 0;
    a_cropTop          = 0;
    a_cropBottom       = 0;
    a_resolution       = VideoWidget::E_CUSTOM;
    a_videoStream      = NULL;
    a_queue            = NULL;
    a_videoCrop        = NULL;
    a_videoFlip        = NULL;
    a_colorSpace       = NULL;
    a_sink             = NULL;
    setAutoFillBackground(true);
    setPalette(QPalette(palette().color(QPalette::Button), Qt::black));
}

VideoWidget::~VideoWidget()
{
    if (a_videoStream) {
        a_videoStream->stop();
        disconnect();
    }
}

void VideoWidget::connect(VideoStream* videoStream)
{
    if (!connected()) {
        
        /*lint -e961 GST_STATE macro is part of Gstreamer interface*/
        if (GST_STATE(videoStream->a_pipeline) != GST_STATE_NULL) {
            /*lint +e961*/
            qFatal("Connect Video Widget to a playing Stream");
        }
        
        /* create elements */
        a_queue = gst_element_factory_make ("queue", NULL);
        a_videoCrop = gst_element_factory_make ("videocrop", NULL);
        g_object_set (G_OBJECT (a_videoCrop), "left", a_cropLeft, "right", a_cropRight,
            "top", a_cropTop, "bottom", a_cropBottom, NULL);
        a_videoFlip = gst_element_factory_make ("videoflip", NULL);
        if (a_mirror) {
            g_object_set (G_OBJECT (a_videoFlip), "method", 4, NULL);
        } else {
            g_object_set (G_OBJECT (a_videoFlip), "method", 0, NULL);
        }
        a_colorSpace = gst_element_factory_make ("ffmpegcolorspace", NULL);
#if defined(Q_WS_X11)
        a_sink = gst_element_factory_make ("ximagesink", NULL);
#elif defined(Q_WS_WIN)
        a_sink = gst_element_factory_make ("directdrawsink", NULL);
#endif
        g_object_set (G_OBJECT (a_sink), "force-aspect-ratio", a_forceAspectRatio, NULL);
        a_sigId = g_signal_connect (videoStream->a_bus, "sync-message", G_CALLBACK (handleMessage), this);
        
        if (!a_queue || !a_videoCrop || !a_videoFlip || !a_colorSpace || !a_sink)
        {
            qFatal("GStreamer elements creation failure");
        }
        
        bool success=true;
        
        /* must add elements to pipeline before linking them */
        success &= (bool)gst_bin_add (GST_BIN (videoStream->a_pipeline), a_queue);
        success &= (bool)gst_bin_add (GST_BIN (videoStream->a_pipeline), a_videoCrop);
        success &= (bool)gst_bin_add (GST_BIN (videoStream->a_pipeline), a_videoFlip);
        success &= (bool)gst_bin_add (GST_BIN (videoStream->a_pipeline), a_colorSpace);
        success &= (bool)gst_bin_add (GST_BIN (videoStream->a_pipeline), a_sink);
        if (!success) {
            qFatal("GStreamer elements add failure");
        }
        
        /* link */
        success&=(bool)gst_element_link_many (videoStream->a_tee, a_queue, a_videoCrop,
            a_videoFlip, a_colorSpace, a_sink, NULL);
        if (!success) {
            qFatal("GStreamer elements link failure");
        }
        videoStream->addConnection(this);
        a_videoStream = videoStream;
        
    } else {
        qWarning("Video Widget already connected");
    }
}

void VideoWidget::disconnect()
{
    if (connected()) {
        
        /*lint -e961 GST_STATE macro is part of Gstreamer interface*/
        if (GST_STATE(a_videoStream->a_pipeline) != GST_STATE_NULL) {
            /*lint +e961*/
            qFatal("Disconnect Video Widget from a playing Stream");
        }
        
        /*Remove signals */
        if (g_signal_handler_is_connected(a_videoStream->a_bus, a_sigId)) {
            g_signal_handler_disconnect(a_videoStream->a_bus, a_sigId);
        }
        
        /* remove elements from pipeline.
        Unlink will be done when removing */
        bool success=true;
        success &= (bool)gst_bin_remove (GST_BIN (a_videoStream->a_pipeline), a_queue);
        success &= (bool)gst_bin_remove (GST_BIN (a_videoStream->a_pipeline), a_videoCrop);
        success &= (bool)gst_bin_remove (GST_BIN (a_videoStream->a_pipeline), a_videoFlip);
        success &= (bool)gst_bin_remove (GST_BIN (a_videoStream->a_pipeline), a_colorSpace);
        success &= (bool)gst_bin_remove (GST_BIN (a_videoStream->a_pipeline), a_sink);
        if (!success) {
            qFatal("GStreamer elements remove failure");
        }
        a_videoStream->removeConnection(this);
        a_videoStream = NULL;
        
    } else {
        qWarning("Video Widget not connected");
    }
}

bool VideoWidget::connected() const
{
    return a_videoStream != NULL;
}

void VideoWidget::setCropLeft(int pixel)
{
    a_cropLeft = pixel;
    if (a_videoCrop) {
        g_object_set (G_OBJECT (a_videoCrop), "left", pixel, NULL);
    }
}

uint VideoWidget::getCropLeft() const
{
    return a_cropLeft;
}

void VideoWidget::setCropRight(int pixel)
{
    a_cropRight = pixel;
    if (a_videoCrop) {
        g_object_set (G_OBJECT (a_videoCrop), "right", pixel, NULL);
    }
}

uint VideoWidget::getCropRight() const
{
    return a_cropRight;
}

void VideoWidget::setCropTop(int pixel)
{
    a_cropTop = pixel;
    if (a_videoCrop) {
        g_object_set (G_OBJECT (a_videoCrop), "top", pixel, NULL);
    }
}

uint VideoWidget::getCropTop() const
{
    return a_cropTop;
}

void VideoWidget::setCropBottom(int pixel)
{
    a_cropBottom = pixel;
    if (a_videoCrop) {
        g_object_set (G_OBJECT (a_videoCrop), "bottom", pixel, NULL);
    }
}

uint VideoWidget::getCropBottom() const
{
    return a_cropBottom;
}

void VideoWidget::setMirror(bool mirror)
{
    a_mirror = mirror;
    if (a_videoFlip) {
        if (mirror) {
            g_object_set (G_OBJECT (a_videoFlip), "method", 4, NULL);
        } else {
            g_object_set (G_OBJECT (a_videoFlip), "method", 0, NULL);
        }
    }
}

bool VideoWidget::getMirror() const
{
    return a_mirror;
}

void VideoWidget::setForceAspectRatio(bool force)
{
    a_forceAspectRatio = force;
    if (a_sink) {
        g_object_set (G_OBJECT (a_sink), "force-aspect-ratio", force, NULL);
    }
}

bool VideoWidget::getForceAspectRatio() const
{
    return a_forceAspectRatio;
}

void VideoWidget::setResolution(E_RESOLUTION resolution)
{
    a_resolution = resolution;
    switch (a_resolution) {
    case VideoWidget::E_QCIF:
        resize(SIZE_QCIF);
        break;
    case VideoWidget::E_CIF:
        resize(SIZE_CIF);
        break;
    case VideoWidget::E_2CIF:
        resize(SIZE_2CIF);
        break;
    case VideoWidget::E_4CIF:
        resize(SIZE_4CIF);
        break;
    }
}

VideoWidget::E_RESOLUTION VideoWidget::getResolution() const
{
    return a_resolution;
}

void VideoWidget::paintEvent(QPaintEvent * /* event */)
{
    QPainter painter (this);
    if (a_sink) {
        if (GST_IS_X_OVERLAY (a_sink)) {
            gst_x_overlay_expose (GST_X_OVERLAY (a_sink));
        }
    }
}

void VideoWidget::mousePressEvent(QMouseEvent * /* event */)
{
    emit sig_clicked();
}

void VideoWidget::resizeEvent(QResizeEvent * event)
{
    if (event->size() == SIZE_QCIF) {
        a_resolution = VideoWidget::E_QCIF;
    } else if (event->size() == SIZE_CIF) {
        a_resolution = VideoWidget::E_CIF;
    } else if (event->size() == SIZE_2CIF) {
        a_resolution = VideoWidget::E_2CIF;
    } else if (event->size() == SIZE_4CIF) {
        a_resolution = VideoWidget::E_4CIF;
    } else {
        a_resolution = VideoWidget::E_CUSTOM;
    }
}
