/***************************************************************************
 *   Copyright (C) 2006 by Peter Penz                                      *
 *   peter.penz@gmx.at                                                     *
 *                                                                         *
 *   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 "statusbarmessagelabel.h"

#include <kcolorscheme.h>
#include <kiconloader.h>
#include <kicon.h>
#include <klocale.h>

#include <QFontMetrics>
#include <QPainter>
#include <QKeyEvent>
#include <QPixmap>
#include <QToolButton>
#include <QTimer>

StatusBarMessageLabel::StatusBarMessageLabel(QWidget* parent) :
    QWidget(parent),
    m_type(DolphinStatusBar::Default),
    m_state(Default),
    m_illumination(0),
    m_minTextHeight(-1),
    m_timer(0),
    m_closeButton(0)
{
    setMinimumHeight(KIconLoader::SizeSmall);

    m_timer = new QTimer(this);
    connect(m_timer, SIGNAL(timeout()),
            this, SLOT(timerDone()));

    m_closeButton = new QToolButton(this);
    m_closeButton->setAutoRaise(true);
    m_closeButton->setIcon(KIcon("dialog-close"));
    m_closeButton->setToolTip(i18nc("@info", "Close"));
    m_closeButton->hide();
    connect(m_closeButton, SIGNAL(clicked()),
            this, SLOT(closeErrorMessage()));
}

StatusBarMessageLabel::~StatusBarMessageLabel()
{
}

void StatusBarMessageLabel::setMessage(const QString& text,
                                       DolphinStatusBar::Type type)
{
    if ((text == m_text) && (type == m_type)) {
        return;
    }

    if (m_type == DolphinStatusBar::Error) {
        if (type == DolphinStatusBar::Error) {
            m_pendingMessages.insert(0, m_text);
        } else if ((m_state != Default) || !m_pendingMessages.isEmpty()) {
            // a non-error message should not be shown, as there
            // are other pending error messages in the queue
            return;
        }
    }

    m_text = text;
    m_type = type;

    m_timer->stop();
    m_illumination = 0;
    m_state = Default;

    const char* iconName = 0;
    QPixmap pixmap;
    switch (type) {
    case DolphinStatusBar::OperationCompleted:
        iconName = "dialog-ok";
        // "ok" icon should probably be "dialog-success", but we don't have that icon in KDE 4.0
        m_closeButton->hide();
        break;

    case DolphinStatusBar::Information:
        iconName = "dialog-information";
        m_closeButton->hide();
        break;

    case DolphinStatusBar::Error:
        m_timer->start(100);
        m_state = Illuminate;

        updateCloseButtonPosition();
        m_closeButton->show();
        break;

    case DolphinStatusBar::Default:
    default:
        m_closeButton->hide();
        break;
    }

    m_pixmap = (iconName == 0) ? QPixmap() : SmallIcon(iconName);
    QTimer::singleShot(GeometryTimeout, this, SLOT(assureVisibleText()));
    update();
}

DolphinStatusBar::Type StatusBarMessageLabel::type() const
{
    return m_type;
}

QString StatusBarMessageLabel::text() const
{
    return m_text;
}

void StatusBarMessageLabel::setDefaultText(const QString& text)
{
    m_defaultText = text;
}

QString StatusBarMessageLabel::defaultText() const
{
    return m_defaultText;
}

void StatusBarMessageLabel::setMinimumTextHeight(int min)
{
    if (min != m_minTextHeight) {
        m_minTextHeight = min;
        setMinimumHeight(min);
        if (m_closeButton->height() > min) {
            m_closeButton->setFixedHeight(min);
        }
    }
}

int StatusBarMessageLabel::minimumTextHeight() const
{
    return m_minTextHeight;
}

int StatusBarMessageLabel::widthGap() const
{
    QFontMetrics fontMetrics(font());
    const int defaultGap = 10;
    return fontMetrics.width(m_text) - availableTextWidth() + defaultGap;
}

void StatusBarMessageLabel::paintEvent(QPaintEvent* /* event */)
{
    QPainter painter(this);

    if (m_illumination > 0) {
        // at this point, a: we are a second label being drawn over the already
        // painted status area, so we can be translucent, and b: our palette's
        // window color (bg only) seems to be wrong (always black)
        KColorScheme scheme(palette().currentColorGroup(), KColorScheme::Window);
        QColor backgroundColor = scheme.background(KColorScheme::NegativeBackground).color();
        backgroundColor.setAlpha(qMin(255, m_illumination * 2));
        painter.setBrush(backgroundColor);
        painter.setPen(Qt::NoPen);
        painter.drawRect(QRect(0, 0, width(), height()));
    }

    // draw pixmap
    int x = BorderGap;
    const int y = (m_minTextHeight - m_pixmap.height()) / 2;

    if (!m_pixmap.isNull()) {
        painter.drawPixmap(x, y, m_pixmap);
        x += m_pixmap.width() + BorderGap;
    }

    // draw text
    painter.setPen(palette().windowText().color());
    int flags = Qt::AlignVCenter;
    if (height() > m_minTextHeight) {
        flags = flags | Qt::TextWordWrap;
    }
    painter.drawText(QRect(x, 0, availableTextWidth(), height()), flags, m_text);
    painter.end();
}

