/*
 * Copyright (C) 2014-2017 Canonical Ltd.
 *
 * 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; version 3.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 */

#include "MockController.h"
#include "MockGreeter.h"
#include <QDBusInterface>
#include <QDBusPendingCall>
#include <QTimer>

namespace QLightDM
{

class GreeterPrivate
{
public:
    bool authenticated = false;
    QString authenticationUser;
    bool twoFactorDone = false;
    QTimer authCallbackTimer;
};

Greeter::Greeter(QObject *parent)
  : QObject(parent)
  , d_ptr(new GreeterPrivate)
{
    Q_D(Greeter);

    d->authCallbackTimer.setSingleShot(true);
    connect(&d->authCallbackTimer, &QTimer::timeout,
            this, &QLightDM::Greeter::handleAuthenticate);
}

Greeter::~Greeter()
{
    delete d_ptr;
}

QString Greeter::authenticationUser() const
{
    Q_D(const Greeter);

    return d->authenticationUser;
}

bool Greeter::hasGuestAccountHint() const
{
    return MockController::instance()->hasGuestAccountHint();
}

QString Greeter::getHint(const QString &name) const
{
    Q_UNUSED(name)
    return "";
}

QString Greeter::defaultSessionHint() const
{
    return QStringLiteral("lomiri");
}

bool Greeter::hideUsersHint() const
{
    return MockController::instance()->hideUsersHint();
}

bool Greeter::showManualLoginHint() const
{
    return MockController::instance()->showManualLoginHint();
}

bool Greeter::showRemoteLoginHint() const
{
    return false;
}

QString Greeter::selectUserHint() const
{
    return MockController::instance()->selectUserHint();
}

bool Greeter::selectGuestHint() const
{
    return MockController::instance()->selectGuestHint();
}

QString Greeter::autologinUserHint() const
{
    return "";
}

bool Greeter::autologinGuestHint() const
{
    return false;
}

int Greeter::autologinTimeoutHint() const
{
    return 0;
}

bool Greeter::inAuthentication() const
{
    return false;
}

QString Greeter::hostname() const
{
    return "hostname1";
}

bool Greeter::isAuthenticated() const
{
    Q_D(const Greeter);

    return d->authenticated;
}

bool Greeter::connectSync()
{
    return true;
}

void Greeter::authenticate(const QString &username)
{
    Q_D(Greeter);

    d->authenticated = false;
    d->authenticationUser = username;
    d->twoFactorDone = false;
    d->authCallbackTimer.start();
}

void Greeter::handleAuthenticate()
{
    Q_D(Greeter);

    if (d->authenticated) {
        // This can happen from the guest authentication flow
        Q_EMIT authenticationComplete();
        return;
    }

    if (d->authenticationUser.isEmpty()) {
        Q_EMIT showPrompt("Username: ", Greeter::PromptTypeQuestion);
        return;
    }

    // Send out any messages we need to
    if (d->authenticationUser == "info-prompt")
        Q_EMIT showMessage("Welcome to Lomiri Greeter", Greeter::MessageTypeInfo);
    else if (d->authenticationUser == "wide-info-prompt")
        Q_EMIT showMessage("Welcome to Lomiri Greeter, the greeteriest greeter that ever did appear in these fine lands", Greeter::MessageTypeInfo);
    else if (d->authenticationUser == "html-info-prompt")
        Q_EMIT showMessage("<b>&</b>", Greeter::MessageTypeInfo);
    else if (d->authenticationUser == "long-info-prompt")
        Q_EMIT showMessage("Welcome to Lomiri Greeter\n\nWe like to annoy you with super ridiculously long messages.\nLike this one\n\nThis is the last line of a multiple line message.", Greeter::MessageTypeInfo);
    else if (d->authenticationUser == "multi-info-prompt") {
        Q_EMIT showMessage("Welcome to Lomiri Greeter", Greeter::MessageTypeInfo);
        Q_EMIT showMessage("This is an error", Greeter::MessageTypeError);
        Q_EMIT showMessage("You should have seen three messages", Greeter::MessageTypeInfo);
    }

    // OK, now actually do the prompt
    if (d->authenticationUser == "no-password") {
        d->authenticated = true;
        Q_EMIT authenticationComplete();
    } else if (d->authenticationUser == "has-pin" || d->authenticationUser == "has-pin-clock") {
        Q_EMIT showPrompt("Password: ", Greeter::PromptTypeSecret);
    } else if (d->authenticationUser == "auth-error") {
        d->authenticated = false;
        Q_EMIT authenticationComplete();
    } else if (d->authenticationUser == "different-prompt") {
        Q_EMIT showPrompt("Secret word： ", Greeter::PromptTypeSecret);
    } else if (d->authenticationUser == "question-prompt") {
        Q_EMIT showPrompt("Favorite Color (blue): ", Greeter::PromptTypeQuestion);
    } else if (d->authenticationUser == "two-prompts") {
        Q_EMIT showPrompt("Favorite Color (blue):", Greeter::PromptTypeQuestion);
        Q_EMIT showPrompt("Password: ", Greeter::PromptTypeSecret);
    } else if (d->authenticationUser == "wacky-prompts") {
        Q_EMIT showMessage("First message", Greeter::MessageTypeInfo);
        Q_EMIT showPrompt("Favorite Color (blue)", Greeter::PromptTypeQuestion);
        Q_EMIT showMessage("Second message", Greeter::MessageTypeError);
        Q_EMIT showPrompt("Password: ", Greeter::PromptTypeSecret);
        Q_EMIT showMessage("Last message", Greeter::MessageTypeInfo);
    } else {
        Q_EMIT showPrompt("Password: ", Greeter::PromptTypeSecret);
    }
}

void Greeter::authenticateAsGuest()
{
    Q_D(Greeter);

    d->authenticated = true;
    d->authenticationUser = QString(); // this is what the real liblightdm does
    d->twoFactorDone = false;
    d->authCallbackTimer.start();
}

void Greeter::authenticateAutologin()
{}

void Greeter::authenticateRemote(const QString &session, const QString &username)
{
    Q_UNUSED(session)
    Q_UNUSED(username)
}

void Greeter::cancelAuthentication()
{
    Q_D(Greeter);
    d->authCallbackTimer.stop();
}

void Greeter::setLanguage (const QString &language)
{
    Q_UNUSED(language)
}

bool Greeter::startSessionSync(const QString &session)
{
    Q_UNUSED(session)

    // Send a request to hide the greeter.  This is normally done by logind,
    // but when testing, we don't want the bother of mocking that out. Instead,
    // just send the request directly ourselves.
    QDBusInterface iface("com.lomiri.LomiriGreeter",
                         "/com/lomiri/LomiriGreeter",
                         "com.lomiri.LomiriGreeter",
                         QDBusConnection::sessionBus());
    iface.asyncCall("HideGreeter");

    return true;
}

void Greeter::respond(const QString &response)
{
    Q_D(Greeter);

    if (d->authenticationUser.isEmpty()) {
        // A manual login, our first question was which username
        d->authenticationUser = response;
        handleAuthenticate();
        return;
    }

    if (d->authenticationUser == "no-response") {
        return;
    } else if (d->authenticationUser == "locked") {
        Q_EMIT showMessage("Account is locked", Greeter::MessageTypeError);
        sendAuthenticationComplete();
        return;
    } else if (d->authenticationUser == "two-factor") {
        if (!d->twoFactorDone) {
            if (response == "password") {
                d->twoFactorDone = true;
                Q_EMIT showPrompt("otp", Greeter::PromptTypeQuestion);
            } else {
                d->authenticated = false;
                sendAuthenticationComplete();
            }
        } else {
            d->authenticated = (response == "otp");
            sendAuthenticationComplete();
        }
        return;
    } else if (d->authenticationUser == "two-prompts" || d->authenticationUser == "wacky-prompts") {
        if (!d->twoFactorDone) {
            d->authenticated = (response == "blue");
            d->twoFactorDone = true;
        } else {
            d->authenticated = d->authenticated && (response == "password");
            sendAuthenticationComplete();
        }
        return;
    }

    if (d->authenticationUser == "has-pin" || d->authenticationUser == "has-pin-clock") {
        QString expectedPincode = "";
        for (int i = 1; i < response.size()+1; ++i) {
            expectedPincode = expectedPincode + QString::number(i);
        }
        d->authenticated = (response == expectedPincode);
    } else if (d->authenticationUser == "question-prompt") {
        d->authenticated = (response == "blue");
    } else {
        d->authenticated = (response == "password");
    }

    if (d->authenticationUser == "info-after-login" && d->authenticated) {
        Q_EMIT showMessage("Congratulations on logging in!", Greeter::MessageTypeInfo);
    }

    sendAuthenticationComplete();
}

void Greeter::sendAuthenticationComplete()
{
    if (qEnvironmentVariableIsEmpty("LOMIRI_TESTING")) {
        // simulate PAM's delay
        QTimer::singleShot(1000, this, &Greeter::authenticationComplete);
    } else {
        Q_EMIT authenticationComplete();
    }
}

}
