/*
 *   This file is part of Dianara
 *   Copyright 2012-2019  JanKusanagi JRR <jancoding@gmx.com>
 *
 *   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 "accountdialog.h"

AccountDialog::AccountDialog(PumpController *pumpController, QWidget *parent) : QWidget(parent)
{
    this->setWindowTitle(tr("Account Configuration") + " - Dianara");
    this->setWindowIcon(QIcon::fromTheme("dialog-password",
                                         QIcon(":/images/button-password.png")));
    this->setWindowFlags(Qt::Dialog);
    this->setWindowModality(Qt::ApplicationModal);
    this->setMinimumSize(640, 520);

    QSettings settings;
    this->resize(settings.value("AccountDialog/accountWindowSize",
                                QSize(760, 620)).toSize());


    m_pumpController = pumpController;

    connect(m_pumpController, &PumpController::openingAuthorizeUrl,
            this, &AccountDialog::showAuthorizationUrl);
    connect(m_pumpController, &PumpController::authorizationStatusChanged,
            this, &AccountDialog::showAuthorizationStatus);
    connect(m_pumpController, &PumpController::authorizationSucceeded,
            this, &AccountDialog::onAuthorizationSucceeded);


    QFont helpFont;
    helpFont.setPointSize(helpFont.pointSize() - 1);

    m_help1Label = new QLabel(tr("First, enter your Webfinger ID, "
                                 "your pump.io address.")
                              + "<br />"
                              + tr("Your address looks like "
                                   "username@pumpserver.org, and "
                                   "you can find it in your profile, "
                                   "in the web interface.")
                              + "<br />"
                              + tr("If your profile is at "
                                   "https://pump.example/yourname, then "
                                   "your address is yourname@pump.example")
                              + "<br /><br />"
                              + tr("If you don't have an account yet, "
                                   "you can sign up for one at %1. "
                                   "This link will take you to a "
                                   "random public server.",
                                   "1=link to website")
                                .arg("<a href=\"http://pump.io/tryit.html\">"
                                     "pump.io/tryit.html</a>")
                              + "<br><br>"
                              + tr("If you need help: %1")
                                .arg("<a href=\"https://pumpio.readthedocs.io"
                                     "/en/latest/userguide.html\">"
                              + tr("Pump.io User Guide")
                              + "</a>"),
                              this);
    m_help1Label->setWordWrap(true);
    m_help1Label->setFont(helpFont);
    m_help1Label->setOpenExternalLinks(true);

    m_userIdIconLabel = new QLabel(this);
    m_userIdIconLabel->setPixmap(QIcon::fromTheme("preferences-desktop-user",
                                                  QIcon(":/images/no-avatar.png"))
                                 .pixmap(64,64)
                                 .scaledToWidth(64, Qt::SmoothTransformation));
    m_userIdLabel = new QLabel("<b>" + tr("Your Pump.io address:") + "</b>",
                               this);
    m_userIdLineEdit = new QLineEdit(this);
    QString userIdTooltip = tr("Your address, like username@pumpserver.org");
    m_userIdLineEdit->setPlaceholderText(userIdTooltip);
    m_userIdLineEdit->setToolTip("<b></b>" + userIdTooltip); // HTMLized for wordwrap
    connect(m_userIdLineEdit, &QLineEdit::returnPressed,
            this, &AccountDialog::askForToken);

    m_getVerifierButton = new QPushButton(QIcon::fromTheme("object-unlocked"),
                                          tr("Get &Verifier Code"),
                                          this);
    m_getVerifierButton->setToolTip("<b></b>" +
                                    tr("After clicking this button, a web "
                                       "browser will open, requesting "
                                       "authorization for Dianara"));
    connect(m_getVerifierButton, &QAbstractButton::clicked,
            this, &AccountDialog::askForToken);


    separatorLine = new QFrame(this);              // ---------------
    separatorLine->setFrameStyle(QFrame::HLine);


    m_help2Label = new QLabel(tr("Once you have authorized Dianara "
                                 "from your Pump server web "
                                 "interface, you'll receive a code "
                                 "called VERIFIER.\n"
                                 "Copy it and paste it into the "
                                 "field below.",
                                 // Comment for translators
                                 "Don't translate the VERIFIER word!"),
                                 this);
    m_help2Label->setWordWrap(true);
    m_help2Label->setFont(helpFont);


    m_verifierIconLabel = new QLabel(this);
    m_verifierIconLabel->setPixmap(QIcon::fromTheme("dialog-password",
                                                    QIcon(":/images/button-password.png"))
                                   .pixmap(64,64)
                                   .scaledToWidth(64, Qt::SmoothTransformation));

    m_verifierLabel = new QLabel("<b>" + tr("Verifier code:") + "</b>",
                                 this);

    QString verifierTooltip = tr("Enter or paste the verifier code provided "
                                 "by your Pump server here");
    m_verifierLineEdit = new QLineEdit(this);
    m_verifierLineEdit->setPlaceholderText(verifierTooltip);
    m_verifierLineEdit->setToolTip("<b></b>" + verifierTooltip);
    connect(m_verifierLineEdit, &QLineEdit::textChanged,
            this, &AccountDialog::onVerifierChanged);


    m_authorizeButton = new QPushButton(QIcon::fromTheme("security-high"),
                                        tr("&Authorize Application"),
                                        this);
    connect(m_authorizeButton, &QAbstractButton::clicked,
            this, &AccountDialog::setVerifierCode);
    connect(m_verifierLineEdit, &QLineEdit::returnPressed,
            m_authorizeButton, &QAbstractButton::click);



    // To notify invalid ID or empty verifier code
    // Cleared when typing in any of the two fields
    m_errorsLabel = new QLabel(this);
    connect(m_userIdLineEdit, &QLineEdit::textChanged,
            m_errorsLabel, &QLabel::clear);


    QFont authorizationStatusFont;
    authorizationStatusFont.setPointSize(authorizationStatusFont.pointSize() + 2);
    authorizationStatusFont.setBold(true);

    m_authorizationStatusLabel = new QLabel(this);
    m_authorizationStatusLabel->setOpenExternalLinks(true);
    m_authorizationStatusLabel->setFont(authorizationStatusFont);


    m_saveButton = new QPushButton(QIcon::fromTheme("document-save",
                                                    QIcon(":/images/button-save.png")),
                                   tr("&Save Details"),
                                   this);
    m_saveButton->setDisabled(true); // Disabled initially
    connect(m_saveButton, &QAbstractButton::clicked,
            this, &AccountDialog::saveDetails);

    m_cancelButton = new QPushButton(QIcon::fromTheme("dialog-cancel",
                                                      QIcon(":/images/button-cancel.png")),
                                     tr("&Cancel"),
                                     this);
    connect(m_cancelButton, &QAbstractButton::clicked,
            this, &QWidget::hide);

    m_bottomButtonBox = new QDialogButtonBox(this);
    m_bottomButtonBox->addButton(m_saveButton,   QDialogButtonBox::AcceptRole);
    m_bottomButtonBox->addButton(m_cancelButton, QDialogButtonBox::RejectRole);


    // Unlock button, visible when the account is already authorized, + info
    m_unlockExplanationLabel = new QLabel("<b>"
                                          + tr("Your account is properly "
                                               "configured.")
                                          + "</b>"
                                            "<br><br>"
                                          + tr("Press Unlock if you wish "
                                               "to configure a different "
                                               "account.")
                                          + "<hr>",
                                          this);
    m_unlockExplanationLabel->setAlignment(Qt::AlignHCenter);

    m_unlockButton = new QPushButton(QIcon::fromTheme("object-unlocked"),
                                     tr("&Unlock"),
                                     this);
    connect(m_unlockButton, &QAbstractButton::clicked,
            this, &AccountDialog::unlockDialog);


    //////////////////////////////////////////////////////////////////// Layout
    m_idLayout = new QHBoxLayout();
    m_idLayout->addWidget(m_userIdIconLabel);
    m_idLayout->addSpacing(4);
    m_idLayout->addWidget(m_userIdLabel);
    m_idLayout->addWidget(m_userIdLineEdit);
    m_idLayout->addWidget(m_getVerifierButton);

    m_verifierLayout = new QHBoxLayout();
    m_verifierLayout->addWidget(m_verifierIconLabel);
    m_verifierLayout->addSpacing(4);
    m_verifierLayout->addWidget(m_verifierLabel);
    m_verifierLayout->addWidget(m_verifierLineEdit);
    m_verifierLayout->addWidget(m_authorizeButton);



    m_mainLayout = new QVBoxLayout();
    m_mainLayout->addWidget(m_unlockExplanationLabel, 6);
    m_mainLayout->addWidget(m_unlockButton, 0, Qt::AlignHCenter);

    m_mainLayout->addWidget(m_help1Label, 6);
    m_mainLayout->addSpacing(2);
    m_mainLayout->addStretch(1);
    m_mainLayout->addLayout(m_idLayout, 2);
    m_mainLayout->addSpacing(4);
    m_mainLayout->addStretch(1);
    m_mainLayout->addWidget(separatorLine);
    m_mainLayout->addStretch(1);
    m_mainLayout->addSpacing(4);
    m_mainLayout->addWidget(m_help2Label, 2);
    m_mainLayout->addSpacing(2);
    m_mainLayout->addStretch(1);
    m_mainLayout->addLayout(m_verifierLayout, 2);
    m_mainLayout->addWidget(m_errorsLabel,              1, Qt::AlignCenter);
    m_mainLayout->addSpacing(1);
    m_mainLayout->addWidget(m_authorizationStatusLabel, 1, Qt::AlignCenter);
    m_mainLayout->addStretch(2);
    m_mainLayout->addSpacing(8);
    m_mainLayout->addWidget(m_bottomButtonBox, 1);
    this->setLayout(m_mainLayout);


    // Load saved User ID, and authorization status
    m_userIdLineEdit->setText(settings.value("userID").toString());

    this->showAuthorizationStatus(settings.value("isApplicationAuthorized",
                                                 false).toBool());


    // Disable verifier input field and button initially
    // They will be used after requesting a token
    m_verifierLineEdit->setDisabled(true);
    m_authorizeButton->setDisabled(true);


    qDebug() << "Account dialog created";
}


AccountDialog::~AccountDialog()
{
    qDebug() << "Account dialog destroyed";
}


void AccountDialog::setLockMode(bool locked)
{
    if (locked)
    {
        m_help1Label->hide();
        separatorLine->hide();
        m_help2Label->hide();

        m_unlockExplanationLabel->show();
        m_unlockButton->show();

        m_verifierLineEdit->setDisabled(true);
        m_authorizeButton->setDisabled(true);
    }
    else
    {
        m_help1Label->show();
        separatorLine->show();
        m_help2Label->show();

        m_unlockExplanationLabel->hide();
        m_unlockButton->hide();
    }

    m_userIdLineEdit->setDisabled(locked);
    m_getVerifierButton->setDisabled(locked);
}



//////////////////////////////////////////////////////////////////////////////
///////////////////////////////////// SLOTS //////////////////////////////////
//////////////////////////////////////////////////////////////////////////////



void AccountDialog::askForToken()
{
    QSettings settings;
    settings.setValue("isApplicationAuthorized", false); // Until steps are completed

    // Don't fail if there are spaces before or after the ID, they're only human ;)
    m_userIdLineEdit->setText(m_userIdLineEdit->text().trimmed());

    // Match username@server.tld AND check for only 1 @ sign
    if (QRegExp("\\S+@(\\S+\\.\\S+|localhost.*)").exactMatch(m_userIdLineEdit->text())
     && m_userIdLineEdit->text().count("@") == 1)
    {
        // Clear verifier field and re-enable it
        m_verifierLineEdit->clear();
        m_verifierLineEdit->setEnabled(true);


        // Show message about the web browser that will be started
        m_errorsLabel->setText("[ "
                               + tr("A web browser will start now, "
                                    "where you can get the verifier code")
                               + " ]");

        m_pumpController->setNewUserId(m_userIdLineEdit->text());
        m_pumpController->getToken();
    }
    else // userID does not match user@hostname.domain
    {
        m_errorsLabel->setText("[ " + tr("Your Pump address is invalid") + " ]");
        qDebug() << "userID is invalid!";
    }
}



void AccountDialog::setVerifierCode()
{
    qDebug() << "AccountDialog::setVerifierCode()"
             << m_verifierLineEdit->text();

    if (!m_verifierLineEdit->text().trimmed().isEmpty())
    {
        m_pumpController->authorizeApplication(m_verifierLineEdit->text()
                                                                  .trimmed());
    }
    else
    {
        m_errorsLabel->setText("[ " + tr("Verifier code is empty") + " ]");
    }
}


void AccountDialog::onVerifierChanged(QString newText)
{
    m_authorizeButton->setDisabled(newText.isEmpty());

    m_errorsLabel->clear();
}



void AccountDialog::onAuthorizationSucceeded()
{
    m_saveButton->setEnabled(true);
    m_errorsLabel->clear();
}



void AccountDialog::showAuthorizationStatus(bool authorized)
{
    if (authorized)
    {
        m_authorizationStatusLabel->setText(QString::fromUtf8("\342\234\224 ") // Check mark
                                            + tr("Dianara is authorized to "
                                                 "access your data"));
    }
    else
    {
        m_authorizationStatusLabel->clear();
    }

    this->setLockMode(m_pumpController->currentlyAuthorized());
}


/*
 * Show the authorization URL in a label,
 * in case the browser doesn't open automatically
 *
 */
