/*
 * gmailserviceplugin.cpp - plugin
 * Copyright (C) 2009-2011 Kravtsov Nikolai, Evgeny Khryukin
 *
 * 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */

#include <QFileDialog>
#include <QProcess>
#include <QDomElement>
#include "gmailserviceplugin.h"
#include "common.h"

#ifndef HAVE_QT5
Q_EXPORT_PLUGIN(GmailNotifyPlugin);
#endif

GmailNotifyPlugin::GmailNotifyPlugin()
    : enabled(false)
    , optionsApplingInProgress_(false)
    , stanzaSender(0)
    , psiOptions(0)
    , accInfo(0)
    , popup(0)
    , accountController(0)
    , iconHost(0)
    , psiEvent(0)
    , sound_(0)
    , soundFile("sound/email.wav")
    , actions_(0)
    , popupId(0)
{
}

QString GmailNotifyPlugin::name() const
{
    return "GMail Service Plugin";
}

QString GmailNotifyPlugin::shortName() const
{
    return "gmailnotify";
}

QString GmailNotifyPlugin::version() const
{
    return PLUGIN_VERSION;
}

QWidget* GmailNotifyPlugin::options()
{
    if (!enabled)
        return 0;

    options_ = new QWidget;
    ui_.setupUi(options_);
    restoreOptions();

    ui_.tb_check->setIcon(iconHost->getIcon("psi/play"));
    ui_.tb_open->setIcon(iconHost->getIcon("psi/browse"));
    ui_.tb_open_prog->setIcon(iconHost->getIcon("psi/browse"));

    connect(ui_.tb_check, SIGNAL(clicked()), SLOT(checkSound()));
    connect(ui_.tb_open, SIGNAL(clicked()), SLOT(getSound()));
    connect(ui_.cb_accounts, SIGNAL(currentIndexChanged(int)), SLOT(updateOptions(int)));
    connect(ui_.tb_open_prog, SIGNAL(clicked()), SLOT(getProg()));

    return options_;
}

bool GmailNotifyPlugin::enable()
{
    enabled = true;
    optionsApplingInProgress_ = false;
    id_.clear();
    accounts.clear();
    mailItems_.clear();
    actions_ = new ActionsList(this);
    connect(actions_, SIGNAL(changeNoSaveState(int,QString,bool)), this, SLOT(changeNoSaveState(int,QString,bool)));

    QFile f(":/icons/gmailnotify.png");
    if(f.open(QIODevice::ReadOnly))
        iconHost->addIcon("gmailnotify/menu", f.readAll());
    f.close();

    f.setFileName(":/icons/nohistory.png");
    if(f.open(QIODevice::ReadOnly))
        iconHost->addIcon("gmailnotify/nohistory", f.readAll());
    f.close();

    soundFile = psiOptions->getPluginOption(OPTION_SOUND, soundFile).toString();
    loadLists();

    int interval = psiOptions->getPluginOption(OPTION_INTERVAL, QVariant(4000)).toInt()/1000;
    popupId = popup->registerOption(POPUP_OPTION, interval, "plugins.options."+shortName()+"."+OPTION_INTERVAL);
    program_ = psiOptions->getPluginOption(OPTION_PROG).toString();

    //Update features
    bool end = false;
    int acc = 0;
    while(!end) {
        QString jid = accInfo->getJid(acc);
        if(jid == "-1") {
            end = true;
            continue;
        }
        QStringList l = jid.split("@");
        QString domain = l.last().split("/").first();
        QString id = stanzaSender->uniqueId(acc);
        id_.append(id);
        if(accInfo->getStatus(acc) != "offline")
            stanzaSender->sendStanza(acc, QString("<iq type='get' to='%1' id='%2' ><query xmlns='http://jabber.org/protocol/disco#info'/></iq>")
                     .arg(domain)
                     .arg(id));
        acc++;
    }

    return true;
}

bool GmailNotifyPlugin::disable()
{
    qDeleteAll(accounts);
    accounts.clear();

    delete actions_;
    actions_ = 0;

    delete mailViewer_;

    popup->unregisterOption(POPUP_OPTION);
    enabled = false;
    return true;
}

