/* BEGIN_COMMON_COPYRIGHT_HEADER
 * (c)LGPL2+
 *
 * LXDE-Qt - a lightweight, Qt based, desktop toolset
 * http://razor-qt.org
 *
 * Copyright: 2012 Razor team
 * Authors:
 *   Petr Vanek <petr@scribus.info>
 *
 * This program or library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 General Public License for more details.

 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include "notificationlayout.h"
#include <QtDebug>
#include <QBrush>


NotificationLayout::NotificationLayout(QWidget *parent)
    : QWidget(parent)
{
    setObjectName("NotificationLayout");

    // Hack to ensure the fully transparent background
    QPalette palette;
    palette.setBrush(QPalette::Base, Qt::NoBrush);
    setPalette(palette);
    // Required to display wallpaper
    setAttribute(Qt::WA_TranslucentBackground);

    // Make the window stay on top
    setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);

    m_layout = new QVBoxLayout(this);
    m_layout->setMargin(0);
    setLayout(m_layout);
}

void NotificationLayout::setSizes(int space, int width)
{
    m_layout->setSpacing(space);
    setMaximumWidth(width);
    setMinimumWidth(width);

    QHashIterator<uint, Notification*> it(m_notifications);
    while (it.hasNext())
    {
        it.next();
        it.value()->setMinimumWidth(width);
        it.value()->setMaximumWidth(width);
    }
}

void NotificationLayout::addNotification(uint id, const QString &application,
                                        const QString &summary, const QString &body,
                                        const QString &icon, int timeout,
                                        const QStringList& actions, const QVariantMap& hints)
{
//    qDebug() << "NotificationLayout::addNotification" << id << application << summary << body << icon << timeout;
    if (m_notifications.contains(id))
    {
        // TODO/FIXME: it can be deleted by timer in this block. Locking?
        Notification *n = m_notifications[id];
        n->setValues(application, summary, body, icon, timeout, actions, hints);
    }
    else
    {
        Notification *n = new Notification(application, summary, body, icon, timeout, actions, hints, this);

        // NOTE: it's hard to use == operator for Notification* in QList...
        QHashIterator<uint, Notification*> it(m_notifications);
        while (it.hasNext())
        {
            it.next();
            if (it.value()->application() == application
                    && it.value()->summary() == summary
                    && it.value()->body() == body)
            {
                qDebug() << "Notification app" << application << "summary" << summary << "is already registered but this request is not an update. Broken app?";
                delete n;
                return;
            }
        }

        connect(n, &Notification::timeout, this, &NotificationLayout::removeNotificationTimeout);
        connect(n, &Notification::userCanceled, this, &NotificationLayout::removeNotificationUser);
        connect(n, &Notification::actionTriggered,
                this, &NotificationLayout::notificationActionCalled);
        m_notifications[id] = n;
        m_layout->addWidget(n);
        n->show();
    }

    checkHeight();
    emit notificationAvailable();

    // NOTE by pcman:
    // This dirty hack is used to workaround a weird and annoying repainting bug caused by Qt.
    // See https://github.com/Razor-qt/razor-qt/issues/536
    // razot-qt bug #536 - Notifications do not repaint under certain conditions
    // When we create the first notification and are about to show the widget, force repaint() here.
    // FIXME: there should be better ways to do this, or it should be fixed in Qt instead.
    if(m_notifications.count() == 1)
        repaint();
}

void NotificationLayout::removeNotificationTimeout()
{
    Notification *n = qobject_cast<Notification*>(sender());
    if (!n)
    {
        qDebug() << "Oooook! TIMEOUT Expecting instance of notification, got:" << sender();
        return;
    }

    removeNotification(m_notifications.key(n), 1);
}

void NotificationLayout::removeNotificationUser()
{
    Notification *n = qobject_cast<Notification*>(sender());
    if (!n)
    {
        qDebug() << "Oooook! USERCANCEL Expecting instance of notification, got:" << sender();
        return;
    }

    removeNotification(m_notifications.key(n), 2);
}

void NotificationLayout::removeNotification(uint key, uint reason)
{
    Notification *n = m_notifications.take(key);
    if (!n)
    {
        qDebug() << "Oooook! Expecting instance of notification, got:" << key;
        return;
    }

    int ix = m_layout->indexOf(n);
    if (ix == -1)
    {
        qDebug() << "Qooook! Widget not in layout. Impossible!" << n;
        return;
    }

    delete m_layout->takeAt(ix);
    n->deleteLater();
    emit notificationClosed(key, reason);

    if (m_notifications.count() == 0)
        emit allNotificationsClosed();

    checkHeight();
}

void NotificationLayout::notificationActionCalled(const QString &actionKey)
{
    Notification *n = qobject_cast<Notification*>(sender());
    if (!n)
    {
        qDebug() << "Oooook! USERACTION Expecting instance of notification, got:" << sender();
        return;
    }

    emit actionInvoked(m_notifications.key(n), actionKey);
}

void NotificationLayout::checkHeight()
{
    int h = 0;
    QHashIterator<uint, Notification*> it(m_notifications);
    while (it.hasNext())
    {
        it.next();
        // *2 is mandatory here to prevent cropping of widgets
        //  with enforced small height
        h += it.value()->height() + m_layout->spacing() * 2;
    }

    setMinimumSize(width(), h);
    setMaximumSize(width(), h);

    emit heightChanged(h);
}

