/*
 *   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 "contactmanager.h"

ContactManager::ContactManager(PumpController *pumpController,
                               GlobalObject *globalObject,
                               QWidget *parent) : QWidget(parent)
{
    m_pumpController = pumpController;
    m_globalObject = globalObject;

    m_followingManually = false;


    // After receiving a contact list, update it
    connect(m_pumpController, &PumpController::contactListReceived,
            this, &ContactManager::setContactListsContents);

    // After receiving the list of lists, update it
    connect(m_pumpController, &PumpController::listsListReceived,
            this, &ContactManager::setListsListContents);

    // After having checked if a webfinger ID is valid, follow it (or not)
    connect(m_pumpController, &PumpController::contactVerified,
            this, &ContactManager::followContact);

    // If a person can't be followed at the moment, re-enable widgets to try again
    connect(m_pumpController, &PumpController::cannotFollowNow,
            this, &ContactManager::onCantFollowNow);


    m_mainLayout = new QVBoxLayout();
    m_mainLayout->setAlignment(Qt::AlignTop);


    QString webfingerHelpMessage = tr("username@server.org or "
                                      "https://server.org/username");

    m_topLayout = new QHBoxLayout();
    m_enterAddressLabel = new QLabel(tr("&Enter address to follow:"), this);
    m_enterAddressLabel->setToolTip("<b></b>"
                                  + webfingerHelpMessage); // HTML tags for wordwrap
    // Ensure it will get focus first, before addressLineEdit
    m_enterAddressLabel->setFocusPolicy(Qt::StrongFocus);

    m_addressLineEdit = new QLineEdit(this);
    m_addressLineEdit->setPlaceholderText(webfingerHelpMessage);
    m_addressLineEdit->setToolTip("<b></b>"
                                + webfingerHelpMessage);  // HTML tags to get wordwrap
    m_addressLineEdit->setClearButtonEnabled(true);
    connect(m_addressLineEdit, &QLineEdit::textChanged,
            this, &ContactManager::toggleFollowButton);
    connect(m_addressLineEdit, &QLineEdit::returnPressed,
            this, &ContactManager::validateContactId);

    m_enterAddressLabel->setBuddy(m_addressLineEdit);


    m_followButton = new QPushButton(QIcon::fromTheme("list-add-user",
                                                    QIcon(":/images/list-add.png")),
                                   tr("&Follow"),
                                   this);
    m_followButton->setDisabled(true); // Disabled until an address is typed
    connect(m_followButton, &QAbstractButton::clicked,
            this, &ContactManager::validateContactId);
    m_topLayout->addWidget(m_enterAddressLabel);
    m_topLayout->addWidget(m_addressLineEdit);
    m_topLayout->addWidget(m_followButton);

    m_mainLayout->addLayout(m_topLayout);
    m_mainLayout->addSpacing(4);


    // Widgets for list of 'following' and 'followers'
    m_followingWidget = new ContactList(m_pumpController,
                                        m_globalObject,
                                        QStringLiteral("following"),
                                        this);
    connect(m_pumpController, &PumpController::contactFollowed,
            m_followingWidget, &ContactList::addSingleContact);
    connect(m_pumpController, &PumpController::contactUnfollowed,
            m_followingWidget, &ContactList::removeSingleContact);

    connect(m_followingWidget, &ContactList::contactCountChanged,
            this, &ContactManager::changeFollowingCount);


    m_followersWidget = new ContactList(m_pumpController,
                                        m_globalObject,
                                        QStringLiteral("followers"),
                                        this);


    // Widget for the list of 'person lists'
    m_listsManager = new ListsManager(m_pumpController, this);
    m_listsScrollArea = new QScrollArea(this);
    m_listsScrollArea->setWidget(m_listsManager);
    m_listsScrollArea->setWidgetResizable(true);
    m_listsScrollArea->setFrameStyle(QFrame::NoFrame);


    // Widget for the list of site users; users from your own server
    m_siteUsersList = new SiteUsersList(m_pumpController,
                                        m_globalObject,
                                        this);

    // Options menu
    m_optionsMenu = new QMenu(QStringLiteral("*options-menu*"), this);
    m_optionsMenu->addAction(QIcon::fromTheme("view-refresh",
                                              QIcon(":/images/menu-refresh.png")),
                             tr("Reload Followers"),
                             this,
                             SLOT(refreshFollowers()));
    m_optionsMenu->addAction(QIcon::fromTheme("view-refresh",
                                              QIcon(":/images/menu-refresh.png")),
                             tr("Reload Following"),
                             this,
                             SLOT(refreshFollowing()));
    m_optionsMenu->addSeparator();
    m_optionsMenu->addAction(QIcon::fromTheme("document-export",
                                              QIcon(":/images/button-download.png")),
                             tr("Export Followers"),
                             this,
                             SLOT(exportFollowers()));
    m_optionsMenu->addAction(QIcon::fromTheme("document-export",
                                              QIcon(":/images/button-download.png")),
                             tr("Export Following"),
                             this,
                             SLOT(exportFollowing()));
    m_optionsMenu->addSeparator();
    m_optionsMenu->addAction(QIcon::fromTheme("view-refresh",
                                              QIcon(":/images/menu-refresh.png")),
                             tr("Reload Lists"),
                             this,
                             SLOT(refreshPersonLists()));


    m_optionsButton = new QPushButton(QIcon::fromTheme("configure",
                                                       QIcon(":/images/button-configure.png")),
                                      QString(),
                                      this);
    m_optionsButton->setMenu(m_optionsMenu);


    m_tabWidget = new QTabWidget(this);
    m_tabWidget->addTab(m_followersWidget,
                        QIcon::fromTheme("meeting-observer",
                                         QIcon(":/images/no-avatar.png")),
                        QString());
    m_tabWidget->addTab(m_followingWidget,
                        QIcon::fromTheme("meeting-participant",
                                         QIcon(":/images/no-avatar.png")),
                        QString());
    m_tabWidget->addTab(m_listsScrollArea,
                        QIcon::fromTheme("preferences-contact-list",
                                         QIcon(":/images/button-edit.png")),
                        QString());
    m_tabWidget->addTab(m_siteUsersList,
                        QIcon::fromTheme("system-users",
                                         QIcon(":/images/button-users.png")),
                        tr("&Neighbors"));
    m_tabWidget->setCornerWidget(m_optionsButton);




    m_followersCount = 0;
    m_followingCount = 0;
    m_listsCount = 0;
    this->setTabLabels();


    m_mainLayout->addWidget(m_tabWidget);
    this->setLayout(m_mainLayout);

    qDebug() << "Contact manager created";
}


ContactManager::~ContactManager()
{
    qDebug() << "Contact manager destroyed";
}




void ContactManager::setTabLabels()
{
    m_tabWidget->setTabText(0, tr("Follo&wers")
                               + QString(" (%1)")
                                 .arg(QLocale::system()
                                              .toString(m_followersCount)));

    m_tabWidget->setTabText(1, tr("Followin&g")
                               + QString(" (%1)")
                                 .arg(QLocale::system()
                                              .toString(m_followingCount)));

    m_tabWidget->setTabText(2, tr("&Lists")
                               + QString(" (%1)").arg(m_listsCount)); // Not worth localizing
}




/*
 * Write the list of contacts (following or followers)
 * to a file selected by the user
 *
 */