void GmailNotifyPlugin::applyOptions()
{
    if (!options_)
        return;

    optionsApplingInProgress_ = true;

    soundFile = ui_.le_sound->text();
    psiOptions->setPluginOption(OPTION_SOUND, soundFile);

    program_ = ui_.le_program->text();
    psiOptions->setPluginOption(OPTION_PROG, program_);

    int index = ui_.cb_accounts->currentIndex();
    if(accounts.size() <= index || index == -1)
        return;

    AccountSettings *as = findAccountSettings(ui_.cb_accounts->currentText());
    if(!as)
        return;

    as->notifyAllUnread = !ui_.rb_new_messages->isChecked();

    as->isMailEnabled = ui_.cb_mail->isChecked();
    as->isArchivingEnabled = ui_.cb_archiving->isChecked();
    as->isSuggestionsEnabled = ui_.cb_suggestions->isChecked();
    as->isSharedStatusEnabled = ui_.cb_shared_statuses->isChecked();
    as->isNoSaveEnbaled = ui_.cb_nosave->isChecked();

    Utils::updateSettings(as, stanzaSender, accInfo);

    if(as->isMailEnabled)
        Utils::requestMail(as, stanzaSender, accInfo);

    if(as->isSharedStatusEnabled)
        Utils::requestSharedStatusesList(as, stanzaSender, accInfo);

    if(as->isNoSaveEnbaled && as->isArchivingEnabled)
        Utils::updateNoSaveState(as, stanzaSender, accInfo);

    updateActions(as);

    saveLists();

    QTimer::singleShot(2000, this, SLOT(stopOptionsApply()));
}

void GmailNotifyPlugin::stopOptionsApply()
{
    optionsApplingInProgress_ = false;
}

void GmailNotifyPlugin::saveLists()
{
    QStringList l;
    foreach(AccountSettings *as, accounts)
        l.append(as->toString());
    psiOptions->setPluginOption(OPTION_LISTS, QVariant(l));
}

void GmailNotifyPlugin::loadLists()
{
    QStringList l = psiOptions->getPluginOption(OPTION_LISTS, QVariant()).toStringList();
    foreach(QString settings, l) {
        AccountSettings *as = new AccountSettings();
        as->fromString(settings);
        accounts.append(as);
    }
}

void GmailNotifyPlugin::restoreOptions()
{
    if (!options_ || optionsApplingInProgress_)
        return;

    ui_.lb_error->hide();
    ui_.gb_settings->setEnabled(true);
    ui_.cb_mail->setVisible(true);
    ui_.cb_shared_statuses->setVisible(true);
    ui_.cb_nosave->setVisible(true);
    ui_.le_sound->setText(soundFile);
    ui_.le_program->setText(program_);

    ui_.cb_accounts->setEnabled(true);
    ui_.cb_accounts->clear();
    if(!accounts.isEmpty()) {
        foreach(AccountSettings* as, accounts) {
            if(as->account != -1)
                ui_.cb_accounts->addItem(as->jid);
        }
    }

    if(!ui_.cb_accounts->count()) {
        ui_.cb_accounts->setEnabled(false);
        ui_.gb_mail_settings->setEnabled(false);
        ui_.gb_settings->setEnabled(false);
        ui_.lb_error->setVisible(true);
    }
    else {
        ui_.cb_accounts->setCurrentIndex(0);
        updateOptions(0);
    }
}

void GmailNotifyPlugin::updateOptions(int index)
{
    if (!options_ || index >= accounts.size() || index < 0)
        return;

    AccountSettings *as = findAccountSettings(ui_.cb_accounts->currentText());
    if(!as)
        return;

    ui_.cb_mail->setChecked(as->isMailEnabled);
    ui_.cb_mail->setVisible(as->isMailSupported);
    ui_.gb_mail_settings->setEnabled(ui_.cb_mail->isChecked());
    ui_.rb_new_messages->setChecked(!as->notifyAllUnread);
    ui_.rb_all_messages->setChecked(as->notifyAllUnread);

    ui_.cb_archiving->setChecked(as->isArchivingEnabled);
    ui_.cb_suggestions->setChecked(as->isSuggestionsEnabled);

    ui_.cb_shared_statuses->setChecked(as->isSharedStatusEnabled);
    ui_.cb_shared_statuses->setVisible(as->isSharedStatusSupported);

    ui_.cb_nosave->setChecked(as->isNoSaveEnbaled);
    ui_.cb_nosave->setVisible(as->isNoSaveSupported);
    ui_.cb_nosave->setEnabled(ui_.cb_archiving->isChecked());
}

