//////////////////////////////////////////////////////////////////////////////
// adwaitatransitionwidget.h
// stores event filters and maps widgets to transitions for transitions
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//////////////////////////////////////////////////////////////////////////////

#ifndef ADWAITA_TRANSITION_WIDGET_H
#define ADWAITA_TRANSITION_WIDGET_H

#include "adwaita.h"
#include "adwaitaanimation.h"
#include "adwaitaqt_export.h"

#include <QWidget>

#include <cmath>

namespace Adwaita
{
//* temporary widget used to perform smooth transition between one widget state and another
class ADWAITAQT_EXPORT TransitionWidget : public QWidget
{
    Q_OBJECT

    //* declare opacity property
    Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity)
public:
    //* shortcut to painter
    typedef WeakPointer<TransitionWidget> Pointer;

    //* constructor
    TransitionWidget(QWidget *parent, int duration);

    //* destructor
    virtual ~TransitionWidget() = default;

    //*@name flags
    //@{
    enum Flag { None = 0, GrabFromWindow = 1 << 0, Transparent = 1 << 1, PaintOnWidget = 1 << 2 };

    Q_DECLARE_FLAGS(Flags, Flag)

    void setFlags(Flags value)
    {
        _flags = value;
    }

    void setFlag(Flag flag, bool value = true)
    {
        if (value) {
            _flags |= flag;
        } else {
            _flags &= (~flag);
        }
    }

    bool testFlag(Flag flag) const
    {
        return _flags.testFlag(flag);
    }

    //@}

    //* duration
    void setDuration(int duration)
    {
        if (_animation) {
            _animation.data()->setDuration(duration);
        }
    }

    //* duration
    int duration() const
    {
        return (_animation) ? _animation.data()->duration() : 0;
    }

    //* steps
    static void setSteps(int value)
    {
        _steps = value;
    }

    //*@name opacity
    //@{

    virtual qreal opacity() const
    {
        return _opacity;
    }

    virtual void setOpacity(qreal value)
    {
        value = digitize(value);
        if (_opacity == value) {
            return;
        }

        _opacity = value;
        update();
    }

    //@}

    //@name pixmaps handling
    //@{

    //* start
    void resetStartPixmap()
    {
        setStartPixmap(QPixmap());
    }

    //* start
    void setStartPixmap(QPixmap pixmap)
    {
        _startPixmap = pixmap;
    }

    //* start
    const QPixmap &startPixmap() const
    {
        return _startPixmap;
    }

    //* end
    void resetEndPixmap()
    {
        setEndPixmap(QPixmap());
    }

    //* end
    void setEndPixmap(QPixmap pixmap)
    {
        _endPixmap = pixmap;
        _currentPixmap = pixmap;
    }

    //* start
    const QPixmap &endPixmap() const
    {
        return _endPixmap;
    }

    //* current
    const QPixmap &currentPixmap() const
    {
        return _currentPixmap;
    }

    //@}

    //* grap pixmap
    QPixmap grab(QWidget * = 0, QRect = QRect());

    //* true if animated
    virtual bool isAnimated() const
    {
        return _animation.data()->isRunning();
    }

    //* end animation
    virtual void endAnimation()
    {
        if (_animation.data()->isRunning()) {
            _animation.data()->stop();
        }
    }

    //* animate transition
    virtual void animate()
    {
        if (_animation.data()->isRunning()) {
            _animation.data()->stop();
        }
        _animation.data()->start();
    }

    //* true if paint is enabled
    static bool paintEnabled();

protected:
    //* generic event filter
    virtual bool event(QEvent *);

    //* paint event
    virtual void paintEvent(QPaintEvent *);

    //* grab widget background
    /*!
    Background is not rendered properly using QWidget::render.
    Use home-made grabber instead. This is directly inspired from bespin.
    Copyright (C) 2007 Thomas Luebking <thomas.luebking@web.de>
    */
    virtual void grabBackground(QPixmap &, QWidget *, QRect &) const;

    //* grab widget
    virtual void grabWidget(QPixmap &, QWidget *, QRect &) const;

    //* fade pixmap
    virtual void fade(const QPixmap &source, QPixmap &target, qreal opacity, const QRect &) const;

    //* apply step
    virtual qreal digitize(const qreal &value) const
    {
        if (_steps > 0) {
            return std::floor(value * _steps) / _steps;
        } else {
            return value;
        }
    }

private:
    //* Flags
    Flags _flags = None;

    //* paint enabled
    static bool _paintEnabled;

    //* internal transition animation
    Animation::Pointer _animation;

    //* animation starting pixmap
    QPixmap _startPixmap;

    //* animation starting pixmap
    QPixmap _localStartPixmap;

    //* animation starting pixmap
    QPixmap _endPixmap;

    //* current pixmap
    QPixmap _currentPixmap;

    //* current state opacity
    qreal _opacity = 0;

    //* steps
    static int _steps;
};

} // namespace Adwaita

#endif // ADWAITA_TRANSITION_WIDGET_H
