#ifndef oxygenmenubardata_imp_h
#define oxygenmenubardata_imp_h

//////////////////////////////////////////////////////////////////////////////
// oxygenmenubardata_imp.h
// implements menubar data templatized methods
// -------------------
//
// SPDX-FileCopyrightText: 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// SPDX-License-Identifier: MIT
//////////////////////////////////////////////////////////////////////////////

namespace Oxygen
{

    //________________________________________________________________________
    template< typename T > void MenuBarDataV1::enterEvent( const QObject* object )
    {

        const T* local = qobject_cast<const T*>( object );
        if( !local ) return;

        // if the current action is still active, one does nothing
        if( local->activeAction() == currentAction().data() ) return;

        if( currentAnimation().data()->isRunning() ) currentAnimation().data()->stop();
        clearCurrentAction();
        clearCurrentRect();

    }

    //________________________________________________________________________
    template< typename T > void MenuBarDataV1::leaveEvent( const QObject* object )
    {

        const T* local = qobject_cast<const T*>( object );
        if( !local ) return;

        // if the current action is still active, one does nothing
        if( local->activeAction() == currentAction().data() ) return;

        if( currentAnimation().data()->isRunning() ) currentAnimation().data()->stop();
        if( previousAnimation().data()->isRunning() ) previousAnimation().data()->stop();
        if( currentAction() )
        {
            setPreviousRect( currentRect() );
            clearCurrentAction();
            clearCurrentRect();
            previousAnimation().data()->start();
        }

        // trigger update
        setDirty();

    }

    //________________________________________________________________________
    template< typename T > void MenuBarDataV1::mouseMoveEvent( const QObject* object )
    {

        const T* local = qobject_cast<const T*>( object );
        if( !local ) return;

        // check action
        if( local->activeAction() == currentAction().data() ) return;

        bool hasCurrentAction( currentAction() );

        // check current action
        if( currentAction() )
        {
            if( currentAnimation().data()->isRunning() ) currentAnimation().data()->stop();
            if( previousAnimation().data()->isRunning() ) {
                previousAnimation().data()->setCurrentTime(0);
                previousAnimation().data()->stop();
            }

            // only start fadeout effect if there is no new selected action
            //if( !activeActionValid )
            if( !local->activeAction() )
            {
                setPreviousRect( currentRect() );
                previousAnimation().data()->start();
            }

            clearCurrentAction();
            clearCurrentRect();

        }

        // check if local current actions is valid
        bool activeActionValid( local->activeAction() && local->activeAction()->isEnabled() && !local->activeAction()->isSeparator() );
        if( activeActionValid )
        {
            if( currentAnimation().data()->isRunning() ) currentAnimation().data()->stop();

            setCurrentAction( local->activeAction() );
            setCurrentRect( local->actionGeometry( currentAction().data() ) );
            if( !hasCurrentAction )
            { currentAnimation().data()->start(); }

        }

    }

    //________________________________________________________________________
    template< typename T > void MenuBarDataV1::mousePressEvent( const QObject* object )
    {

        const T* local = qobject_cast<const T*>( object );
        if( !local ) return;

        // check action
        if( local->activeAction() == currentAction().data() ) return;

        // check current action
        bool activeActionValid( local->activeAction() && local->activeAction()->isEnabled() && !local->activeAction()->isSeparator() );
        if( currentAction() && !activeActionValid )
        {

            if( currentAnimation().data()->isRunning() ) currentAnimation().data()->stop();
            if( previousAnimation().data()->isRunning() ) previousAnimation().data()->stop();

            setPreviousRect( currentRect() );
            previousAnimation().data()->start();

            clearCurrentAction();
            clearCurrentRect();

        }

    }

    //________________________________________________________________________
    template< typename T > void MenuBarDataV2::enterEvent( const QObject* object )
    {

        // cast widget
        const T* local = qobject_cast<const T*>( object );
        if( !local ) return;

        if( _timer.isActive() ) _timer.stop();

        // if the current action is still active, one does nothing
        if( currentAction() && local->activeAction() == currentAction().data() ) return;

        if( animation().data()->isRunning() ) animation().data()->stop();
        if( progressAnimation().data()->isRunning() ) progressAnimation().data()->stop();
        clearPreviousRect();
        clearAnimatedRect();

        if( local->activeAction() &&  local->activeAction()->isEnabled() && !local->activeAction()->isSeparator() )
        {
            setCurrentAction( local->activeAction() );
            setCurrentRect( local->actionGeometry( currentAction().data() ) );
            animation().data()->setDirection( Animation::Forward );
            animation().data()->start();

        } else {

            clearCurrentAction();
            clearCurrentRect();

        }

        return;
    }

    //________________________________________________________________________
    template< typename T > void MenuBarDataV2::leaveEvent( const QObject* object )
    {

        const T* local = qobject_cast<const T*>( object );
        if( !local ) return;

        // if the current action is still active, one does nothing
        if( local->activeAction() && local->activeAction() == currentAction().data() )
        { return; }

        if( progressAnimation().data()->isRunning() ) progressAnimation().data()->stop();
        if( animation().data()->isRunning() ) animation().data()->stop();
        clearAnimatedRect();
        clearPreviousRect();
        if( currentAction() )
        {
            clearCurrentAction();
            animation().data()->setDirection( Animation::Backward );
            animation().data()->start();
        }

        // trigger update
        setDirty();

        return;

    }

    //________________________________________________________________________
    template< typename T > void MenuBarDataV2::mouseMoveEvent( const QObject* object )
    {
        const T* local = qobject_cast<const T*>( object );
        if( !local ) return;
        if( local->activeAction() == currentAction().data() ) return;

        // check if current position match another action
        if( local->activeAction() && local->activeAction()->isEnabled() && !local->activeAction()->isSeparator())
        {

            if( _timer.isActive() ) _timer.stop();

            QAction* activeAction( local->activeAction() );

            // update previous rect if the current action is valid
            QRect activeRect( local->actionGeometry( activeAction ) );
            if( currentAction() )
            {
                if( !progressAnimation().data()->isRunning() )
                {

                    setPreviousRect( currentRect() );

                } else if( progress() < 1 && currentRect().isValid() && previousRect().isValid() ) {

                    // re-calculate previous rect so that animatedRect
                    // is unchanged after currentRect is updated
                    // this prevents from having jumps in the animation
                    qreal ratio = progress()/(1.0-progress());
                    _previousRect.adjust(
                        ratio*( currentRect().left() - activeRect.left() ),
                        ratio*( currentRect().top() - activeRect.top() ),
                        ratio*( currentRect().right() - activeRect.right() ),
                        ratio*( currentRect().bottom() - activeRect.bottom() ) );

                }

                // update current action
                setCurrentAction( activeAction );
                setCurrentRect( activeRect );
                if( animation().data()->isRunning() ) animation().data()->stop();
                if( !progressAnimation().data()->isRunning() ) progressAnimation().data()->start();

            } else {

                // update current action
                setCurrentAction( activeAction );
                setCurrentRect( activeRect );
                if( !_entered )
                {

                    _entered = true;
                    if( animation().data()->isRunning() ) animation().data()->stop();
                    if( !progressAnimation().data()->isRunning() ) progressAnimation().data()->start();

                } else {

                    setPreviousRect( activeRect );
                    clearAnimatedRect();
                    if( progressAnimation().data()->isRunning() ) progressAnimation().data()->stop();
                    animation().data()->setDirection( Animation::Forward );
                    if( !animation().data()->isRunning() ) animation().data()->start();
                }

            }

        } else if( currentAction() ) {

            _timer.start( 150, this );

        }

        return;

    }

}

#endif