bool GmailNotifyPlugin::incomingStanza(int account, const QDomElement& stanza)
{
    if (enabled) {
        if (stanza.tagName() == "iq") {
            QDomElement query = stanza.firstChild().toElement();
            if (!query.isNull()) {
                if(checkFeatures(account, stanza, query))
                    return true;

                if(checkEmail(account, stanza, query))
                    return true;

                if(checkSettings(account, stanza, query))
                    return true;

                if(checkSharedStatus(account, stanza, query))
                    return true;

                if(checkNoSave(account, stanza, query))
                    return true;

                if(checkAttributes(account, stanza, query))
                    return true;
            }
        }
        else if(stanza.tagName() == "message") {
            QDomElement x = stanza.firstChildElement("x");
            if(!x.isNull() && x.attribute("xmlns") == "google:nosave") {
                QString jid = stanza.attribute("from").split("/").first();
                bool val = (x.attribute("value") == "enabled");
                AccountSettings *as = findAccountSettings(accInfo->getJid(account));
                if(as && as->noSaveList.contains(jid)
                    && as->noSaveList.value(jid) != val)
                {
                    as->noSaveList.insert(jid, val);
                    showPopup(tr("No-save state for contact %1 is changed").arg(jid));
                    return true;
                }
            }
        }
    }
    return false;
}

bool GmailNotifyPlugin::outgoingStanza(int account, QDomElement& stanza)
{
    if(enabled && hasAccountSettings(account)) {
        if(stanza.tagName() == "presence") {
            AccountSettings* as = findAccountSettings(accInfo->getJid(account));
            if(as && as->account == account && as->isSharedStatusEnabled && as->isSharedStatusSupported) {
                QString status = accInfo->getStatus(account);
                QString message = accInfo->getStatusMessage(account);
                if(message.length() > as->statusMax)
                    message.chop(message.length() - as->statusMax);
                if(status == as->status && message == as->message) {
                    return false;
                }
                as->message = message;
                as->status = status;
                qRegisterMetaType<AccountSettings*>();
                QMetaObject::invokeMethod(this, "updateSharedStatus", Qt::QueuedConnection, Q_ARG(AccountSettings*, as));
            }
        }
    }
    return false;
}

bool GmailNotifyPlugin::checkFeatures(int account, const QDomElement &stanza, const QDomElement& query)
{
    bool myReqyest = false;
    if (stanza.attribute("type") == "result"
         && query.tagName() == "query"
         && query.attribute("xmlns") == "http://jabber.org/protocol/disco#info")
    {
        if(id_.contains(stanza.attribute("id"))) {
            id_.removeAll(stanza.attribute("id"));
            myReqyest = true;
        }
        bool foundGoogleExt = false;

        const QString from = stanza.attribute("from").toLower();
        if(from.contains("@")) { // нам нужен disco#info от сервера, а не от клиента.
            return false;
        }
        const QString jid = accInfo->getJid(account);
        QStringList tmpList = jid.split("@");
        const QString domain = tmpList.last().split("/").first();
        if(domain != from) { // нам нужен disco#info от нашего сервера
            return false;
        }
        QString fullJid = stanza.attribute("to");
        for (QDomNode child = query.firstChild(); !child.isNull(); child = child.nextSibling()) {
            QDomElement feature = child.toElement();
            if(feature.isNull() || feature.tagName() != "feature")
                continue;

            //If the server supports the Gmail extension
            if(feature.attribute("var") == "google:mail:notify" && feature.attribute("node").isEmpty()) {
                AccountSettings *as = create(account, jid);
                as->isMailSupported = true;
                //Utils::requestMail(as, stanzaSender, accInfo);
                foundGoogleExt = true;
            }
            else if(feature.attribute("var") == "google:setting" && feature.attribute("node").isEmpty()) {
                AccountSettings *as = create(account, jid);
                Utils::getUserSettings(as, stanzaSender, accInfo);
                foundGoogleExt = true;
            }
            else if(feature.attribute("var") == "google:shared-status" && feature.attribute("node").isEmpty()) {
                AccountSettings *as = create(account, jid);
                as->isSharedStatusSupported = true;
                as->status = accInfo->getStatus(account);
                as->message = accInfo->getStatusMessage(account);
                as->fullJid = fullJid;
                if(as->isSharedStatusEnabled)
                    Utils::requestSharedStatusesList(as, stanzaSender, accInfo);
                foundGoogleExt = true;
            }
            else if(feature.attribute("var") == "google:nosave" && feature.attribute("node").isEmpty()) {
                AccountSettings *as = create(account, jid);
                as->isNoSaveSupported = true;
                updateActions(as);
                if(as->isNoSaveEnbaled)
                    Utils::updateNoSaveState(as, stanzaSender, accInfo);
                foundGoogleExt = true;
            }
            else if(feature.attribute("var") == "google:roster" && feature.attribute("node").isEmpty()) {
                AccountSettings *as = create(account, jid);
                as->isAttributesSupported = true;
                if(as->isAttributesEnabled)
                    Utils::requestExtendedContactAttributes(as, stanzaSender, accInfo);
                foundGoogleExt = true;
            }

        }
        if(foundGoogleExt) {
            optionsApplingInProgress_ = false;
            restoreOptions();
        }
    }
    return myReqyest;
}

