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

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


    // Need action defined before its target lineedit, to show localized shortcut
    m_focusFilterAction = new QAction(this);
    m_focusFilterAction->setShortcut(QKeySequence("Ctrl+F"));

    QString filterNote = tr("Type a partial name or ID to find a contact...")
                         + " ("
                         + m_focusFilterAction->shortcut()
                                               .toString(QKeySequence::NativeText)
                         + ")";

    m_filterLineEdit = new QLineEdit(this);
    m_filterLineEdit->setPlaceholderText(filterNote);
    m_filterLineEdit->setToolTip("<b></b>" + filterNote); // HTMLized for wordwrapping
    m_filterLineEdit->setClearButtonEnabled(true);
    connect(m_filterLineEdit, &QLineEdit::textChanged,
            this, &ContactList::filterList);

    connect(m_focusFilterAction, &QAction::triggered,
            this, &ContactList::focusFilterField);
    this->addAction(m_focusFilterAction);


    const int iconSize = m_filterLineEdit->sizeHint().height(); // filterLineEdit->font().pixelSize()?
    m_filterIcon = new QLabel(this);
    m_filterIcon->setAlignment(Qt::AlignCenter);
    m_filterIcon->setPixmap(QIcon::fromTheme("edit-find",
                                             QIcon(":/images/menu-find.png"))
                                  .pixmap(iconSize, iconSize));


    m_matchesCountLabel = new QLabel(this);
    m_matchesCountLabel->hide();


    m_clearFilterButton = new QPushButton(QIcon::fromTheme("view-list-icons"),
                                          tr("F&ull List"),
                                          this);
    m_clearFilterButton->setDisabled(true); // Disabled initially, until a search happens
    connect(m_clearFilterButton, &QAbstractButton::clicked,
            m_filterLineEdit, &QLineEdit::clear);



    // Layout
    m_contactsLayout = new QVBoxLayout();

    m_contactsWidget = new QWidget(this);
    m_contactsWidget->setLayout(m_contactsLayout);

    m_contactsScrollArea = new QScrollArea(this);
    m_contactsScrollArea->setWidget(m_contactsWidget);
    m_contactsScrollArea->setWidgetResizable(true);
    m_contactsScrollArea->setFrameStyle(QFrame::NoFrame);
    m_contactsScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);


    m_filterLayout = new QHBoxLayout();
    m_filterLayout->addWidget(m_filterIcon, 0, Qt::AlignVCenter);
    m_filterLayout->addWidget(m_filterLineEdit);
    m_filterLayout->addWidget(m_matchesCountLabel);
    m_filterLayout->addWidget(m_clearFilterButton);

    m_mainLayout = new QVBoxLayout();
    m_mainLayout->addWidget(m_contactsScrollArea);
    m_mainLayout->addLayout(m_filterLayout);
    this->setLayout(m_mainLayout);


    // Add demo contacts
    QVariantMap demoContactData;
    QVariantMap demoContactHometown;
    QVariantMap demoContactFollowed;

    m_isFollowing = false;
    if (listType == "following")
    {
        m_isFollowing = true;

        demoContactData.insert("displayName",  "Demo Contact");
        demoContactData.insert("id",           "democontact@pumpserver.org");
        demoContactData.insert("url",          "https://jancoding.wordpress.com");
        demoContactFollowed.insert("followed", "true");
    }
    else
    {
        demoContactData.insert("displayName",  "Demo Follower");
        demoContactData.insert("id",           "demofollower@pumpserver.org");
        demoContactData.insert("url",          "http://dianara.nongnu.org");
        demoContactFollowed.insert("followed", "false");
    }

    demoContactHometown.insert("displayName",  "Some city");
    demoContactData.insert("location", demoContactHometown);

    demoContactData.insert("pump_io",  demoContactFollowed);

    demoContactData.insert("published", "2013-05-01T00:00:00Z"); // Dianara's birthday


    ASPerson *demoContactPerson = new ASPerson(demoContactData, this);
    ContactCard *demoContactCard = new ContactCard(m_pumpController,
                                                   m_globalObject,
                                                   demoContactPerson,
                                                   this);
    m_contactsLayout->addWidget(demoContactCard);
    m_contactsInList.append(demoContactCard);
    delete demoContactPerson;

    qDebug() << "ContactList created";
}

ContactList::~ContactList()
{
    qDebug() << "ContactList destroyed";
}



void ContactList::clearListContents()
{
    foreach (ContactCard *card, m_contactsInList)
    {
        m_contactsLayout->removeWidget(card);
        delete card;
    }
    m_contactsInList.clear();

    if (m_isFollowing)
    {
        m_globalObject->clearNickCompletionModel();
    }
}


