//////////////////////////////////////////////////////////////////////////////
// oxygenanimations.cpp
// container for all animation engines
// -------------------
//
// SPDX-FileCopyrightText: 2006, 2007 Riccardo Iaconelli <riccardo@kde.org>
// SPDX-FileCopyrightText: 2006, 2007 Casper Boemann <cbr@boemann.dk>
// SPDX-FileCopyrightText: 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// SPDX-License-Identifier: MIT
//////////////////////////////////////////////////////////////////////////////

#include "oxygenanimations.h"
#include "oxygenpropertynames.h"
#include "oxygenstyleconfigdata.h"

#include <QAbstractItemView>
#include <QComboBox>
#include <QDial>
#include <QHeaderView>
#include <QLineEdit>
#include <QMainWindow>
#include <QMdiSubWindow>
#include <QProgressBar>
#include <QScrollBar>
#include <QSpinBox>
#include <QSplitterHandle>
#include <QTextEdit>
#include <QToolBar>
#include <QToolBox>
#include <QToolButton>
#include <QGroupBox>

namespace Oxygen
{

    //____________________________________________________________
    Animations::Animations( QObject* parent ):
        QObject( parent )
    {

        _widgetEnableStateEngine = new WidgetStateEngine( this );
        _spinBoxEngine = new SpinBoxEngine( this );
        _comboBoxEngine = new WidgetStateEngine( this );
        _toolButtonEngine = new WidgetStateEngine( this );
        _toolBoxEngine = new ToolBoxEngine( this );
        _busyIndicatorEngine = new BusyIndicatorEngine( this );

        registerEngine( _dockSeparatorEngine = new DockSeparatorEngine( this ) );
        registerEngine( _headerViewEngine = new HeaderViewEngine( this ) );
        registerEngine( _widgetStateEngine = new WidgetStateEngine( this ) );
        registerEngine( _progressBarEngine = new ProgressBarEngine( this ) );
        registerEngine( _menuBarEngine = new MenuBarEngineV1( this ) );
        registerEngine( _menuEngine = new MenuEngineV1( this ) );
        registerEngine( _scrollBarEngine = new ScrollBarEngine( this ) );
        registerEngine( _inputWidgetEngine = new WidgetStateEngine( this ) );
        registerEngine( _splitterEngine = new SplitterEngine( this ) );
        registerEngine( _tabBarEngine = new TabBarEngine( this ) );
        registerEngine( _toolBarEngine = new ToolBarEngine( this ) );
        registerEngine( _mdiWindowEngine = new MdiWindowEngine( this ) );
    }