void ContactManager::exportContactsToFile(QString listType)
{
    QString dialogTitle = listType == "following" ?
                    tr("Export list of 'following' to a file") :
                    tr("Export list of 'followers' to a file");

    QString suggestedFilename = "dianara-"
                                + m_pumpController->currentUsername()
                                + "-"
                                + listType;

    QString filename = QFileDialog::getSaveFileName(this, dialogTitle,
                                                    QDir::homePath() + "/"
                                                    + suggestedFilename,
                                                    QString()).trimmed();

    if (filename.isEmpty()) // If dialog was cancelled, do nothing
    {
        return;
    }


    qDebug() << "Exporting to:"  << filename;

    QFile exportFile(filename);
    if (exportFile.open(QIODevice::WriteOnly))
    {
        if (listType == "following")
        {
            exportFile.write(m_followingWidget->getContactsStringForExport()
                                               .toLocal8Bit());
        }
        else // "followers"
        {
            exportFile.write(m_followersWidget->getContactsStringForExport()
                                               .toLocal8Bit());
        }

        exportFile.close();
    }
    else
    {
        QMessageBox::critical(this,
                              tr("Error"),
                              tr("Cannot export to this file:")
                              + "\n"
                              + filename
                              + "\n\n"
                              + tr("Please enter another file name, "
                                   "or choose a different folder."));

        qDebug() << "Cannot write to that file!";
    }
}



