/*
 * Kopete plugin to display incoming messages in message indicator
 *
 * Copyright 2009-2010 Canonical Ltd.
 *
 * Authors:
 * - Aurélien Gâteau <aurelien.gateau@canonical.com>
 *
 * License: GPL v3
 */
// Qt
#include <QDateTime>

// KDE
#include <kdebug.h>
#include <kgenericfactory.h>
#include <kicon.h>
#include <kmessagebox.h>
#include <kservice.h>
#include <kwindowsystem.h>

// libindicate-qt
#include <qindicateindicator.h>

// libkopete
#include <kopete/kopetechatsessionmanager.h>
#include <kopete/kopetecontact.h>
#include <kopete/kopetemessageevent.h>
#include <kopete/kopetemetacontact.h>
#include <kopete/kopetepicture.h>
#include <kopete/kopeteuiglobal.h>

// local
#include "indicatorplugin.h"

K_PLUGIN_FACTORY(IndicatorPluginFactory, registerPlugin<IndicatorPlugin>();)
K_EXPORT_PLUGIN(IndicatorPluginFactory("kopete_message_indicator"))

IndicatorPlugin::IndicatorPlugin(QObject* parent, const QVariantList&  /* args */)
: Kopete::Plugin(IndicatorPluginFactory::componentData(), parent)
, m_interestCount(0)
{
    m_indicateServer = QIndicate::Server::defaultInstance();
    m_indicateServer->setType("message.im");
    QString appName = KGlobal::mainComponent().componentName();
    kDebug() << "appName" << appName;
    KService::Ptr service = KService::serviceByDesktopName(appName);
    if (service) {
        m_indicateServer->setDesktopFile(service->entryPath());
    } else {
        kWarning() << "Could not find desktop file for application";
    }
    m_indicateServer->show();
    connect(m_indicateServer, SIGNAL(serverDisplay()),
        SLOT(showMainWindow()));

    connect(m_indicateServer, SIGNAL(interestAdded(QIndicate::Interest)),
        SLOT(slotInterestAdded(QIndicate::Interest)));
    connect(m_indicateServer, SIGNAL(interestRemoved(QIndicate::Interest)),
        SLOT(slotInterestRemoved(QIndicate::Interest)));

    Kopete::ChatSessionManager* sessionManager = Kopete::ChatSessionManager::self();
    connect(sessionManager, SIGNAL(chatSessionCreated(Kopete::ChatSession*)),
        SLOT(createIndicatorForChatSession(Kopete::ChatSession*)));
    connect(sessionManager, SIGNAL(newEvent(Kopete::MessageEvent*)),
        SLOT(slotNewEvent(Kopete::MessageEvent*)));
}

IndicatorPlugin::~IndicatorPlugin()
{
    qDeleteAll(m_indicatorForSession);
}

void IndicatorPlugin::slotNewEvent(Kopete::MessageEvent* event)
{
    m_eventList.append(event);
    connect(event, SIGNAL(done(Kopete::MessageEvent*)),
        SLOT(slotEventDone(Kopete::MessageEvent*)));
}

void IndicatorPlugin::slotEventDone(Kopete::MessageEvent* event)
{
    m_eventList.removeAll(event);
}

void IndicatorPlugin::createIndicatorForChatSession(Kopete::ChatSession* session)
{
    if (m_indicatorForSession.contains(session)) {
        return;
    }
    QIndicate::Indicator* indicator = new QIndicate::Indicator;
    m_indicatorForSession.insert(session, indicator);

    connect(Kopete::ChatSessionManager::self(), SIGNAL(viewActivated(KopeteView*)),
        SLOT(slotViewActivated(KopeteView*)));

    connect(session, SIGNAL(closing(Kopete::ChatSession*)),
        SLOT(deleteIndicatorForChatSession(Kopete::ChatSession*)));

    connect(session, SIGNAL(messageReceived(Kopete::Message&, Kopete::ChatSession*)),
        SLOT(slotMessageReceived(Kopete::Message&, Kopete::ChatSession*)));

    connect(indicator, SIGNAL(display(QIndicate::Indicator*)),
        SLOT(slotIndicatorDisplay(QIndicate::Indicator*)), Qt::QueuedConnection);
}

void IndicatorPlugin::deleteIndicatorForChatSession(Kopete::ChatSession* session)
{
    IndicatorForSessionHash::Iterator it = m_indicatorForSession.find(session);
    if (it == m_indicatorForSession.end()) {
        kWarning() << "no indicator for chat session";
        return;
    }
    m_indicatorForSession.erase(it);
    delete it.value();
}