bool GmailNotifyPlugin::checkEmail(int account, const QDomElement &stanza, const QDomElement& query)
{
    if (stanza.attribute("type") == "set"
        && query.tagName() == "new-mail"
        && query.attribute("xmlns") == "google:mail:notify")
    {
        //Server reports new mail
        //send success result
        QString from = stanza.attribute("to");
        QString to = from.split("/").at(0);
        QString iqId = stanza.attribute("id");
        QString reply = QString("<iq type='result' from='%1' to='%2' id='%3' />").arg(from,to,iqId);
        stanzaSender->sendStanza(account, reply);
        AccountSettings *as = findAccountSettings(to.toLower());
        if(!as || as->account != account) {
            return true;
        }

        //requests new mail
        Utils::requestMail(as, stanzaSender, accInfo);
        //block stanza processing
        return true;
    }
    else if(stanza.attribute("type") == "result"
        && query.tagName() == "mailbox"
        && query.attribute("xmlns") == "google:mail:notify")
    {
        //Email Query Response
        const QString jid = stanza.attribute("to").split("/").at(0);
        const QString server = stanza.attribute("from").toLower();
        if(!server.isEmpty() && jid.toLower() != server) {
            return false;
        }
        const QString from = stanza.attribute("to");
        AccountSettings *as = findAccountSettings(jid);
        if(!as || as->account != account) {
            return true;
        }

        as->lastMailTime = query.attribute("result-time");
        QDomElement lastmail = query.firstChildElement("mail-thread-info");
        if (!lastmail.isNull())
            as->lastMailTid = lastmail.attribute("tid");

        //save last check values
        saveLists();

        incomingMail(account, query);
        return true;
    }
    return false;
}

bool GmailNotifyPlugin::checkSettings(int account, const QDomElement &stanza, const QDomElement& query)
{
    bool foundSettings = false;
    if ( (stanza.attribute("type") == "result"
         || stanza.attribute("type") == "set")
        && query.tagName() == "usersetting"
        && query.attribute("xmlns") == "google:setting")
    {
        foundSettings = true;
        //id_.removeAll(stanza.attribute("id"));
        const QString jid = stanza.attribute("to").split("/").at(0);
        const QString server = stanza.attribute("from").toLower();
        if(!server.isEmpty() && jid.toLower() != server) {
            return false;
        }
        AccountSettings *as = findAccountSettings(jid.toLower());
        if(!as || as->account != account) {
            return true;
        }
        for (QDomNode child = query.firstChild(); !child.isNull(); child = child.nextSibling()) {
            QDomElement setting = child.toElement();
            QString value = setting.attribute("value");
            if(setting.isNull() || value.isEmpty())
                continue;

            if(setting.tagName() == "autoacceptsuggestions")
                as->isSuggestionsEnabled = (value == "true");
            else if(setting.tagName() == "mailnotifications") {
                as->isMailEnabled = (value == "true");
                Utils::requestMail(as, stanzaSender, accInfo);
            }
            else if(setting.tagName() == "archivingenabled") {
                as->isArchivingEnabled = (value == "true");
                updateActions(as);
            }
        }
        restoreOptions();

        if(stanza.attribute("type") == "set") {
            showPopup(tr("Settings for an account %1 are changed").arg(jid));
            const QString str = QString("<iq to='%1' type='result' id='%2' />").arg(accInfo->getJid(account), stanza.attribute("id"));
            stanzaSender->sendStanza(account, str);
        }
    }

    return foundSettings;
}