void StatusBarMessageLabel::resizeEvent(QResizeEvent* event)
{
    QWidget::resizeEvent(event);
    updateCloseButtonPosition();
    QTimer::singleShot(GeometryTimeout, this, SLOT(assureVisibleText()));
}

void StatusBarMessageLabel::timerDone()
{
    switch (m_state) {
    case Illuminate: {
        // increase the illumination
        const int illumination_max = 128;
        if (m_illumination < illumination_max) {
            m_illumination += 32;
            if (m_illumination > illumination_max) {
                m_illumination = illumination_max;
            }
            update();
        } else {
            m_state = Illuminated;
            m_timer->start(5000);
        }
        break;
    }

    case Illuminated: {
        // start desaturation
        m_state = Desaturate;
        m_timer->start(100);
        break;
    }

    case Desaturate: {
        // desaturate
        if (m_illumination > 0) {
            m_illumination -= 5;
            update();
        } else {
            m_state = Default;
            m_timer->stop();
        }
        break;
    }

    default:
        break;
    }
}

void StatusBarMessageLabel::assureVisibleText()
{
    if (m_text.isEmpty()) {
        return;
    }

    int requiredHeight = m_minTextHeight;
    if (m_type != DolphinStatusBar::Default) {
        // Calculate the required height of the widget thats
        // needed for having a fully visible text. Note that for the default
        // statusbar type (e. g. hover information) increasing the text height
        // is not wanted, as this might rearrange the layout of items.

        QFontMetrics fontMetrics(font());
        const QRect bounds(fontMetrics.boundingRect(0, 0, availableTextWidth(), height(),
                           Qt::AlignVCenter | Qt::TextWordWrap, m_text));
        requiredHeight = bounds.height();
        if (requiredHeight < m_minTextHeight) {
            requiredHeight = m_minTextHeight;
        }
    }

    // Increase/decrease the current height of the widget to the
    // required height. The increasing/decreasing is done in several
    // steps to have an animation if the height is modified
    // (see StatusBarMessageLabel::resizeEvent())
    const int gap = m_minTextHeight / 2;
    int minHeight = minimumHeight();
    if (minHeight < requiredHeight) {
        minHeight += gap;
        if (minHeight > requiredHeight) {
            minHeight = requiredHeight;
        }
        setMinimumHeight(minHeight);
        updateGeometry();
    } else if (minHeight > requiredHeight) {
        minHeight -= gap;
        if (minHeight < requiredHeight) {
            minHeight = requiredHeight;
        }
        setMinimumHeight(minHeight);
        updateGeometry();
    }

    updateCloseButtonPosition();
}

int StatusBarMessageLabel::availableTextWidth() const
{
    const int buttonWidth = (m_type == DolphinStatusBar::Error) ?
                            m_closeButton->width() + BorderGap : 0;
    return width() - m_pixmap.width() - (BorderGap * 4) - buttonWidth;
}

void StatusBarMessageLabel::updateCloseButtonPosition()
{
    const int x = width() - m_closeButton->width() - BorderGap;
    m_closeButton->move(x, 0);
}

void StatusBarMessageLabel::closeErrorMessage()
{
    if (!showPendingMessage()) {
        reset();
        setMessage(m_defaultText, DolphinStatusBar::Default);
    }
}

bool StatusBarMessageLabel::showPendingMessage()
{
    if (!m_pendingMessages.isEmpty()) {
        reset();
        setMessage(m_pendingMessages.takeFirst(), DolphinStatusBar::Error);
        return true;
    }
    return false;
}

void StatusBarMessageLabel::reset()
{
    m_text.clear();
    m_type = DolphinStatusBar::Default;
}

#include "statusbarmessagelabel.moc"
