/*  This file is part of the KDE project.

    Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).

    This library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2.1 or 3 of the License.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <QtGui/QApplication>

#include <gst/video/video.h>

#include "qwidgetvideosink.h"
#include "videowidget.h"

QT_BEGIN_NAMESPACE

namespace Phonon
{
namespace Gstreamer
{

static GstVideoSinkClass*   parentClass;

/*!
    \class gstreamer::QWidgetVideoSink
    \internal
*/

template <VideoFormat FMT>
GstCaps* QWidgetVideoSink<FMT>::get_caps(GstBaseSink* sink)
{
    Q_UNUSED(sink);
    return 0;
}

template <>
const char* QWidgetVideoSinkClass<VideoFormat_YUV>::get_name()
{
    return "QWidgetVideoSinkYUV";
}

template <>
const char* QWidgetVideoSinkClass<VideoFormat_RGB>::get_name()
{
    return "QWidgetVideoSinkRGB";
}

template <VideoFormat FMT>
gboolean QWidgetVideoSink<FMT>::set_caps(GstBaseSink* sink, GstCaps* caps)
{
    GstStructure*       data;
    QWidgetVideoSink<FMT> *self = G_TYPE_CHECK_INSTANCE_CAST(sink, QWidgetVideoSinkClass<FMT>::get_type(), QWidgetVideoSink<FMT>);

    data = gst_caps_get_structure(caps, 0);

    gst_structure_get_int(data, "width", &self->width);
    gst_structure_get_int(data, "height", &self->height);
    gst_structure_get_int(data, "bpp", &self->bpp);
    gst_structure_get_int(data, "depth", &self->depth);
    return TRUE;
}

template <VideoFormat FMT>
GstStateChangeReturn QWidgetVideoSink<FMT>::change_state(GstElement* element, GstStateChange transition)
{
    return GST_ELEMENT_CLASS(parentClass)->change_state(element, transition);
}

template <VideoFormat FMT>
GstFlowReturn QWidgetVideoSink<FMT>::render(GstBaseSink* sink, GstBuffer* buf)
{
    GstFlowReturn rc = GST_FLOW_OK;

    if (buf != 0)
    {
        QWidgetVideoSink<FMT> *self = G_TYPE_CHECK_INSTANCE_CAST(sink, QWidgetVideoSinkClass<FMT>::get_type(), QWidgetVideoSink<FMT>);
        QByteArray frame;
        frame.resize(buf->size);
        memcpy(frame.data(), buf->data, buf->size);
        NewFrameEvent *frameEvent = new NewFrameEvent(frame, self->width, self->height);
        QApplication::postEvent(self->renderWidget, frameEvent);
    }
    else
        rc = GST_FLOW_ERROR;
    return rc;
}

static GstStaticPadTemplate template_factory_yuv =
    GST_STATIC_PAD_TEMPLATE("sink",
                            GST_PAD_SINK,
                            GST_PAD_ALWAYS,
                            GST_STATIC_CAPS("video/x-raw-yuv, "
                                            "framerate = (fraction) [ 0, MAX ], "
                                            "width = (int) [ 1, MAX ], "
                                            "height = (int) [ 1, MAX ],"
                                            "bpp = (int) 32"));

static GstStaticPadTemplate template_factory_rgb =
    GST_STATIC_PAD_TEMPLATE("sink",
                            GST_PAD_SINK,
                            GST_PAD_ALWAYS,
                            GST_STATIC_CAPS(GST_VIDEO_CAPS_xRGB_HOST_ENDIAN));

template <VideoFormat FMT>
struct template_factory;


template <>
struct template_factory<VideoFormat_YUV>
{
    static GstStaticPadTemplate *getFactory()
    {
        return &template_factory_yuv;
    }
};

template <>
struct template_factory<VideoFormat_RGB>
{
    static GstStaticPadTemplate *getFactory()
    {
        return &template_factory_rgb;
    }
};

template <VideoFormat FMT>
void QWidgetVideoSink<FMT>::base_init(gpointer g_class)
{
    gst_element_class_add_pad_template(GST_ELEMENT_CLASS(g_class),
                                       gst_static_pad_template_get(template_factory<FMT>::getFactory()));
}

template <VideoFormat FMT>
void QWidgetVideoSink<FMT>::instance_init(GTypeInstance *instance, gpointer g_class)
{
    Q_UNUSED(g_class);

    QWidgetVideoSink<FMT>* self = reinterpret_cast<QWidgetVideoSink<FMT>*>(instance);

    self->renderWidget = 0;
    self->width = 0;
    self->height = 0;
    self->bpp = 0;
    self->depth = 0;
}

// QWidgetVideoSinkClass
template <VideoFormat FMT>
void QWidgetVideoSinkClass<FMT>::class_init(gpointer g_class, gpointer class_data)
{
    Q_UNUSED(class_data);
    GstBaseSinkClass*   gstBaseSinkClass = (GstBaseSinkClass*)g_class;
    GstElementClass*    gstElementClass = (GstElementClass*)g_class;

    parentClass = reinterpret_cast<GstVideoSinkClass*>(g_type_class_peek_parent(g_class));

    // base
    gstBaseSinkClass->set_caps = QWidgetVideoSink<FMT>::set_caps;
    gstBaseSinkClass->preroll = QWidgetVideoSink<FMT>::render;
    gstBaseSinkClass->render = QWidgetVideoSink<FMT>::render;

    // element
    gstElementClass->change_state = QWidgetVideoSink<FMT>::change_state;
}

template <VideoFormat FMT>
GType QWidgetVideoSinkClass<FMT>::get_type()
{
    static GType type = 0;

    if (type == 0)
    {
        static const GTypeInfo info =
        {
            sizeof(QWidgetVideoSinkClass<FMT>),                 // class_size
            QWidgetVideoSink<FMT>::base_init,                   // base init
            NULL,                                               // base_finalize

            QWidgetVideoSinkClass<FMT>::class_init,             // class_init
            NULL,                                               // class_finalize
            NULL,                                               // class_data

            sizeof(QWidgetVideoSink<FMT>),                      // instance_size
            0,                                                  // n_preallocs
            QWidgetVideoSink<FMT>::instance_init,               // instance_init
            0                                                   // value_table
        };

        type = g_type_register_static(GST_TYPE_VIDEO_SINK,
                                     QWidgetVideoSinkClass<FMT>::get_name(),
                                      &info,
                                      GTypeFlags(0));
    }
    return type;
}

GType get_type_YUV()
{
    return QWidgetVideoSinkClass<VideoFormat_YUV>::get_type();
}

GType get_type_RGB()
{
    return QWidgetVideoSinkClass<VideoFormat_RGB>::get_type();
}

}
} //namespace Phonon::Gstreamer

QT_END_NAMESPACE