bool GmailNotifyPlugin::checkSharedStatus(int account, const QDomElement &stanza, const QDomElement &query)
{
    bool found = false;
    if (query.tagName() == "query"
        && query.attribute("xmlns") == "google:shared-status") {
        found = true;
        const QString jid = stanza.attribute("to").split("/").at(0);
        const QString server = stanza.attribute("from").toLower();
        if(!server.isEmpty() && jid.toLower() != server) {
            return false;
        }
        AccountSettings *as = findAccountSettings(jid);
        if(!as || as->account != account) {
            return true;
        }
        QString type = stanza.attribute("type");
        if(type == "set")
            as->sharedStatuses.clear();
        if(query.hasAttribute("status-max"))
            as->statusMax = query.attribute("status-max").toInt();
        if(query.hasAttribute("status-list-contents-max"))
            as->listContentsMax = query.attribute("status-list-contents-max").toInt();
        if(query.hasAttribute("status-list-max"))
            as->listMax = query.attribute("status-list-max").toInt();

        if(type == "result" || type == "set") {
            for (QDomNode child = query.firstChild(); !child.isNull(); child = child.nextSibling()) {
                QDomElement settings = child.toElement();
                if(settings.isNull())
                    continue;
                QString tagName = settings.tagName();
                if(tagName == "status")
                    as->message = settings.text();
                else if(tagName == "show")
                    as->status = settings.text().replace("default", "online");
                else if(tagName == "status-list") {
                    QStringList l;
                    for (QDomNode child = settings.firstChild(); !child.isNull(); child = child.nextSibling()) {
                        QDomElement st = child.toElement();
                        if(st.isNull() || st.tagName() != "status")
                            continue;

                        l.append(st.text());
                    }
                    if(!l.isEmpty())
                        as->sharedStatuses.insert(settings.attribute("show").replace("default", "online"), l);
                }
            }
        }

        if(as->sharedStatuses.isEmpty())
            as->sharedStatuses.insert(as->status, QStringList(as->message));


        if(as->isSharedStatusEnabled) {
            //timer_->start();
            accountController->setStatus(account, as->status, as->message);
            showPopup(tr("Shared Status for an account %1 is updated").arg(jid));
        }
        if(type == "set") {
            const QString str = QString("<iq to='%1' type='result' id='%2' />").arg(accInfo->getJid(account), stanza.attribute("id"));
            stanzaSender->sendStanza(account, str);
        }
    }

    return found;
}

bool GmailNotifyPlugin::checkNoSave(int account, const QDomElement &stanza, const QDomElement &query)
{
    bool found = false;
    if(query.tagName() == "query"
       && query.attribute("xmlns") == "google:nosave")
    {
        found = true;
        const QString jid = stanza.attribute("to").split("/").at(0);
        const QString server = stanza.attribute("from").toLower();
        if(!server.isEmpty() && jid.toLower() != server) {
            return false;
        }
        AccountSettings *as = findAccountSettings(jid);
        if(!as || as->account != account) {
            return true;
        }

        const QString type = stanza.attribute("type");
        for(QDomNode child = query.firstChild(); !child.isNull(); child = child.nextSibling()) {
            QDomElement noSave = child.toElement();
            if(noSave.isNull() || noSave.tagName() != "item")
                continue;

            const QString item = noSave.attribute("jid");
            bool state = (noSave.attribute("value") == "enabled");
            bool changed;
            if(!as->noSaveList.contains(item)) {
                changed = true;
            }
            else {
                changed = as->noSaveList.value(item) != state;
            }

            if(changed) {
                as->noSaveList.insert(item, state);
                actions_->updateAction(account, item, state);

                if(type == "set") {
                    showPopup(tr("No-save state for contact %1 is changed").arg(item));
                }
            }

            if(type == "set") {
                const QString str = QString("<iq to='%1' type='result' id='%2' />").arg(accInfo->getJid(account), stanza.attribute("id"));
                stanzaSender->sendStanza(account, str);
            }
        }
    }

    return found;
}

