/*
 *   Copyright (C) 2007, 2008, 2009, 2010 Ivan Cukic <ivan.cukic(at)kde.org>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser/Library General Public License version 2,
 *   or (at your option) any later version, as published by the Free
 *   Software Foundation
 *
 *   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 Lesser/Library General Public License for more details
 *
 *   You should have received a copy of the GNU Lesser/Library 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 "ExtenderButton.h"

#include <QAction>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QIcon>
#include <QRectF>
#include <QTimer>
#include <QPointer>

#include <KDebug>

#define ACTIVATION_TIME 300
#define EXTENDER_Z_VALUE 100000.0

namespace Lancelot {

// Inner class ExtenderObject
class ExtenderObject : public BasicWidget {
public:
    ExtenderObject(const Plasma::Svg & icon,
            ExtenderButton * parent)
      : BasicWidget(icon, "", "")
    {
        setParentItem(parent);
        setInnerOrientation(Qt::Vertical);
        setAlignment(Qt::AlignCenter);
        setZValue(EXTENDER_Z_VALUE);
    }

    // Needed because of a Qt bug - making it public
    // so that ExtenderButton can invoke it directly
    void hoverLeaveEvent(QGraphicsSceneHoverEvent * event)
    {
        BasicWidget::hoverLeaveEvent(event);
    }

    void paint(QPainter * painter,
            const QStyleOptionGraphicsItem * option, QWidget * widget)
    {
        Q_UNUSED(option);
        Q_UNUSED(widget);

        if (Plasma::FrameSvg * svg = group()->backgroundSvg()) {
            svg->setEnabledBorders(borders);
        }

        BasicWidget::paint(painter, option, widget);
    }

public:
    Plasma::FrameSvg::EnabledBorders borders;

};

// ExtenderButton
class ExtenderButton::Private {
public:
    Private(ExtenderButton * parent)
      : q(parent),
        shortcut(NULL),
        extenderPosition(NoExtender),
        activationMethod(ClickActivate),
        checkable(false),
        checked(false)
    {
        if (!extenderIconSvg.isValid()) {
            extenderIconSvg.setImagePath("lancelot/extender-button-icon");
        }

        if (!timer) {
            timer = new QTimer();
            timer->setInterval(ACTIVATION_TIME);
            timer->setSingleShot(true);
        }

    }

    void releaseExtender() {
        ExtenderObject * ext = extender(false);
        if (ext) {
            ext->setVisible(false);
            ext->hoverLeaveEvent(NULL);
            ext->setParentItem(NULL);
        }

        disconnectTimer();
    }

    void disconnectTimer() {
        timer->stop();
        disconnect(timer, SIGNAL(timeout()), 0, 0);
    }

    void connectTimer() {
        disconnectTimer();

        connect(
                timer, SIGNAL(timeout()),
                q, SLOT(activate())
               );
    }

    ExtenderObject * extender(bool take = true) {
        ExtenderObject * result = 0;

        if (!m_extenders.contains(q->scene()) ||
                !m_extenders[q->scene()]) {
            result = new ExtenderObject(extenderIconSvg, NULL);

            result->setVisible(false);
            result->setIconSize(QSize(16, 16));

            connect(
                    result, SIGNAL(mouseHoverEnter()),
                    timer, SLOT(start())
                   );
            connect(
                    result, SIGNAL(mouseHoverLeave()),
                    timer, SLOT(stop())
                   );

            m_extenders[q->scene()] = result;
        } else {
            result = m_extenders[q->scene()];
        }

        if (result->parentItem() != q) {
            if (!take) {
                return NULL;
            }

            result->setParentItem(q);
            result->setGroupByName(
                    q->group()->name() + "-Extender"
                    );

            connectTimer();
            relayoutExtender();
        }

        return result;
    }

    ~Private()
    {
    }

    void relayoutExtender()
    {
        if (!m_extenders.contains(q->scene())
            || !m_extenders[q->scene()]
            || q != m_extenders[q->scene()]->parentItem()) return;
        QRectF geometry = QRectF(QPointF(0, 0), q->size());

        extender()->borders = Plasma::FrameSvg::AllBorders;
        borders = Plasma::FrameSvg::AllBorders;

        switch (extenderPosition) {
        case TopExtender:
            geometry.setHeight(EXTENDER_SIZE);
            geometry.moveTop(- EXTENDER_SIZE);
            borders &= ~ Plasma::FrameSvg::TopBorder;
            extender()->borders &= ~ Plasma::FrameSvg::BottomBorder;
            break;
        case BottomExtender:
            geometry.moveTop(geometry.bottom());
            geometry.setHeight(EXTENDER_SIZE);
            borders &= ~ Plasma::FrameSvg::BottomBorder;
            extender()->borders &= ~ Plasma::FrameSvg::TopBorder;
            break;
        case LeftExtender:
            geometry.setWidth(EXTENDER_SIZE);
            geometry.moveLeft(- EXTENDER_SIZE);
            borders &= ~ Plasma::FrameSvg::LeftBorder;
            extender()->borders &= ~ Plasma::FrameSvg::RightBorder;
            break;
        case RightExtender:
            geometry.moveLeft(geometry.right());
            geometry.setWidth(EXTENDER_SIZE);
            borders &= ~ Plasma::FrameSvg::RightBorder;
            extender()->borders &= ~ Plasma::FrameSvg::LeftBorder;
            break;
        case NoExtender:
            break;
        }
        extender()->setGeometry(geometry);
        extender()->setPreferredSize(geometry.size());

        // qreal left, top, right, bottom;
        // q->getContentsMargins(&left, &top, &right, &bottom);
    }

    ExtenderButton * q;
    QAction * shortcut;

    ExtenderPosition extenderPosition;
    ActivationMethod activationMethod;

    Plasma::FrameSvg::EnabledBorders borders;

    static Plasma::Svg extenderIconSvg;
    static QHash < QGraphicsScene *, QPointer < ExtenderObject > > m_extenders;
    static QTimer * timer;

    bool checkable : 1;
    bool checked : 1;

};

Plasma::Svg ExtenderButton::Private::extenderIconSvg;
QHash < QGraphicsScene *, QPointer < ExtenderObject > > ExtenderButton::Private::m_extenders;
QTimer * ExtenderButton::Private::timer = 0;

ExtenderButton::ExtenderButton(QGraphicsItem * parent)
  : BasicWidget(parent),
    d(new Private(this))
{
    setGroupByName("ExtenderButton");
    connect(this, SIGNAL(clicked()), this, SLOT(activate()));
}

ExtenderButton::ExtenderButton(QString title,
        QString description, QGraphicsItem * parent)
  : BasicWidget(title, description, parent),
    d(new Private(this))
{
    setGroupByName("ExtenderButton");
    connect(this, SIGNAL(clicked()), this, SLOT(activate()));

    setTitle(title);
}

ExtenderButton::ExtenderButton(QIcon icon, QString title,
        QString description, QGraphicsItem * parent)
  : BasicWidget(icon, title, description, parent),
    d(new Private(this))
{
    setGroupByName("ExtenderButton");
    connect(this, SIGNAL(clicked()), this, SLOT(activate()));

    setTitle(title);
}

ExtenderButton::ExtenderButton(const Plasma::Svg & icon, QString title,
        QString description, QGraphicsItem * parent)
  : BasicWidget(icon, title, description, parent),
    d(new Private(this))
{
    setGroupByName("ExtenderButton");
    connect(this, SIGNAL(clicked()), this, SLOT(activate()));

    setTitle(title);
}

void ExtenderButton::setGroup(Group * g)
{
    BasicWidget::setGroup(g);
}

ExtenderButton::~ExtenderButton()
{
    if (Private::m_extenders.contains(scene())
        && Private::m_extenders[scene()]->parentItem() == this) {
        Private::m_extenders[scene()]->setParentItem(0);
    }

    delete d;
}

QRectF ExtenderButton::boundingRect() const
{
    return QRectF(0, 0, size().width(), size().height());
}

void ExtenderButton::hoverEnterEvent(QGraphicsSceneHoverEvent * event)
{
    if (d->extenderPosition != NoExtender) {
        d->extender()->setVisible(true);
    } else if (d->activationMethod == HoverActivate) {
        d->connectTimer();
        d->timer->start();
    }
    BasicWidget::hoverEnterEvent(event);
}

void ExtenderButton::hideEvent(QHideEvent * event)
{
    BasicWidget::hideEvent(event);
    d->releaseExtender();
}

void ExtenderButton::hoverLeaveEvent(QGraphicsSceneHoverEvent * event)
{
    BasicWidget::hoverLeaveEvent(event);
    d->releaseExtender();
}

void ExtenderButton::setExtenderPosition(int position)
{
    if (d->extenderPosition == position)
        return;
    d->extenderPosition = (ExtenderPosition) position;
    d->relayoutExtender();
}

int ExtenderButton::extenderPosition() const
{
    return d->extenderPosition;
}

void ExtenderButton::setActivationMethod(int method)
{
    if (d->activationMethod == method)
        return;
    d->activationMethod = (ActivationMethod) method;
    if (d->activationMethod != ExtenderActivate)
        setExtenderPosition(NoExtender);
}

int ExtenderButton::activationMethod() const
{
    return d->activationMethod;
}

void ExtenderButton::activate()
{
    toggle();

    hoverLeaveEvent(0);
    d->releaseExtender();

    update();
    emit activated();
}

void ExtenderButton::resizeEvent(QGraphicsSceneResizeEvent * event)
{
    BasicWidget::resizeEvent(event);
    d->relayoutExtender();
}

void ExtenderButton::paint(QPainter * painter,
        const QStyleOptionGraphicsItem * option, QWidget * widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);

    if (Plasma::FrameSvg * svg = group()->backgroundSvg()) {
        if (isHovered() && (d->extenderPosition != NoExtender)) {
            svg->setEnabledBorders(d->borders);
        } else {
            svg->setEnabledBorders(Plasma::FrameSvg::AllBorders);
        }
    }

    if (d->checked && !isDown()) {
        paintBackground(painter, "checked");
    } else {
        paintBackground(painter);
    }
    paintForeground(painter);
}

void ExtenderButton::setChecked(bool checked)
{
    if (!d->checkable) return;
    if (d->checked == checked) return;
    d->checked = checked;
    update();
    emit toggled(d->checked);
}

bool ExtenderButton::isChecked() const
{
    return d->checked;
}

void ExtenderButton::toggle()
{
    if (!d->checkable) return;
    d->checked = !d->checked;
    update();
    emit toggled(d->checked);
}

void ExtenderButton::setCheckable(bool checkable)
{
    if (d->checkable == checkable) return;
    d->checkable = checkable;
    if (!checkable) {
        d->checked = false;
    }
    update();
}

bool ExtenderButton::isCheckable() const
{
    return d->checkable;
}

void ExtenderButton::setShortcutKey(const QString & key)
{
    kDebug() << title() << " set the key:" << key;

    if (key.isEmpty()) {
        delete d->shortcut;
        d->shortcut = NULL;
    } else if (scene() && scene()->views().size()) {
        if (!d->shortcut) {
            d->shortcut = new QAction(this);
            scene()->views().at(0)->addAction(d->shortcut);
        }

        d->shortcut->setShortcut("Alt+" + key);
        connect(d->shortcut, SIGNAL(triggered()),
                this, SIGNAL(activated()));
    }
}

} // namespace Lancelot