void ContactManager::enableManualFollowWidgets()
{
    m_addressLineEdit->setEnabled(true);
    m_followButton->setEnabled(!m_addressLineEdit->text().isEmpty());
}


/*****************************************************************************/
/*********************************** SLOTS ***********************************/
/*****************************************************************************/



void ContactManager::setContactListsContents(QString listType,
                                             QVariantList contactList,
                                             int totalReceivedCount)
{
    qDebug() << "ContactManager; Setting contact list contents";

    if (listType == "following")
    {
        if (totalReceivedCount <= 200) // Only for the first batch
        {
            m_followingWidget->clearListContents();
            m_followingCount = 0;
        }
        m_followingWidget->setListContents(contactList);

        if (totalReceivedCount < m_pumpController->currentFollowingCount())
        {
            m_pumpController->getContactList(listType, totalReceivedCount);
        }

        m_followingCount += contactList.size();
    }
    else
    {
        if (totalReceivedCount <= 200)
        {
            m_followersWidget->clearListContents();
            m_followersCount = 0;
        }
        m_followersWidget->setListContents(contactList);

        if (totalReceivedCount < m_pumpController->currentFollowersCount())
        {
            m_pumpController->getContactList(listType, totalReceivedCount);
        }

        m_followersCount += contactList.size();
    }


    // Update tab labels with number of following or followers, which were updated before
    this->setTabLabels();
}



/*
 * Fill the list of lists
 *
 */
void ContactManager::setListsListContents(QVariantList listsList)
{
    m_listsCount = listsList.count();
    // Update tab labels with number of following or followers, which were updated before
    this->setTabLabels();


    m_listsManager->setListsList(listsList);
}


void ContactManager::changeFollowingCount(int difference)
{
    m_followingCount += difference; // FIXME: pumpController should be notified

    this->setTabLabels();

    // Set "Following" tab as active, only when the contact manager is not visible
    // (following someone from an AvatarButton menu)
    if (!this->isVisible())
    {
        m_tabWidget->setCurrentIndex(1);
    }
}



/*
 * Ask for the updated list of Following
 *
 */
void ContactManager::refreshFollowing()
{
    qDebug() << "Refreshing list of Following...";
    m_pumpController->getContactList("following");
}


/*
 * Ask for the updated list of followers
 *
 */
void ContactManager::refreshFollowers()
{
    qDebug() << "Refreshing list of Followers...";
    m_pumpController->getContactList("followers");
}


/*
 * Export list of "Following" to a text file
 *
 */
void ContactManager::exportFollowing()
{
    qDebug() << "Exporting Following...";
    exportContactsToFile("following");
}


/*
 * Export list of "Followers" to a text file
 *
 */
void ContactManager::exportFollowers()
{
    qDebug() << "Exporting Followers...";
    exportContactsToFile("followers");
}


void ContactManager::refreshPersonLists()
{
    qDebug() << "Refreshing list of person lists...";
    m_pumpController->getListsList();
}



/*
 * Enable or disable Follow button
 *
 */
void ContactManager::toggleFollowButton(QString currentAddress)
{
    if (currentAddress.isEmpty())
    {
        m_followButton->setDisabled(true);
    }
    else
    {
        m_followButton->setEnabled(true);
    }
}



/*
 * Add the address entered by the user to the /following list.
 *
 * This supports adding webfinger addresses in the form
 * user@hostname or https://host/username.
 *
 * First step is checking, via Webfinger, if the ID exists and its server is up.
 *
 */
