/*************************************************************************
 * Copyright (C) 2014 by Hugo Pereira Da Costa <hugo.pereira@free.fr>    *
 *                                                                       *
 * This program is free software; you can redistribute it and/or modify  *
 * it under the terms of the GNU General Public License as published by  *
 * the Free Software Foundation; either version 2 of the License, or     *
 * (at your option) any later version.                                   *
 *                                                                       *
 * This program 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 General Public License for more details.                          *
 *                                                                       *
 * You should have received a copy of the GNU General Public License     *
 * along with this program; if not, write to the                         *
 * Free Software Foundation, Inc.,                                       *
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
 *************************************************************************/

#include "adwaitaanimations.h"

#include <QAbstractItemView>
#include <QComboBox>
#include <QCheckBox>
#include <QDial>
#include <QGroupBox>
#include <QHeaderView>
#include <QLineEdit>
#include <QProgressBar>
#include <QRadioButton>
#include <QScrollBar>
#include <QSpinBox>
#include <QTextEdit>
#include <QToolBox>
#include <QToolButton>

namespace Adwaita
{

//____________________________________________________________
Animations::Animations(QObject *parent)
    : QObject(parent)
{
    _widgetEnabilityEngine = new WidgetStateEngine(this);
    _busyIndicatorEngine = new BusyIndicatorEngine(this);
    _comboBoxEngine = new WidgetStateEngine(this);
    _toolButtonEngine = new WidgetStateEngine(this);
    _spinBoxEngine = new SpinBoxEngine(this);
    _toolBoxEngine = new ToolBoxEngine(this);

    registerEngine(_headerViewEngine = new HeaderViewEngine(this));
    registerEngine(_widgetStateEngine = new WidgetStateEngine(this));
    registerEngine(_inputWidgetEngine = new WidgetStateEngine(this));
    registerEngine(_scrollBarEngine = new ScrollBarEngine(this));
    registerEngine(_stackedWidgetEngine = new StackedWidgetEngine(this));
    registerEngine(_tabBarEngine = new TabBarEngine(this));
    registerEngine(_dialEngine = new DialEngine(this));
}

//____________________________________________________________
void Animations::setupEngines()
{
    // animation steps
    AnimationData::setSteps(Adwaita::Config::AnimationSteps);

    bool animationsEnabled(Adwaita::Config::AnimationsEnabled);
    int animationsDuration(Adwaita::Config::AnimationsDuration);

    _widgetEnabilityEngine->setEnabled(animationsEnabled);
    _comboBoxEngine->setEnabled(animationsEnabled);
    _toolButtonEngine->setEnabled(animationsEnabled);
    _spinBoxEngine->setEnabled(animationsEnabled);
    _toolBoxEngine->setEnabled(animationsEnabled);

    _widgetEnabilityEngine->setDuration(animationsDuration);
    _comboBoxEngine->setDuration(animationsDuration);
    _toolButtonEngine->setDuration(animationsDuration);
    _spinBoxEngine->setDuration(animationsDuration);
    _stackedWidgetEngine->setDuration(animationsDuration);
    _toolBoxEngine->setDuration(animationsDuration);

    // registered engines
    foreach (const BaseEngine::Pointer &engine, _engines) {
        engine.data()->setEnabled(animationsEnabled);
        engine.data()->setDuration(animationsDuration);
    }

    // stacked widget transition has an extra flag for animations
    _stackedWidgetEngine->setEnabled(animationsEnabled && Adwaita::Config::StackedWidgetTransitionsEnabled);

    // busy indicator
    _busyIndicatorEngine->setEnabled(Adwaita::Config::ProgressBarAnimated);
    _busyIndicatorEngine->setDuration(Adwaita::Config::ProgressBarBusyStepDuration);
}

//____________________________________________________________
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;
    }

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

    // install animation timers
    // for optimization, one should put with most used widgets here first

    // buttons
    if (qobject_cast<QToolButton *>(widget)) {
        _toolButtonEngine->registerWidget(widget, AnimationHover | AnimationPressed);
        _widgetStateEngine->registerWidget(widget, AnimationHover | AnimationPressed);
    } else if (qobject_cast<QCheckBox *>(widget) || qobject_cast<QRadioButton *>(widget)) {
        _widgetStateEngine->registerWidget(widget, AnimationHover | AnimationFocus | AnimationPressed);
    } else if (qobject_cast<QAbstractButton *>(widget)) {
        // register to toolbox engine if needed
        if (qobject_cast<QToolBox *>(widget->parent())) {
            _toolBoxEngine->registerWidget(widget);
        }

        _widgetStateEngine->registerWidget(widget, AnimationHover | AnimationPressed);
    // groupboxes
    } else if (QGroupBox *groupBox = qobject_cast<QGroupBox *>(widget)) {
        if (groupBox->isCheckable()) {
            _widgetStateEngine->registerWidget(widget, AnimationHover | AnimationFocus);
        }
    // sliders
    } else if (qobject_cast<QScrollBar *>(widget)) {
        _scrollBarEngine->registerWidget(widget, AnimationHover | AnimationFocus);
    } else if (qobject_cast<QSlider *>(widget)) {
        _widgetStateEngine->registerWidget(widget, AnimationHover | AnimationFocus);
    } else if (qobject_cast<QDial *>(widget)) {
        _dialEngine->registerWidget(widget, AnimationHover | AnimationFocus);
    // progress bar
    } else if (qobject_cast<QProgressBar *>(widget)) {
        _busyIndicatorEngine->registerWidget(widget);
    // combo box
    } else if (qobject_cast<QComboBox *>(widget)) {
        _comboBoxEngine->registerWidget(widget, AnimationHover | AnimationPressed);
        _inputWidgetEngine->registerWidget(widget, AnimationHover | AnimationFocus | AnimationPressed);
    // spinbox
    } else if (qobject_cast<QAbstractSpinBox *>(widget)) {
        _spinBoxEngine->registerWidget(widget);
        _inputWidgetEngine->registerWidget(widget, AnimationHover | AnimationFocus | AnimationPressed);
    // editors
    } 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);
    // header views
    // need to come before abstract item view, otherwise is skipped
    } else if (qobject_cast<QHeaderView *>(widget)) {
        _headerViewEngine->registerWidget(widget);
    // lists
    } else if (qobject_cast<QAbstractItemView *>(widget)) {
        _inputWidgetEngine->registerWidget(widget, AnimationHover | AnimationFocus);
    // tabbar
    } else if (qobject_cast<QTabBar *>(widget)) {
        _tabBarEngine->registerWidget(widget);
    // scrollarea
    } else if (QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea *>(widget)) {
        if (scrollArea->frameShadow() == QFrame::Sunken && (widget->focusPolicy()&Qt::StrongFocus)) {
            _inputWidgetEngine->registerWidget(widget, AnimationHover | AnimationFocus);
        }
    }

    // stacked widgets
    if (QStackedWidget *stack = qobject_cast<QStackedWidget *>(widget)) {
        _stackedWidgetEngine->registerWidget(stack);
    }

    return;
}

//____________________________________________________________
void Animations::unregisterWidget(QWidget *widget) const
{
    if (!widget) {
        return;
    }

    _widgetEnabilityEngine->unregisterWidget(widget);
    _spinBoxEngine->unregisterWidget(widget);
    _comboBoxEngine->unregisterWidget(widget);
    _busyIndicatorEngine->registerWidget(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.
    foreach (const BaseEngine::Pointer &engine, _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 *)));
}

} // namespace Adwaita
