/*
 * activeprofiles_dbus.cpp - Class for interacting with other psi instances
 * Copyright (C) 2006-2007  Martin Hostettler
 *
 * 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 "activeprofiles.h"

#include <QCoreApplication>
#include <QString>
#include <QStringList>
#include <QByteArray>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusInterface>


#include <QLabel>
#include <QTimer>


#include "applicationinfo.h"
#include "dbus.h"





/** \brief encodes a string to "[A-Z][a-z][0-9]_-" ascii subset
 * [A-Z][a-z][0-9] -> [A-Z][a-z][0-9]
 * / -> _
 * everything else to "-XX" with XX hex code of char
 * (slow)
 */
static QString encodeAlNumD(QString in)
{
    QString out;
    QByteArray chars = in.toUtf8();
    bool first = true;
    foreach(char c, chars) {
        if (('A' <= c) && (c <= 'z')) {
            out += (char)c;
        } else if (('0' <= c) && (c <= '9') && !first) {
            out += (char)c;
        } else if ('/' == c) {
            out += "_";
        } else {
            out += QString("-%1").arg(c, 2, 16, QChar('0'));
        }
        first = false;
    }
    return out;
}


/** \brief DBus busname registration helper
 * \param dbusIface interface of bus
 * \param name busname to register
 * \param queue try queueing?
 * \return got dbus name?
 */
static bool registerBusname(QDBusConnectionInterface *dbusIface, QString name, bool queue)
{
    bool ok = false;
    QDBusReply<QDBusConnectionInterface::RegisterServiceReply> reply;
    reply = dbusIface->registerService(name,
        queue ?    QDBusConnectionInterface::QueueService : QDBusConnectionInterface::DontQueueService,
            QDBusConnectionInterface::AllowReplacement);
    if (reply.isValid()) {
        switch (reply.value()) {
            case QDBusConnectionInterface::ServiceNotRegistered:
                qWarning("failed to register dbus name %s:", qPrintable(name));
                break;
            case QDBusConnectionInterface::ServiceQueued:
                qDebug("dbus name %s already taken, queueing", qPrintable(name));
                break;
            case QDBusConnectionInterface::ServiceRegistered:
                ok = true;
        }
    } else {
        qWarning("failed to register dbus name %s: %s", qPrintable(name), qPrintable(reply.error().message()));
    }
    return ok;
}


class ActiveProfiles::Private {
public:
    QString profile;
    QStringList busNames;
    bool registerBusnames(QString prof);

    QString dbusName(QString prof);
};


QString ActiveProfiles::Private::dbusName(QString prof)
{
    QString name = PSIDBUSNAME;
    name += ".";
    name += encodeAlNumD(ApplicationInfo::homeDir(ApplicationInfo::ConfigLocation)).right(qMax(0,200-name.size()));
    if (!prof.isEmpty()) {
        name += ".";
        name += encodeAlNumD(prof).right(qMax(0,250-name.size()));
    }
    return name;
}



bool ActiveProfiles::Private::registerBusnames(QString prof)
{
    // FIXME move where?
    if (!QDBusConnection::sessionBus().isConnected()) {
        qWarning("can't connect to dbus");
        return true;
    }


    QDBusConnectionInterface *dbusIface = QDBusConnection::sessionBus().interface ();

    QString name = PSIDBUSNAME;
    registerBusname(dbusIface, name, true);
    busNames << name;
    name = dbusName(QString());
    registerBusname(dbusIface, name, true);
    busNames << name;
    name = dbusName(prof);
    busNames << name;
    return registerBusname(dbusIface, name, false);

}


bool ActiveProfiles::isActive(const QString &profile) const
{
    if (!QDBusConnection::sessionBus().isConnected()) {
        qWarning("can't connect to dbus");
        return false;
    }

    QDBusConnectionInterface *dbusIface = QDBusConnection::sessionBus().interface ();
    return dbusIface->isServiceRegistered(d->dbusName(profile));
}

bool ActiveProfiles::isAnyActive() const
{
    return isActive("");
}


bool ActiveProfiles::setThisProfile(const QString &profile)
{
    if (profile == d->profile)
        return true;

    if (profile.isEmpty()) {
        unsetThisProfile();
        return true;
    }
    bool ok = d->registerBusnames(profile);
    if (ok) {
        d->profile = profile;
    } else {
        unsetThisProfile();
    }
    return ok;
}

void ActiveProfiles::unsetThisProfile()
{
    QDBusConnectionInterface *dbusIface = QDBusConnection::sessionBus().interface ();
    foreach(QString name, d->busNames) {
        dbusIface->unregisterService(name);
    }
    d->busNames.clear();
    d->profile = QString::null;
}

QString ActiveProfiles::thisProfile() const
{
    return d->profile;
}

ActiveProfiles::ActiveProfiles()
    : QObject(QCoreApplication::instance())
{
    d = new ActiveProfiles::Private;
}

ActiveProfiles::~ActiveProfiles()
{
    delete d;
    d = 0;
}

bool ActiveProfiles::setStatus(const QString &profile, const QString &status, const QString &message) const
{
    QDBusInterface(d->dbusName(profile), "/Main", PSIDBUSMAINIF).call(QDBus::NoBlock,
            "setStatus", status, message);
    return true;
}

bool ActiveProfiles::openUri(const QString &profile, const QString &uri) const
{
    QDBusInterface(d->dbusName(profile), "/Main", PSIDBUSMAINIF).call(QDBus::NoBlock,
            "openURI", uri);
    return true;
}

bool ActiveProfiles::raise(const QString &profile, bool withUI) const
{
    QLabel *lab=0;
    QDBusMessage msg = QDBusMessage::createMethodCall ( d->dbusName(profile), "/Main", PSIDBUSMAINIF, "raise" );
    if (withUI) {
        lab = new QLabel(tr("This psi profile is already running...<br>please wait..."));
        QTimer::singleShot(250, lab, SLOT(show()));
    }
    QDBusMessage rmsg = QDBusConnection::sessionBus().call(msg, QDBus::BlockWithGui, 10000);
    if (withUI) {
        lab->hide();
        delete lab;
    }
    if (rmsg.type() == QDBusMessage::ReplyMessage) {
        return true;
    } else return false;
}
