/*

Conversation widget for kdm greeter

Copyright (C) 2008 Dirk Mueller <mueller@kde.org>
Copyright (C) 2008 Oswald Buddenhagen <ossi@kde.org>

based on classic kdm greeter:

  Copyright (C) 1997, 1998, 2000 Steffen Hansen <hansen@kde.org>
  Copyright (C) 2000-2003 Oswald Buddenhagen <ossi@kde.org>

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 "kgreet_generic.h"

#include <kglobal.h>
#include <klocale.h>
#include <klineedit.h>
#include <kuser.h>

#include <QLayout>
#include <QLabel>
#include <QTextDocument>

extern KDE_EXPORT KGreeterPluginInfo kgreeterplugin_info; // defined at bottom

static int echoMode;

class KDMPasswordEdit : public KLineEdit {
public:
    KDMPasswordEdit(QWidget *parent = 0) : KLineEdit(parent)
    {
        if (::echoMode == -1)
            setPasswordMode(true);
        else
            setEchoMode(::echoMode ? Password : NoEcho);
        setContextMenuPolicy(Qt::NoContextMenu);
    }
};

KGenericGreeter::KGenericGreeter(KGreeterPluginHandler *_handler,
                                 QWidget *parent,
                                 const QString &_fixedEntity,
                                 Function _func, Context _ctx) :
    QObject(),
    KGreeterPlugin(_handler),
    m_lineEdit(0),
    fixedUser(_fixedEntity),
    func(_func),
    ctx(_ctx),
    exp(-1),
    running(false)
{
    m_parentWidget = new QWidget(parent);
    m_parentWidget->setObjectName("talker");
    // XXX set some minimal size
    widgetList << m_parentWidget;
    m_grid = new QGridLayout(m_parentWidget);
    m_grid->setMargin(0);

    if (ctx == ExUnlock || ctx == ExChangeTok)
        fixedUser = KUser().loginName();
}

// virtual
KGenericGreeter::~KGenericGreeter()
{
    abort();
    delete m_parentWidget;
}

void // virtual
KGenericGreeter::loadUsers(const QStringList &users)
{
    m_users = users;
}

void // virtual
KGenericGreeter::presetEntity(const QString &entity, int /* field */)
{
    // assert(!running);
    curUser = entity;
}

QString // virtual
KGenericGreeter::getEntity() const
{
    return fixedUser.isEmpty() ? curUser : fixedUser;
}

void // virtual
KGenericGreeter::setUser(const QString &user)
{
    // assert(running);
    // assert(fixedUser.isEmpty());
    if (!(kgreeterplugin_info.flags & KGreeterPluginInfo::Presettable))
        return; // Not interested in PAM telling us who logged in
    if (exp) {
        abort();
        start();
    }
    curUser = user;
    if (m_lineEdit) { // could be null if plugin is misconfigured
        m_lineEdit->setText(user);
        m_lineEdit->selectAll();
        m_lineEdit->setFocus();
    }
}

void // virtual
KGenericGreeter::setEnabled(bool enable)
{
    // assert(func == Authenticate && ctx == Shutdown);
    // XXX this is likely to bear some bogosity
    foreach (QWidget *w, m_children)
        w->setEnabled(enable);
    if (enable && m_lineEdit)
        m_lineEdit->setFocus();
}

bool // virtual
KGenericGreeter::textMessage(const char *text, bool err)
{
    if (err)
        return false;

    if (m_infoMsgs.isEmpty())
        revive();
    QString qtext = QString::fromUtf8(text);
    m_infoMsgs.append(qtext);
    QLabel *label = new QLabel(qtext, m_parentWidget);
    m_grid->addWidget(label, m_line++, 0, 1, 2);
    m_children.append(label);

    return true;
}