bool GmailNotifyPlugin::checkAttributes(int account, const QDomElement &stanza, const QDomElement &query)
{
    bool found = false;
    if(query.tagName() == "query"
       && query.attribute("xmlns") == "jabber:iq:roster"
       && query.attribute("ext") == "2")
    {
        const QString jid = stanza.attribute("to").split("/").at(0);
        const QString server = stanza.attribute("from").toLower();
        if(!server.isEmpty() && jid.toLower() != server) {
            return false;
        }
        AccountSettings *as = findAccountSettings(jid);
        if(!as || as->account != account) {
            return true;
        }

        found = true;

        const QString type = stanza.attribute("type");
        if(type == "set") {
            const QString str = QString("<iq to='%1' type='result' id='%2' />").arg(accInfo->getJid(account), stanza.attribute("id"));
            stanzaSender->sendStanza(account, str);
        }

        for(QDomNode child = query.firstChild(); !child.isNull(); child = child.nextSibling()) {
            QDomElement itemElem = child.toElement();
            if(itemElem.isNull() || itemElem.tagName() != "item")
                continue;

            const QString jid = itemElem.attribute("jid");
            const QString descr = itemElem.attribute("t");

            Attributes atr;
            if(as->attributes.contains(jid)) {
                atr = as->attributes.value(jid);
            }
            if(atr.t != descr && type == "set") {
                showPopup(tr("Attributes for contact %1 are changed").arg(jid));
            }
            atr.t = descr;
            as->attributes.insert(jid, atr);
        }
    }

    return found;
}

AccountSettings* GmailNotifyPlugin::findAccountSettings(const QString &jid)
{
    if(!jid.isEmpty()) {
        foreach(AccountSettings* as, accounts) {
            if(as->jid == jid.toLower())
                return as;
        }
    }

    return 0;
}

AccountSettings* GmailNotifyPlugin::create(int account, QString jid)
{
    jid = jid.toLower();
    if(jid.contains("/"))
        jid = jid.split("/").first();

    AccountSettings *as = findAccountSettings(jid);
    if(!as) {
        as = new AccountSettings(account, jid);
        accounts.append(as);
    }
    else
        as->account = account;

    return as;
}

void GmailNotifyPlugin::changeNoSaveState(int account, QString jid, bool val)
{
    if(!Utils::checkAccount(account, accInfo))
        return;

    QString str = QString("<iq type='set' to='%1' id='%2'>"
                "<query xmlns='google:nosave'>"
                "<item xmlns='google:nosave' jid='%3' value='%4'/>"
                "</query></iq>")
            .arg(accInfo->getJid(account), stanzaSender->uniqueId(account))
            .arg(jid, (val ? "enabled" : "disabled"));

    stanzaSender->sendStanza(account, str);
//    AccountSettings *as = findAccountSettings(accInfo->getJid(account));
//    if(as) {
//        as->noSaveList.insert(jid, (val ? "enabled" : "disabled"));
//    }
}

bool GmailNotifyPlugin::hasAccountSettings(int account)
{
    bool has = false;
    foreach(AccountSettings *as, accounts) {
        if(as->account == account) {
            has = true;
            break;
        }
    }

    return has;
}

void GmailNotifyPlugin::setStanzaSendingHost(StanzaSendingHost *host)
{
    stanzaSender = host;
}

void GmailNotifyPlugin::setAccountInfoAccessingHost(AccountInfoAccessingHost* host)
{
    accInfo = host;
}