void IndicatorPlugin::slotViewActivated(KopeteView* view)
{
    Kopete::ChatSession* session = view->msgManager();
    QIndicate::Indicator* indicator = m_indicatorForSession.value(session);
    if (!indicator) {
        kWarning() << "no indicator for chat session";
        return;
    }
    indicator->hide();
}

void IndicatorPlugin::slotMessageReceived(Kopete::Message& msg, Kopete::ChatSession* session)
{
    QIndicate::Indicator* indicator = m_indicatorForSession.value(session);
    if (!indicator) {
        kWarning() << "no indicator for chat session";
        return;
    }
    KopeteView* view = session->view();
    if (view && Kopete::ChatSessionManager::self()->activeView() == view) {
        // View is visible, do not show an indicator
        return;
    }

    indicator->setTimeProperty(msg.timestamp());
    const Kopete::Contact* contact = msg.from();
    const Kopete::MetaContact* metaContact = contact->metaContact();
    QImage image;
    QString name;
    if (metaContact) {
        image = metaContact->picture().image();
        name = metaContact->displayName();
    } else {
        // I'm quite sure metaContact can't be null, but let's play it safe
        name = contact->nickName();
        kWarning() << "No metaContact() for" << name;
    }
    indicator->setNameProperty(name);
    indicator->setIconProperty(image);
    indicator->setDrawAttentionProperty(true);
    indicator->show();
}

void IndicatorPlugin::showMainWindow()
{
    QWidget* widget = Kopete::UI::Global::mainWidget();
    if (!widget) {
        kWarning() << "No main widget!";
        return;
    }
    widget->show();
    KWindowSystem::forceActiveWindow(widget->winId());
}

void IndicatorPlugin::slotIndicatorDisplay(QIndicate::Indicator* indicator)
{
    // Find session... not very optimized, but we should not have too many
    // indicators at once
    IndicatorForSessionHash::ConstIterator
        it = m_indicatorForSession.constBegin(),
        end = m_indicatorForSession.constEnd();
    for (;it != end; ++it) {
        if (it.value() == indicator) {
            break;
        }
    }
    if (it == end) {
        kWarning() << "Could not find session for this indicator";
        return;
    }

    Kopete::ChatSession* session = it.key();

    // Mark event done, to stop systray blink
    Q_FOREACH(Kopete::MessageEvent* event, m_eventList) {
        if (event->message().manager() == session) {
            event->apply();
        }
    }

    // Show window
    KopeteView* view = session->view(true /* canCreate */);
    if (!view) {
        kWarning() << "No view created for this session";
        return;
    }
    view->makeVisible();
    QWidget* widget = view->mainWidget();
    if (!widget) {
        // Highly unlikely
        kWarning() << "No widget";
        view->raise(true /* activate */);
    } else {
        KWindowSystem::forceActiveWindow(widget->window()->winId());
    }
}

bool IndicatorPlugin::showCloseWindowMessage()
{
    if (!isConnectedToListener()) {
        return false;
    }
    QWidget* widget = Kopete::UI::Global::mainWidget();
    KMessageBox::information ( widget,
                               i18n ( "<qt>Closing the main window will keep Kopete running in the "
                                      "indicator widget. Use 'Quit' from the 'File' menu to quit the application.</qt>" ),
                               i18n ( "Docking in indicator" ), "dockInIndicatorOnCloseInfo" );
    return true;
}

bool IndicatorPlugin::shouldExitOnClose()
{
    return !isConnectedToListener();
}

bool IndicatorPlugin::isConnectedToListener() const
{
    // This is not 100% reliable as it relies on listeners to correctly set and
    // reset the InterestServerSignal Interest. In particular, m_interestCount
    // could still be greater than 0 if a listener goes away without removing
    // its interest.
    return m_interestCount > 0;
}

void IndicatorPlugin::slotInterestAdded(QIndicate::Interest interest)
{
    if (interest != QIndicate::InterestServerSignal) {
        return;
    }
    ++m_interestCount;
}

void IndicatorPlugin::slotInterestRemoved(QIndicate::Interest interest)
{
    if (interest != QIndicate::InterestServerSignal) {
        return;
    }
    --m_interestCount;

    QWidget* widget = Kopete::UI::Global::mainWidget();
    if (m_interestCount == 0 && widget->isHidden()) {
        // No listener anymore, bring the widget back to the user
        widget->show();
    }
}

#include "indicatorplugin.moc"