    //____________________________________________________________
    void Animations::setupEngines( void )
    {

        // animation steps
        AnimationData::setSteps( StyleConfigData::animationSteps() );

        {
            // default enability, duration and maxFrame
            bool animationsEnabled( StyleConfigData::animationsEnabled() );

            // enability
            _widgetEnableStateEngine->setEnabled( animationsEnabled &&  StyleConfigData::genericAnimationsEnabled() );
            _widgetStateEngine->setEnabled( animationsEnabled &&  StyleConfigData::genericAnimationsEnabled() );
            _inputWidgetEngine->setEnabled( animationsEnabled &&  StyleConfigData::genericAnimationsEnabled() );
            _comboBoxEngine->setEnabled( animationsEnabled &&  StyleConfigData::genericAnimationsEnabled() );
            _toolButtonEngine->setEnabled( animationsEnabled &&  StyleConfigData::genericAnimationsEnabled() );
            _toolBoxEngine->setEnabled( animationsEnabled &&  StyleConfigData::genericAnimationsEnabled() );
            _splitterEngine->setEnabled( animationsEnabled &&  StyleConfigData::genericAnimationsEnabled() );
            _scrollBarEngine->setEnabled( animationsEnabled &&  StyleConfigData::genericAnimationsEnabled() );
            _spinBoxEngine->setEnabled( animationsEnabled &&  StyleConfigData::genericAnimationsEnabled() );
            _tabBarEngine->setEnabled( animationsEnabled &&  StyleConfigData::genericAnimationsEnabled() );
            _dockSeparatorEngine->setEnabled( animationsEnabled &&  StyleConfigData::genericAnimationsEnabled() );
            _headerViewEngine->setEnabled( animationsEnabled &&  StyleConfigData::genericAnimationsEnabled() );
            _mdiWindowEngine->setEnabled( animationsEnabled &&  StyleConfigData::genericAnimationsEnabled() );
            _progressBarEngine->setEnabled( animationsEnabled &&  StyleConfigData::progressBarAnimationsEnabled() );

            // busy indicator
            _busyIndicatorEngine->setEnabled( StyleConfigData::progressBarAnimated() );

            // menubar engine
            int menuBarAnimationType( StyleConfigData::menuBarAnimationType() );
            if( menuBarAnimationType == StyleConfigData::MB_FADE && !qobject_cast<MenuBarEngineV1*>( _menuBarEngine ) )
            {
                if( _menuBarEngine )
                {

                    MenuBarEngineV1* newEngine = new MenuBarEngineV1( this, _menuBarEngine );
                    registerEngine( newEngine );
                    _menuBarEngine->deleteLater();
                    _menuBarEngine = newEngine;

                } else registerEngine( _menuBarEngine = new MenuBarEngineV1( this ) );

            } else if( menuBarAnimationType == StyleConfigData::MB_FOLLOW_MOUSE && !qobject_cast<MenuBarEngineV2*>( _menuBarEngine ) ) {

                if( _menuBarEngine )
                {

                    MenuBarEngineV2* newEngine = new MenuBarEngineV2( this, _menuBarEngine );
                    registerEngine( newEngine );
                    _menuBarEngine->deleteLater();
                    _menuBarEngine = newEngine;

                } else registerEngine( _menuBarEngine = new MenuBarEngineV1( this ) );

            }

            // menu engine
            int menuAnimationType( StyleConfigData::menuAnimationType() );
            if( menuAnimationType == StyleConfigData::ME_FADE && !qobject_cast<MenuEngineV1*>( _menuEngine ) )
            {

                if( _menuEngine )
                {

                    MenuEngineV1* newEngine = new MenuEngineV1( this, _menuEngine );
                    registerEngine( newEngine );
                    _menuEngine->deleteLater();
                    _menuEngine = newEngine;

                } else registerEngine( _menuEngine = new MenuEngineV1( this ) );

            } else if( menuAnimationType == StyleConfigData::ME_FOLLOW_MOUSE && !qobject_cast<MenuEngineV2*>( _menuEngine ) ) {

                if( _menuEngine )
                {

                    MenuEngineV2* newEngine = new MenuEngineV2( this, _menuEngine );
                    registerEngine( newEngine );
                    _menuEngine->deleteLater();
                    _menuEngine = newEngine;

                } else registerEngine( _menuEngine = new MenuEngineV1( this ) );

            }

            _menuBarEngine->setEnabled( animationsEnabled && menuBarAnimationType != StyleConfigData::MB_NONE );
            _menuEngine->setEnabled( animationsEnabled && menuAnimationType != StyleConfigData::ME_NONE );

            // toolbar engine
            int toolBarAnimationType( StyleConfigData::toolBarAnimationType() );
            if( toolBarAnimationType == StyleConfigData::TB_NONE || toolBarAnimationType == StyleConfigData::TB_FOLLOW_MOUSE )
            {

                // disable toolbar engine
                _toolBarEngine->setEnabled( animationsEnabled && toolBarAnimationType == StyleConfigData::TB_FOLLOW_MOUSE );

                // unregister all toolbuttons that belong to a toolbar
                const auto widgets = _widgetStateEngine->registeredWidgets( AnimationHover|AnimationFocus );
                for ( QWidget *widget : widgets)
                {
                    if( qobject_cast<QToolButton*>( widget ) && qobject_cast<QToolBar*>( widget->parentWidget() ) )
                    { _widgetStateEngine->unregisterWidget( widget ); }
                }

            } else if( toolBarAnimationType == StyleConfigData::TB_FADE ) {

                // disable toolbar engine
                _toolBarEngine->setEnabled( false );

                // retrieve all registered toolbars
                const BaseEngine::WidgetList widgets( _toolBarEngine->registeredWidgets() );
                for ( QWidget *widget : widgets )
                {
                    // get all toolbuttons
                    const auto children = widget->children();
                    for ( QObject *child : children )
                    {
                        if( QToolButton* toolButton = qobject_cast<QToolButton*>( child ) )
                        { _widgetStateEngine->registerWidget( toolButton, AnimationHover ); }
                    }
                }

            }

        }


        {

            // durations
            _widgetEnableStateEngine->setDuration( StyleConfigData::genericAnimationsDuration() );
            _widgetStateEngine->setDuration( StyleConfigData::genericAnimationsDuration() );
            _inputWidgetEngine->setDuration( StyleConfigData::genericAnimationsDuration() );
            _comboBoxEngine->setDuration( StyleConfigData::genericAnimationsDuration() );
            _toolButtonEngine->setDuration( StyleConfigData::genericAnimationsDuration() );
            _toolBoxEngine->setDuration( StyleConfigData::genericAnimationsDuration() );
            _splitterEngine->setDuration( StyleConfigData::genericAnimationsDuration() );
            _scrollBarEngine->setDuration( StyleConfigData::genericAnimationsDuration() );
            _spinBoxEngine->setDuration( StyleConfigData::genericAnimationsDuration() );
            _tabBarEngine->setDuration( StyleConfigData::genericAnimationsDuration() );
            _dockSeparatorEngine->setDuration( StyleConfigData::genericAnimationsDuration() );
            _headerViewEngine->setDuration( StyleConfigData::genericAnimationsDuration() );
            _mdiWindowEngine->setDuration( StyleConfigData::genericAnimationsDuration() );

            _progressBarEngine->setDuration( StyleConfigData::progressBarAnimationsDuration() );
            _busyIndicatorEngine->setDuration( StyleConfigData::progressBarBusyStepDuration() );

            _toolBarEngine->setDuration( StyleConfigData::genericAnimationsDuration() );
            _toolBarEngine->setFollowMouseDuration( StyleConfigData::toolBarAnimationsDuration() );

            _menuBarEngine->setDuration( StyleConfigData::menuBarAnimationsDuration() );
            _menuBarEngine->setFollowMouseDuration( StyleConfigData::menuBarFollowMouseAnimationsDuration() );

            _menuEngine->setDuration( StyleConfigData::menuAnimationsDuration() );
            _menuEngine->setFollowMouseDuration( StyleConfigData::menuFollowMouseAnimationsDuration() );

        }

    }