void ContactManager::validateContactId()
{
    QString address = m_addressLineEdit->text().trimmed();
    bool validAddress = false;

    qDebug() << "ContactManager::followContact(); Address entered:" << address;

    // First, if the address is in URL form, convert it
    if (address.startsWith("https://") || address.startsWith("http://"))
    {
        address.remove("https://");
        address.remove("http://");

        if (address.contains("/")) // Very basic sanity check
        {
            QStringList addressParts = address.split("/");
            address = addressParts.at(1) + "@" + addressParts.at(0);
            // .at(2) could also have stuff, so don't use .first() and .last()
        }
    }

    // Then, check that the address actually matches something@somewhere.tld
    if (address.contains(QRegExp(".+@.+\\..+")))
    {
        validAddress = true;
    }
    else
    {
        qDebug() << "Invalid webfinger address!";
        m_addressLineEdit->setFocus();
    }


    if (validAddress)
    {
        m_followingManually = true;
        m_followingAddress = address;

        m_tabWidget->setFocus(); // Take focus away from the widgets that will be disabled now

        m_addressLineEdit->setDisabled(true);
        m_followButton->setDisabled(true);

        m_pumpController->followContact(address);
    }
}


void ContactManager::followContact(QString userId, int httpCode,
                                   bool requestTimedOut, QString serverVersion)
{
    if (httpCode != 200)
    {
        QString hostname = userId.split('@').last();

        // FIXME/TMP: Unreliable, future/alternate implementations might not use this
        bool isPumpServer = serverVersion.contains("pump.io");

        QString specificError;
        if (httpCode == 404)
        {
            if (isPumpServer)
            {
                specificError = tr("The server seems to be a Pump server, "
                                   "but the account does not exist.");
            }
            else
            {
                specificError = tr("%1 doesn't seem to be a Pump server.",
                                   "%1 is a hostname")
                                .arg(hostname);
            }
        }
        else
        {
            // Really only error 0 would mean unavailable server
            // Any other error code probably means the server is not a Pump server
            specificError = "<b>&bull;&bull;&bull; "
                            + tr("Following this account at this time "
                                 "will probably not work.")
                            + "</b>";
        }

        // Timeout was reached or general connection error
        if (requestTimedOut || httpCode == 0)
        {
            specificError.append(" <b>"
                                 + tr("The %1 server seems unavailable.",
                                      "%1 is a hostname")
                                   .arg(hostname)
                                 + "</b>");
        }

        if (serverVersion.isEmpty())
        {
            serverVersion = tr("Unknown",
                               "Refers to server version");
        }

        int choice = QMessageBox::warning(this,
                      tr("Error"),
                      "<big>"
                      + tr("The user address %1 does not exist, or the "
                           "%2 server is down.")
                        .arg("<b>" + userId + "</b>")
                        .arg("<b>" + hostname + "</b>")
                      + "</big>"
                        "<br><hr><br>"
                      + specificError
                      + "<br><br>"
                      + tr("Server version") + ": <i>"
                      + serverVersion
                      + "</i>"
                        "<br><br>"
                      + tr("Check the address, and keep in mind that "
                           "usernames are case-sensitive.")
                      + "<br><br><br><br>"
                      + tr("Do you want to try following this address anyway?")
                      + "<br>"
                      + tr("(not recommended)")
                      + "<br>"
                        "<br>",
                      tr("Yes, follow anyway"),
                      tr("No, cancel"),
                      QString(), 1, 1);

        if (choice == 1)
        {
            if (m_followingManually && userId == m_followingAddress)
            {
                this->enableManualFollowWidgets();
                m_addressLineEdit->setFocus(); // Re-focus to try again, maybe

                m_followingManually = false;
                m_followingAddress.clear();
            }

            return;
        }
    }

    qDebug() << "About to follow this address:" << userId;
    m_globalObject->setStatusMessage(tr("About to follow %1...")
                                     .arg("'" + userId + "'"));
    m_pumpController->followVerifiedContact(userId);

    if (m_followingManually && userId == m_followingAddress)
    {
        enableManualFollowWidgets();
        m_addressLineEdit->clear(); // Which will disable Follow button again

        m_followingManually = false;
        m_followingAddress.clear();

        // Activate 'Following' tab
        m_tabWidget->setFocus();
        m_tabWidget->setCurrentIndex(1);
    }
}


void ContactManager::onCantFollowNow(QString userId)
{
    if (m_followingManually && userId == m_followingAddress)
    {
        enableManualFollowWidgets();
        m_addressLineEdit->setFocus();
    }
}