void AccountDialog::showAuthorizationUrl(QUrl url, bool browserLaunched)
{
    QString message = tr("If the browser doesn't open automatically, "
                         "copy this address manually")
                      + ":<br /><a href=\"" + url.toString() + "\">"
                      + url.toString() + "</a>";

    m_authorizationStatusLabel->setText(message);

    if (!browserLaunched)
    {
        m_errorsLabel->setText("[ " + tr("Unable to open web browser!") + " ]");
    }
}





/*
 * Save the new userID and inform other parts of the program about it
 *
 */
void AccountDialog::saveDetails()
{
    QString newUserId = m_userIdLineEdit->text().trimmed();
    if (newUserId.isEmpty() || !newUserId.contains("@"))
    {
        return;  // If no user ID, or with no "@", ignore // FIXME
    }

    QSettings settings;
    settings.setValue("userID", newUserId);
    settings.sync();

    m_errorsLabel->clear(); // Clear previous error messages, if any


    // If it's authorized, disable the Save Details button
    if (m_pumpController->currentlyAuthorized())
    {
        m_saveButton->setDisabled(true);

        // Set wheels in motion only if the auth process is complete
        emit userIdChanged(newUserId);
    }

    this->hide();
}


void AccountDialog::unlockDialog()
{
    this->setLockMode(false);
}



//////////////////////////////////////////////////////////////////////////////
/////////////////////////////////// PROTECTED ////////////////////////////////
//////////////////////////////////////////////////////////////////////////////


void AccountDialog::hideEvent(QHideEvent *event)
{
    this->setLockMode(m_pumpController->currentlyAuthorized());

    QSettings settings;
    if (settings.isWritable())
    {
        settings.setValue("AccountDialog/accountWindowSize", this->size());
    }

    event->accept();
}