    //____________________________________________________________
    void Animations::registerWidget( QWidget* widget ) const
    {

        if( !widget ) return;

        // check against noAnimations propery
        QVariant propertyValue( widget->property( PropertyNames::noAnimations ) );
        if( propertyValue.isValid() && propertyValue.toBool() ) return;

        // these are needed to not register animations for kwin widgets
        if( widget->objectName() == QStringLiteral( "decoration widget" ) ) return;
        if( widget->inherits( "KCommonDecorationButton" ) ) return;
        if( widget->inherits( "QShapedPixmapWidget" ) ) return;

        // all widgets are registered to the enability engine.
        _widgetEnableStateEngine->registerWidget( widget, AnimationEnable );

        // install animation timers
        // for optimization, one should put with most used widgets here first
        if( qobject_cast<QToolButton*>(widget) )
        {

            _toolButtonEngine->registerWidget( widget, AnimationHover );
            bool isInToolBar( qobject_cast<QToolBar*>(widget->parent()) );
            if( isInToolBar )
            {

                if( StyleConfigData::toolBarAnimationType() == StyleConfigData::TB_FADE )
                { _widgetStateEngine->registerWidget( widget, AnimationHover ); }

            } else _widgetStateEngine->registerWidget( widget, AnimationHover|AnimationFocus );

        } else if( qobject_cast<QAbstractButton*>(widget) ) {

            if( qobject_cast<QToolBox*>( widget->parent() ) )
            { _toolBoxEngine->registerWidget( widget ); }

            _widgetStateEngine->registerWidget( widget, AnimationHover|AnimationFocus );

        } else if( qobject_cast<QDial*>(widget) ) {

            _widgetStateEngine->registerWidget( widget, AnimationHover|AnimationFocus );

        }

        // groupboxes
        else if( QGroupBox* groupBox = qobject_cast<QGroupBox*>( widget ) )
        {
            if( groupBox->isCheckable() )
            { _widgetStateEngine->registerWidget( widget, AnimationHover|AnimationFocus ); }
        }

        // scrollbar
        else if( qobject_cast<QScrollBar*>( widget ) ) { _scrollBarEngine->registerWidget( widget ); }
        else if( qobject_cast<QSlider*>( widget ) ) { _widgetStateEngine->registerWidget( widget, AnimationHover|AnimationFocus ); }
        else if( qobject_cast<QProgressBar*>( widget ) )
        {
            _progressBarEngine->registerWidget( widget );
            _busyIndicatorEngine->registerWidget( widget );
        }
        else if( qobject_cast<QSplitterHandle*>( widget ) ) { _splitterEngine->registerWidget( widget ); }
        else if( qobject_cast<QMainWindow*>( widget ) ) { _dockSeparatorEngine->registerWidget( widget ); }
        else if( qobject_cast<QHeaderView*>( widget ) ) { _headerViewEngine->registerWidget( widget ); }

        // menu
        else if( qobject_cast<QMenu*>( widget ) ) { _menuEngine->registerWidget( widget ); }
        else if( qobject_cast<QMenuBar*>( widget ) ) { _menuBarEngine->registerWidget( widget ); }
        else if( qobject_cast<QTabBar*>( widget ) ) { _tabBarEngine->registerWidget( widget ); }
        else if( qobject_cast<QToolBar*>( widget ) ) { _toolBarEngine->registerWidget( widget ); }

        // editors
        else if( qobject_cast<QComboBox*>( widget ) ) {
            _comboBoxEngine->registerWidget( widget, AnimationHover );
            _inputWidgetEngine->registerWidget( widget, AnimationHover|AnimationFocus );
        } else if( qobject_cast<QSpinBox*>( widget ) ) {
            _spinBoxEngine->registerWidget( widget );
            _inputWidgetEngine->registerWidget( widget, AnimationHover|AnimationFocus );
        }
        else if( qobject_cast<QLineEdit*>( widget ) ) { _inputWidgetEngine->registerWidget( widget, AnimationHover|AnimationFocus ); }
        else if( qobject_cast<QTextEdit*>( widget ) ) { _inputWidgetEngine->registerWidget( widget, AnimationHover|AnimationFocus ); }
        else if( widget->inherits( "KTextEditor::View" ) ) { _inputWidgetEngine->registerWidget( widget, AnimationHover|AnimationFocus ); }

        // lists
        else if( qobject_cast<QAbstractItemView*>( widget ) )
        { _inputWidgetEngine->registerWidget( widget, AnimationHover|AnimationFocus ); }

        // scrollarea
        else if( QAbstractScrollArea* scrollArea = qobject_cast<QAbstractScrollArea*>( widget ) ) {

            if( scrollArea->frameShadow() == QFrame::Sunken && (widget->focusPolicy()&Qt::StrongFocus) )
            { _inputWidgetEngine->registerWidget( widget, AnimationHover|AnimationFocus ); }

        }

        // mdi subwindows
        else if( qobject_cast<QMdiSubWindow*>( widget ) )
        { _mdiWindowEngine->registerWidget( widget ); }

        return;

    }

