/*  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 "ancestormovemonitor.h"
#include "utils.h"

#include "videooutput_dsa.h"

#include <QCoreApplication>

QT_BEGIN_NAMESPACE

using namespace Phonon::MMF;

/*! \class Phonon::MMF::AncestorMoveMonitor
  \internal
  \brief Class which installs a global event filter, and listens for move
  events which may affect the absolute position of widgets registered with
  the monitor
  See QTBUG-4956
*/


/*! \class Phonon::MMF::VideoOutputObserver
    \internal
*/

//-----------------------------------------------------------------------------
// Constructor / destructor
//-----------------------------------------------------------------------------

AncestorMoveMonitor::AncestorMoveMonitor(QObject *parent)
    :   QObject(parent)
{
    QCoreApplication::instance()->installEventFilter(this);
}

AncestorMoveMonitor::~AncestorMoveMonitor()
{
    QCoreApplication::instance()->removeEventFilter(this);
}


//-----------------------------------------------------------------------------
// Public functions
//-----------------------------------------------------------------------------

void AncestorMoveMonitor::registerTarget(DsaVideoOutput *target)
{
    TRACE_CONTEXT(AncestorMoveMonitor::registerTarget, EVideoInternal);
    TRACE_ENTRY("target 0x%08x", target);

    // First un-register the target, in case this is being called as a result
    // of re-parenting.  This is not the most efficient way to update the
    // target hash, but since this is not likely to be a frequent operation,
    // simplicity is preferred over outright speed.  In any case, re-parenting
    // of the video widget leads to re-creation of native windows, which is
    // likely to take far more processing than any implementation of this
    // function.
    unRegisterTarget(target);

    QWidget *ancestor = target->parentWidget();
    while(ancestor) {
        const Hash::iterator it = m_hash.find(ancestor);
        if(m_hash.end() == it) {
            TargetList targetList;
            targetList.append(target);
            m_hash.insert(ancestor, targetList);
        } else {
            TargetList& targetList = it.value();
            Q_ASSERT(targetList.indexOf(target) == -1);
            targetList.append(target);
        }
        ancestor = ancestor->parentWidget();
    }

    dump();

    TRACE_EXIT_0();
}

void AncestorMoveMonitor::unRegisterTarget(DsaVideoOutput *target)
{
    TRACE_CONTEXT(AncestorMoveMonitor::unRegisterTarget, EVideoInternal);
    TRACE_ENTRY("target 0x%08x", target);

    Hash::iterator it = m_hash.begin();
    while(it != m_hash.end()) {
        TargetList& targetList = it.value();
        const int index = targetList.indexOf(target);
        if(index != -1)
            targetList.removeAt(index);
        if(targetList.count())
            ++it;
        else
            it = m_hash.erase(it);
    }

    dump();

    TRACE_EXIT_0();
}

bool AncestorMoveMonitor::eventFilter(QObject *watched, QEvent *event)
{
    TRACE_CONTEXT(AncestorMoveMonitor::eventFilter, EVideoInternal);

    if(event->type() == QEvent::Move || event->type() == QEvent::ParentChange) {

        //TRACE_ENTRY("watched 0x%08x event.type %d", watched, event->type());

        const Hash::const_iterator it = m_hash.find(watched);
        if(it != m_hash.end()) {
            const TargetList& targetList = it.value();
            DsaVideoOutput* target = 0;
            foreach(target, targetList) {
                switch (event->type()) {

                case QEvent::Move:
                    // Notify the target that its ancestor has moved
                    target->ancestorMoved();
                    break;

                case QEvent::ParentChange:
                    // Update ancestor list for the target
                    registerTarget(target);
                    break;

                default:
                    Q_ASSERT(false);
                }
            }
        }

        //TRACE_EXIT_0();
    }

    // The event is never consumed by this filter
    return false;
}

//-----------------------------------------------------------------------------
// Private functions
//-----------------------------------------------------------------------------

void AncestorMoveMonitor::dump()
{
#ifndef QT_NO_DEBUG
    TRACE_CONTEXT(AncestorMoveMonitor::dump, EVideoInternal);
    for(Hash::const_iterator it = m_hash.begin();
        it != m_hash.end(); ++it) {
        const QObject *ancestor = it.key();
        TRACE("ancestor 0x%08x", ancestor);
        const TargetList& targetList = it.value();
        DsaVideoOutput* target = 0;
        foreach(target, targetList) {
            TRACE("    target 0x%08x", target);
        }
    }
#endif
}

QT_END_NAMESPACE