void ContactList::setListContents(QVariantList contactList)
{
    qDebug() << "ContactList; Setting list contents";

    QStringList followingIdStringList;

    foreach (const QVariant contact, contactList)
    {
        QVariantMap contactMap = contact.toMap();
        if (!contactMap.keys().contains(QStringLiteral("id")))
        {
            /*
             * Temporary hack to fix user profiles when the list comes
             * from the site user list, which is currently empty,
             * and contains ID only inside the profile property
             *
             */

            QString id = ASPerson::cleanupId(contactMap.value("profile").toMap()
                                                       .value("id").toString());
            contactMap.insert("id", id);

            QVariantMap followedMap;
            followedMap.insert("followed", m_pumpController->userInFollowing(id));
            contactMap.insert("pump_io", followedMap);

            id.remove("@" + m_pumpController->currentServerDomain());
            contactMap.insert("displayName", id);

            contactMap.insert("url", m_pumpController->currentServerScheme()
                                     + m_pumpController->currentServerDomain()
                                     + "/" + id);

            QVariantMap hrefMap;
            hrefMap.insert("href", m_pumpController->currentServerScheme()
                                   + m_pumpController->currentServerDomain()
                                   + "/api/user/" + id + "/feed");
            QVariantMap outboxMap;
            outboxMap.insert("activity-outbox", hrefMap);
            contactMap.insert("links", outboxMap);
        }


        ASPerson *person = new ASPerson(contactMap, this);
        ContactCard *contactCard = new ContactCard(m_pumpController,
                                                   m_globalObject,
                                                   person,
                                                   this);
        m_contactsLayout->addWidget(contactCard);
        m_contactsInList.append(contactCard);

        if (m_isFollowing)
        {
            // Add to internal following list for PumpController
            followingIdStringList.append(person->getId());

            // Add also to GlobalObject's model for nick completion
            m_globalObject->addToNickCompletionModel(person->getId(),
                                                     person->getNameWithFallback(),
                                                     person->getUrl());
        }

        delete person;
    } ///// End foreach


    // Batch of contacts added to list, add them also to the internal list
    if (m_isFollowing)
    {
        m_pumpController->updateInternalFollowingIdList(followingIdStringList);
    }

    m_filterLineEdit->clear(); // Unfilter the list
}



QString ContactList::getContactsStringForExport()
{
    QString allContactsString;

    foreach (ContactCard *card, m_contactsInList)
    {
        allContactsString.append(card->getNameAndIdString() + "\n");
    }

    return allContactsString;
}



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


void ContactList::filterList(QString filterText)
{
    qDebug() << "Filtering for contacts matching:" << filterText;

    if (!filterText.isEmpty())
    {
        int matchCount = 0;
        foreach (ContactCard *card, m_contactsInList)
        {
            if (card->getNameAndIdString().contains(filterText, Qt::CaseInsensitive))
            {
                card->show();
                ++matchCount;
            }
            else
            {
                card->hide();
            }
        }

        m_matchesCountLabel->setText(QString("(%1)")
                                     .arg(QLocale::system()
                                                  .toString(matchCount)));
        m_matchesCountLabel->show();
        m_clearFilterButton->setEnabled(true);
    }
    else // If no filter at all, more optimized version showing all
    {
        foreach (ContactCard *card, m_contactsInList)
        {
            card->show();
        }

        m_matchesCountLabel->setText(QString());
        m_matchesCountLabel->hide();
        m_clearFilterButton->setDisabled(true);
    }
}


void ContactList::addSingleContact(ASPerson *contact)
{
    ContactCard *card = new ContactCard(m_pumpController,
                                        m_globalObject,
                                        contact,
                                        this);

    m_contactsLayout->insertWidget(0, card);
    m_contactsInList.append(card);

    emit contactCountChanged(1);

    // This check is actually unnecessary, since this slot is only called
    if (m_isFollowing) // for the 'following' list
    {
        QStringList contactsToAdd = QStringList{ contact->getId() };
        m_pumpController->updateInternalFollowingIdList(contactsToAdd);

        // Add also to GlobalObject's model for nick completion
        m_globalObject->addToNickCompletionModel(contact->getId(),
                                                 contact->getNameWithFallback(),
                                                 contact->getUrl());
    }

    // Other slots use this object, but this should be safe
    contact->deleteLater();
}


void ContactList::removeSingleContact(ASPerson *contact)
{
    foreach (ContactCard *card, m_contactsInList)
    {
        /* Ignore disabled cards, to avoid substracting more than once, in case
         * there were more ContactCards for the same contact, from following
         * and unfollowing previously.
         *
         */
        if (card->isEnabled() &&
            card->getId() == contact->getId())
        {
            emit contactCountChanged(-1);

            m_pumpController->removeFromInternalFollowingList(contact->getId());

            // Remove from GlobalObject's model for nick completion, too
            m_globalObject->removeFromNickCompletionModel(contact->getId());

            card->setDisabled(true);
        }
    }

    contact->deleteLater();
}


/*
 * When pressing Ctrl+F, focus filter field,
 * or select all text if already focused
 *
 */
void ContactList::focusFilterField()
{
    if (m_filterLineEdit->hasFocus())
    {
        m_filterLineEdit->selectAll();
    }
    else
    {
        m_filterLineEdit->setFocus();
    }
}