void GmailNotifyPlugin::setOptionAccessingHost(OptionAccessingHost* host)
{
    psiOptions = host;
}

void GmailNotifyPlugin::setPopupAccessingHost(PopupAccessingHost *host)
{
    popup = host;
}

void GmailNotifyPlugin::setPsiAccountControllingHost(PsiAccountControllingHost *host)
{
    accountController = host;
}

void GmailNotifyPlugin::setIconFactoryAccessingHost(IconFactoryAccessingHost *host)
{
    iconHost = host;
}

void GmailNotifyPlugin::setEventCreatingHost(EventCreatingHost *host)
{
    psiEvent = host;
}

void GmailNotifyPlugin::setSoundAccessingHost(SoundAccessingHost *host)
{
    sound_ = host;
}

void GmailNotifyPlugin::showPopup(const QString& text)
{
    int interval = popup->popupDuration(POPUP_OPTION);
    if(!interval)
        return;

    popup->initPopup(text, name(), "gmailnotify/menu", popupId);
}

void GmailNotifyPlugin::updateSharedStatus(AccountSettings* as)
{
    if(as->sharedStatuses.contains(as->status)) {
        QStringList l = as->sharedStatuses.value(as->status);
        if(l.contains(as->message)) {
            l.removeAll(as->message);
        }
        l.push_front(as->message);
        while(l.size() > as->listContentsMax) {
            l.removeLast();
        }
        as->sharedStatuses.insert(as->status, l);
    }
    else {
        as->sharedStatuses.insert(as->status, QStringList() << as->message);
        while(as->sharedStatuses.size() > as->listMax) {
            foreach(QString key, as->sharedStatuses.keys()) {
                if(key != as->status) {
                    as->sharedStatuses.remove(key);
                    break;
                }
            }
        }
    }
    Utils::updateSharedStatus(as, stanzaSender, accInfo);
}

QList < QVariantHash > GmailNotifyPlugin::getButtonParam()
{
    return QList < QVariantHash >();
}

QAction* GmailNotifyPlugin::getAction(QObject* parent, int account, const QString& contact)
{
    const QString bareJid = contact.split("/").first();
    QAction *act = actions_->newAction(parent, account, bareJid, iconHost->getIcon("gmailnotify/nohistory"));
    AccountSettings* as = findAccountSettings(accInfo->getJid(account));
    if(as) {
        act->setVisible(as->isNoSaveEnbaled && as->isNoSaveSupported && as->isArchivingEnabled);
        if(as->noSaveList.contains(bareJid))
            act->setChecked(as->noSaveList.value(bareJid));
    }
    return act;
}

void GmailNotifyPlugin::updateActions(AccountSettings *as)
{
    bool val = as->isNoSaveEnbaled && as->isNoSaveSupported && as->isArchivingEnabled;
    actions_->updateActionsVisibility(as->account, val);
}

void GmailNotifyPlugin::incomingMail(int account, const QDomElement &xml)
{
    MailItemsList l;
    QDomElement mail = xml.firstChildElement("mail-thread-info");
    while(!mail.isNull()) {
        MailItem mi;
        mi.url = mail.attribute("url");
        mi.subject = mail.firstChildElement("subject").text();
        mi.text = mail.firstChildElement("snippet").text();
        mi.account = accInfo->getJid(account);
        QDomElement senders = mail.firstChildElement("senders");
        QDomElement sender = senders.firstChildElement("sender");
        QStringList fl;
        while(!sender.isNull()) {
            QString from = sender.attribute("name") + " <" + sender.attribute("address") + ">";
            fl.append(from);
            sender = sender.nextSiblingElement("sender");
        }
        fl.removeDuplicates();
        mi.from = fl.join(", ");
        l.append(mi);
        mail = mail.nextSiblingElement("mail-thread-info");
    }
    if(l.isEmpty()) {
        return;
    }

    mailItems_.append(l);
    bool soundEnabled = psiOptions->getGlobalOption("options.ui.notifications.sounds.enable").toBool();
    if(soundEnabled) {
        psiOptions->setGlobalOption("options.ui.notifications.sounds.enable", false);
        playSound(soundFile);
    }

    QString popupMessage = tr("<b>mail.google.com - incoming mail!</b>");
    if(psiOptions->getGlobalOption("options.ui.notifications.passive-popups.showMessage").toBool()) {
        popupMessage += "<br><br>";
        foreach(const MailItem& i, l)
            popupMessage += ViewMailDlg::mailItemToText(i).replace("\n", "<br>") + "<br>";
    }
    if(mailViewer_ && mailViewer_->isActiveWindow()) {
        showPopup(popupMessage);
        mailEventActivated();

    }
    else {
        if(mailItems_.count() > 1) {
            showPopup(popupMessage);
        }
        else {
            psiEvent->createNewEvent(account, accInfo->getJid(account), popupMessage, this, SLOT(mailEventActivated()));
        }

        if(mailViewer_) {
            mailViewer_->setWindowTitle("*" + mailViewer_->caption());
        }
    }
    psiOptions->setGlobalOption("options.ui.notifications.sounds.enable", soundEnabled);

    if(!program_.isEmpty()) {
        QStringList prog = program_.split(" ");
        QProcess *p = new QProcess(this);
        p->startDetached(prog.takeFirst(), prog);
        p->deleteLater();
    }
}

