/*  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 "videowidget.h"

#include <QtGui/QPainter>
#include <QtGui/QPaintEvent>
#include <QtCore/QTimer>
#include <QtCore/QSettings>

#include "mediaobject.h"

#ifndef Q_OS_WINCE
#include "videorenderer_evr.h"
#include "videorenderer_vmr9.h"
#else
#include "videorenderer_default.h"
#endif
#include "videorenderer_soft.h"

QT_BEGIN_NAMESPACE

#ifndef QT_NO_PHONON_VIDEO

namespace Phonon
{
    namespace DS9
    {
        //class used internally to return the widget where the video is shown on
        class VideoWindow : public QWidget
        {
        public:
            explicit VideoWindow(QWidget *parent, VideoWidget *vw)
                : QWidget(parent), m_node(vw), m_currentRenderer(0)
            {
                //default background color
                setPalette(QPalette(Qt::black));                 
                setAttribute(Qt::WA_OpaquePaintEvent, true);
                setAttribute(Qt::WA_NoSystemBackground, true);
                setAttribute(Qt::WA_PaintOnScreen, true);
                setAutoFillBackground(false);
            }

            QPaintEngine* paintEngine() const
            {
                return 0;
            }

            bool isEmbedded() const
            {
#if QT_VERSION >= 0x040400
                return window()->testAttribute(Qt::WA_DontShowOnScreen);
#else
                return false;
#endif
            }

            bool needsSoftRendering() const
            {
                QPaintDevice *dev = QPainter::redirected(this, 0);
                return (dev && dev != this);
            }

            void resizeEvent(QResizeEvent *e)
            {
                m_node->updateVideoSize();
                QWidget::resizeEvent(e);
            }

            AbstractVideoRenderer *currentRenderer() const
            {
                return m_currentRenderer;
            }

            void setCurrentRenderer(AbstractVideoRenderer *renderer)
            {
                m_currentRenderer = renderer;
                //we disallow repaint on that widget for just a fraction of second
                //this allows better transition between videos
                setUpdatesEnabled(false);
                m_flickerFreeTimer.start(20, this);
            }

            void timerEvent(QTimerEvent *e)
            {
                if (e->timerId() == m_flickerFreeTimer.timerId()) {
                    m_flickerFreeTimer.stop();
                    setUpdatesEnabled(true);
                }
                QWidget::timerEvent(e);
            }

            QSize sizeHint() const
            {
                return m_currentRenderer->sizeHint().expandedTo(QWidget::sizeHint());
            }

            void changeEvent(QEvent *e)
            {
                checkCurrentRenderingMode();
                QWidget::changeEvent(e);
            }

            void setVisible(bool visible)
            {
                checkCurrentRenderingMode();
                QWidget::setVisible(visible);
            }

            void paintEvent(QPaintEvent *e)
            {
                if (!updatesEnabled())
                    return; //this avoids repaint from native events
                checkCurrentRenderingMode();
                m_currentRenderer->repaintCurrentFrame(this, e->rect());
            }

            //this code manages the activation/deactivation of the screensaver
            /*bool event(QEvent *e)
            {
                if (e->type() == QEvent::Resize) {
                    //we disable the screensaver if the video is in fullscreen mode
                    disableScreenSaver(window()->windowState() & Qt::WindowFullScreen);
                }
                return QWidget::event(e);
            }*/

        private:
            //for fullscreen mode
            void disableScreenSaver(bool b)
            {
                const QLatin1String screenSaverActive("ScreenSaveActive");
                QSettings settings( QLatin1String("HKEY_CURRENT_USER\\Control Panel\\Desktop"), QSettings::NativeFormat);
                if (b) {
                    if (m_restoreScreenSaverActive.isNull()) {
                        //we store the value to be able to restore it later
                        m_restoreScreenSaverActive = settings.value(screenSaverActive);
                        settings.setValue(screenSaverActive, QString::number(!b));
                    }
                } else if (!m_restoreScreenSaverActive.isNull()) {
                    //we restore the previous value
                    settings.setValue(screenSaverActive, m_restoreScreenSaverActive);
                }
            }

            void checkCurrentRenderingMode()
            {
                if (!m_currentRenderer)
                    return;

                if (m_currentRenderer->isNative()) {
                    if (isEmbedded()) {
                        //we need to switch to software renderer
                        m_currentRenderer = m_node->switchRendering(m_currentRenderer);
                        setAttribute(Qt::WA_PaintOnScreen, false);
                    } else if (needsSoftRendering()) {
                        m_node->performSoftRendering(m_currentRenderer->snapshot());
                    }
                } else if (!isEmbedded()) {
                    m_currentRenderer = m_node->switchRendering(m_currentRenderer);
                    setAttribute(Qt::WA_PaintOnScreen, false);
                }
            }

            VideoWidget *m_node;
            AbstractVideoRenderer *m_currentRenderer;
            QVariant m_restoreScreenSaverActive;
            QBasicTimer m_flickerFreeTimer;
        };

        VideoWidget::VideoWidget(QWidget *parent)
            : BackendNode(parent), m_aspectRatio(Phonon::VideoWidget::AspectRatioAuto),
              m_scaleMode(Phonon::VideoWidget::FitInView),
              m_brightness(0.), m_contrast(0.), m_hue(0.), m_saturation(0.), m_noNativeRendererSupported(false)
              
        {
            //initialisation of the widget
            m_widget = new VideoWindow(parent, this);

            //initialization of the renderers
            qMemSet(m_renderers, 0, sizeof(m_renderers));

            for(int i = 0; i< FILTER_COUNT ;++i) {
                //This might return a non native (ie Qt) renderer in case native is not supported
                AbstractVideoRenderer *renderer = getRenderer(i, Native, true);
                m_filters[i] = renderer->getFilter();
            }

            //by default, we take the first VideoWindow object
            setCurrentGraph(0);
        }

        VideoWidget::~VideoWidget()
        {
            for (int i = 0; i < 4; ++i) {
                delete m_renderers[i];
            }
        }

        void VideoWidget::notifyVideoLoaded()
        {
            updateVideoSize();
            m_widget->updateGeometry();
        }

        AbstractVideoRenderer *VideoWidget::switchRendering(AbstractVideoRenderer *current)
        {
            const bool toNative = !current->isNative();
            if (toNative && m_noNativeRendererSupported)
                return current; //no switch here

            if (!mediaObject())
                return current;

            //firt we delete the renderer
            //initialization of the widgets
            for(int i = 0; i < FILTER_COUNT; ++i) {
                Filter oldFilter = m_filters[i];

                //Let's create a software renderer
                AbstractVideoRenderer *renderer = getRenderer(i, toNative ? Native : NonNative, true);

                if (m_mediaObject) {
                    m_mediaObject->switchFilters(i, oldFilter, renderer->getFilter());
                }

                m_filters[i] = renderer->getFilter();
            }

            return getRenderer(mediaObject()->currentGraph()->index(), toNative ? Native: NonNative);
        }

        void VideoWidget::performSoftRendering(const QImage &currentImage)
        {
            const int graphIndex = mediaObject()->currentGraph()->index();
            VideoRendererSoft *r = static_cast<VideoRendererSoft*>(getRenderer(graphIndex, NonNative, true /*autocreation*/));
            r->setSnapshot(currentImage);
            r->notifyResize(m_widget->size(), m_aspectRatio, m_scaleMode);
            r->repaintCurrentFrame(m_widget, m_widget->rect());

        }

        void VideoWidget::setCurrentGraph(int index)
        {
            for(int i = 0; i < 2; ++i) {
                if (AbstractVideoRenderer *renderer = getRenderer(i, Native))
                    renderer->setActive(index == i);
            }

            //be sure to update all the things that needs an update
            applyMixerSettings();
            updateVideoSize();

            AbstractVideoRenderer *r = m_widget->currentRenderer();

            //we determine dynamically if it is native or non native
            r = getRenderer(index, !r || r->isNative() ? Native : NonNative);
			if (!r)
				r = getRenderer(index, NonNative);
            m_widget->setCurrentRenderer(r);
        }


        Phonon::VideoWidget::AspectRatio VideoWidget::aspectRatio() const
        {
            return m_aspectRatio;
        }

        void VideoWidget::setAspectRatio(Phonon::VideoWidget::AspectRatio aspectRatio)
        {
            m_aspectRatio = aspectRatio;
            updateVideoSize();
            m_widget->update();
        }

        Phonon::VideoWidget::ScaleMode VideoWidget::scaleMode() const
        {
            return m_scaleMode;
        }


        QWidget *VideoWidget::widget()
        {
            return m_widget;
        }


        void VideoWidget::setScaleMode(Phonon::VideoWidget::ScaleMode scaleMode)
        {
            m_scaleMode = scaleMode;
            updateVideoSize();
            m_widget->update();
        }

        void VideoWidget::setBrightness(qreal b)
        {
            m_brightness = b;
            applyMixerSettings();
        }

        void VideoWidget::setContrast(qreal c)
        {
            m_contrast = c;
            applyMixerSettings();
        }

        void VideoWidget::setHue(qreal h)
        {
            m_hue = h;
            applyMixerSettings();
        }

        void VideoWidget::setSaturation(qreal s)
        {
            m_saturation = s;
            applyMixerSettings();
        }

        qreal VideoWidget::brightness() const
        {
            return m_brightness;
        }


        qreal VideoWidget::contrast() const
        {
            return m_contrast;
        }

        qreal VideoWidget::hue() const
        {
            return m_hue;
        }

        qreal VideoWidget::saturation() const
        {
            return m_saturation;
        }


        AbstractVideoRenderer *VideoWidget::getRenderer(int graphIndex, RendererType type, bool autoCreate)
        {
            int index = graphIndex * 2 + type;
            if (m_renderers[index] == 0 && autoCreate) {
                AbstractVideoRenderer *renderer = 0;
                if (type == Native) {
#ifndef Q_OS_WINCE
                    renderer = new VideoRendererEVR(m_widget);
                    if (renderer->getFilter() == 0) {
                        delete renderer;
                        //EVR not present, let's try VMR
                        renderer = new VideoRendererVMR9(m_widget);
                        if (renderer->getFilter() == 0) {
                            //instanciating the renderer might fail
                            m_noNativeRendererSupported = true;
                            delete renderer;
                            renderer = 0;
                        }
                    }
#else
                    renderer = new VideoRendererDefault(m_widget);
                    if (renderer->getFilter() == 0) {
                        //instanciating the renderer might fail
                        m_noNativeRendererSupported = true;
                        delete renderer;
                        renderer = 0;
                    }
#endif
                }

                if (renderer == 0) {
                    type = NonNative;
                    index = graphIndex * 2 + type;
                    if (m_renderers[index] == 0)
                        renderer = new VideoRendererSoft(m_widget); //this always succeeds
                    else
                        renderer = m_renderers[index];
                }

                m_renderers[index] = renderer;

                //be sure to update all the things that needs an update
                applyMixerSettings();
                updateVideoSize();

            }
            return m_renderers[index];
        }

        //this must be called whe nthe node is actually connected
        void  VideoWidget::applyMixerSettings() const
        {
            for (int i = 0; i < 4; ++i) {
                if (AbstractVideoRenderer *renderer = m_renderers[i])
                    renderer->applyMixerSettings(m_brightness, m_contrast, m_hue, m_saturation);
            }
        }

        void VideoWidget::connected(BackendNode *, const InputPin&)
        {
            //in case of a connection, we simply reapply the mixer settings
            applyMixerSettings();
            updateVideoSize();
        }

        void VideoWidget::updateVideoSize() const
        {
            for (int i = 0; i < 4; ++i) {
                if (AbstractVideoRenderer *renderer = m_renderers[i])
                    renderer->notifyResize(m_widget->size(), m_aspectRatio, m_scaleMode);
            }
        }



    }
}

#endif //QT_NO_PHONON_VIDEO

QT_END_NAMESPACE

#include "moc_videowidget.cpp"