    //____________________________________________________________
    void Animations::unregisterWidget( QWidget* widget ) const
    {

        if( !widget ) return;

        /*
        these are the engines that have not been stored
        inside the list, because they can be register widgets in combination
        with other engines
        */
        _widgetEnableStateEngine->unregisterWidget( widget );
        _spinBoxEngine->unregisterWidget( widget );
        _comboBoxEngine->unregisterWidget( widget );
        _toolButtonEngine->unregisterWidget( widget );
        _toolBoxEngine->unregisterWidget( widget );
        _busyIndicatorEngine->unregisterWidget( widget );

        // the following allows some optimization of widget unregistration
        // it assumes that a widget can be registered atmost in one of the
        // engines stored in the list.
        for ( const BaseEngine::Pointer &engine : std::as_const(_engines) )
        { if( engine && engine.data()->unregisterWidget( widget ) ) break; }

    }

    //_______________________________________________________________
    void Animations::unregisterEngine( QObject* object )
    {
        int index( _engines.indexOf( qobject_cast<BaseEngine*>(object) ) );
        if( index >= 0 ) _engines.removeAt( index );
    }

    //_______________________________________________________________
    void Animations::registerEngine( BaseEngine* engine )
    {
        _engines.append( engine );
        connect( engine, SIGNAL(destroyed(QObject*)), this, SLOT(unregisterEngine(QObject*)) );
    }

}