void GmailNotifyPlugin::mailEventActivated()
{
    if(mailItems_.isEmpty()) {
        return;
    }

    if(!mailViewer_) {
        mailViewer_ = new ViewMailDlg(mailItems_.takeFirst(), iconHost);
    }

    while(!mailItems_.isEmpty()) {
        mailViewer_->appendItems(mailItems_.takeFirst());
    }

    mailViewer_->show();
    mailViewer_->raise();
    mailViewer_->activateWindow();
}

void GmailNotifyPlugin::playSound(const QString &file)
{
    sound_->playSound(file);
}

void GmailNotifyPlugin::checkSound()
{
    playSound(ui_.le_sound->text());
}

void GmailNotifyPlugin::getSound()
{
    QString fileName = QFileDialog::getOpenFileName(0,tr("Choose a sound file"),"", tr("Sound (*.wav)"));
    if(fileName.isEmpty())
        return;
    ui_.le_sound->setText(fileName);
}

void GmailNotifyPlugin::getProg()
{
    QString fileName = QFileDialog::getOpenFileName(0,tr("Choose a program"),"","");
    if(fileName.isEmpty())
        return;
    ui_.le_program->setText(fileName);
}

QAction* GmailNotifyPlugin::getContactAction(QObject *parent, int account, const QString &contact)
{
    QAction *act = 0;
    AccountSettings* as = findAccountSettings(accInfo->getJid(account));
    if(as && as->isAttributesEnabled && as->isAttributesSupported) {
        act = new QAction(iconHost->getIcon("psi/stop"), tr("Block gmail contact"), parent);
        act->setCheckable(true);
        if(as->attributes.contains(contact) && as->attributes.value(contact).t == "B")
            act->setChecked(true);
        act->setProperty("jid", contact);
        act->setProperty("account", account);
        connect(act, SIGNAL(triggered(bool)), SLOT(blockActionTriggered(bool)));
    }

    return act;
}

void GmailNotifyPlugin::blockActionTriggered(bool block)
{
    QAction* act = static_cast<QAction*>(sender());
    const QString jid = act->property("jid").toString();
    int acc = act->property("account").toInt();
    QString str = QString("<iq type='set' id='%1'>"
                "<query xmlns='jabber:iq:roster' xmlns:gr='google:roster' gr:ext='2'>"
                "<item jid='%2' gr:t='%3'/></query></iq>")
            .arg(stanzaSender->uniqueId(acc))
            .arg(jid, block ? "B" : "");
    stanzaSender->sendStanza(acc, str);
}

QString GmailNotifyPlugin::pluginInfo()
{
    return tr("Authors: ") +  "VampiRUS\nDealer_WeARE\n\n"
            + trUtf8("Shows notifications of new messages in your Gmailbox.\n"
             "Note: The plugin only checks the root of your Inbox folder in your"
             " Gmailbox for new messages. When using server side mail filtering, you may not be notified about all new messages.");
}

QPixmap GmailNotifyPlugin::icon() const
{
    return QPixmap(":/icons/gmailnotify.png");
}