void // virtual
KGenericGreeter::textPrompt(const char *prompt, bool echo, bool /* nonBlocking */)
{
    exp =
        exp >= 0 ||
        func != Authenticate ||
        !(kgreeterplugin_info.flags & KGreeterPluginInfo::Presettable);

    if (!exp && !fixedUser.isEmpty()) {
        handler->gplugReturnText(fixedUser.toLocal8Bit(),
                                 KGreeterPluginHandler::IsUser);
        return;
    }

    if (m_infoMsgs.isEmpty())
        revive();
    else
        m_infoMsgs.clear();

    QLabel *label = new QLabel(QString::fromUtf8(prompt).trimmed());
    m_grid->addWidget(label, m_line, 0);
    m_children.append(label);
    m_echo = echo;
    if (echo) {
        m_lineEdit = new KLineEdit;
        m_lineEdit->setContextMenuPolicy(Qt::NoContextMenu);
        if (!exp) {
            if (!m_users.isEmpty()) {
                KCompletion *userNamesCompletion = new KCompletion;
                userNamesCompletion->setItems(m_users);
                m_lineEdit->setCompletionObject(userNamesCompletion);
                m_lineEdit->setAutoDeleteCompletionObject(true);
                m_lineEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
            }
            if (!curUser.isEmpty()) {
                m_lineEdit->setText(curUser);
                m_lineEdit->selectAll();
                connect(m_lineEdit, SIGNAL(selectionChanged()), SLOT(slotChanged()));
            }
            connect(m_lineEdit, SIGNAL(editingFinished()), SLOT(slotLoginLostFocus()));
        }
        connect(m_lineEdit, SIGNAL(editingFinished()), SLOT(slotChanged()));
        connect(m_lineEdit, SIGNAL(textChanged(QString)), SLOT(slotChanged()));
    } else {
        m_lineEdit = new KDMPasswordEdit;
    }
    m_lineEdit->setMinimumWidth(
        m_lineEdit->fontMetrics().width("This is a long password"));
    m_grid->addWidget(m_lineEdit, m_line, 1);
    m_children.append(m_lineEdit);
    m_lineEdit->show();
    m_lineEdit->setFocus();
}

bool // virtual
KGenericGreeter::binaryPrompt(const char *, bool)
{
    // FIXME
    return true;
}

void // virtual
KGenericGreeter::start()
{
    exp = -1;
    running = true;
    handler->gplugStart();
}

void // virtual
KGenericGreeter::suspend()
{
}

void // virtual
KGenericGreeter::resume()
{
}

void // virtual
KGenericGreeter::next()
{
    if (m_lineEdit) {
        m_lineEdit->setEnabled(false);
        QString text = m_lineEdit->text();
        m_lineEdit = 0;
        handler->gplugReturnText(text.toLocal8Bit(),
                                 !m_echo ?
                                     KGreeterPluginHandler::IsSecret :
                                     !exp ?
                                         KGreeterPluginHandler::IsUser : 0);
    }
}

void // virtual
KGenericGreeter::abort()
{
    running = false;
    if (exp >= 0) {
        exp = -1;
        handler->gplugReturnText(0, 0);
    }
}

void // virtual
KGenericGreeter::succeeded()
{
    failed(); // redefining terms :-D
}

void // virtual
KGenericGreeter::failed()
{
    // assert(running || timed_login);
    if (!m_infoMsgs.isEmpty()) {
        QString text = "<qt>";
        foreach (const QString &msg, m_infoMsgs)
            text += "<p>" + Qt::escape(msg) + "</p>";
        text += "</qt>";
        revive();
        handler->gplugMsgBox(QMessageBox::Information, text);
    } else {
        foreach (QWidget *w, m_children)
            w->setEnabled(false);
    }
    exp = -1;
    running = false;
}

void // virtual
KGenericGreeter::revive()
{
    // assert(!running);
    foreach (QWidget *w, m_children)
        w->deleteLater();
    m_children.clear();
    m_infoMsgs.clear();
    m_lineEdit = 0;
    m_line = 0;
}

void // virtual
KGenericGreeter::clear()
{
    // assert(!running && !passwd1Edit);
    revive();
    curUser = QString::null;
}


// private
void
KGenericGreeter::slotLoginLostFocus()
{
    m_lineEdit->setText(m_lineEdit->text().trimmed());
    if (curUser != m_lineEdit->text()) {
        curUser = m_lineEdit->text();
        handler->gplugSetUser(curUser);
    }
}

void
KGenericGreeter::slotChanged()
{
    handler->gplugChanged();
}

// factory
static bool init(const QString &,
                 QVariant(*getConf)(void *, const char *, const QVariant &),
                 void *ctx)
{
    echoMode = getConf(ctx, "EchoMode", QVariant(-1)).toInt();
    // Fielded entities are not supported per se.
    // This implies that the first field is the presettable entity, if any.
    if (getConf(ctx, "generic.Presettable", QVariant(false)).toBool())
        kgreeterplugin_info.flags |= KGreeterPluginInfo::Presettable;
    KGlobal::locale()->insertCatalog("kgreet_generic");
    return true;
}

static void done(void)
{
    KGlobal::locale()->removeCatalog("kgreet_generic");
}

static KGreeterPlugin *
create(KGreeterPluginHandler *handler,
       QWidget *parent,
       const QString &fixedEntity,
       KGreeterPlugin::Function func,
       KGreeterPlugin::Context ctx)
{
    return new KGenericGreeter(handler, parent, fixedEntity, func, ctx);
}

KDE_EXPORT KGreeterPluginInfo kgreeterplugin_info = {
    I18N_NOOP2("@item:inmenu authentication method", "Generic"), "generic",
    KGreeterPluginInfo::Local,
    init, done, create
};

#include "kgreet_generic.moc"
