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

AudienceSelector::AudienceSelector(PumpController *pumpController,
                                   QString selectorType,
                                   QWidget *parent) : QFrame(parent)
{
    m_selectorType = selectorType;
    m_pumpController = pumpController;

    QString titlePart;
    if (m_selectorType == QStringLiteral("to"))
    {
        titlePart = tr("'To' List");
    }
    else
    {
        titlePart = tr("'Cc' List");
    }

    this->setWindowTitle(titlePart + " - Dianara");
    this->setWindowIcon(QIcon::fromTheme("system-users",
                                         QIcon(":/images/button-users.png")));
    this->setWindowFlags(Qt::Dialog);
    this->setWindowModality(Qt::WindowModal);
    this->setMinimumSize(440, 340);

    QSettings settings;
    QSize savedWindowsize = settings.value("AudienceSelector/"
                                           "audienceWindowSize").toSize();
    if (savedWindowsize.isValid())
    {
        this->resize(savedWindowsize);
    }



    // Upper-left side, all contacts, with filter
    m_peopleWidget = new PeopleWidget(tr("&Add to Selected") + " >>",
                                      PeopleWidget::EmbeddedWidget,
                                      m_pumpController,
                                      this);
    connect(m_peopleWidget, &PeopleWidget::contactSelected,
            this, &AudienceSelector::copyToSelected);
    connect(m_peopleWidget, &PeopleWidget::addButtonPressed,
            this, &AudienceSelector::copyToSelected);



    m_allGroupboxLayout = new QVBoxLayout();
    m_allGroupboxLayout->setContentsMargins(0, 0, 0, 0);
    m_allGroupboxLayout->addWidget(m_peopleWidget);

    m_allContactsGroupbox = new QGroupBox(tr("All Contacts"),
                                          this);
    m_allContactsGroupbox->setLayout(m_allGroupboxLayout);


    // Upper-right side, selected contacts
    m_explanationLabel = new QLabel(tr("Select people from the list on the left.\n"
                                       "You can drag them with the mouse, click or "
                                       "double-click on them, or select them and "
                                       "use the button below.",
                                       "ON THE LEFT should change to ON THE "
                                       "RIGHT in RTL languages"),
                                    this);
    m_explanationLabel->setWordWrap(true);

    m_selectedListWidget = new QListWidget(this);
    m_selectedListWidget->setDragDropMode(QListView::DragDrop);
    m_selectedListWidget->setDefaultDropAction(Qt::MoveAction);
    m_selectedListWidget->setSelectionMode(QListView::ExtendedSelection);
    m_selectedListWidget->setIconSize(QSize(48, 48));

    m_clearSelectedListButton = new QPushButton(QIcon::fromTheme("edit-clear-list",
                                                                 QIcon(":/images/button-delete.png")),
                                                tr("Clear &List"),
                                                this);
    connect(m_clearSelectedListButton, &QAbstractButton::clicked,
            m_selectedListWidget, &QListWidget::clear);


    m_selectedGroupboxLayout = new QVBoxLayout();
    m_selectedGroupboxLayout->addWidget(m_explanationLabel);
    m_selectedGroupboxLayout->addSpacing(8);
    m_selectedGroupboxLayout->addWidget(m_selectedListWidget);
    m_selectedGroupboxLayout->addWidget(m_clearSelectedListButton, 0, Qt::AlignLeft);

    m_selectedListGroupbox = new QGroupBox(tr("Selected People"),
                                           this);
    m_selectedListGroupbox->setLayout(m_selectedGroupboxLayout);

    m_upperLayout = new QHBoxLayout();
    m_upperLayout->addWidget(m_allContactsGroupbox,  3);
    m_upperLayout->addWidget(m_selectedListGroupbox, 4);


    // Lower part
    m_doneButton = new QPushButton(QIcon::fromTheme("dialog-ok",
                                                    QIcon(":/images/button-save.png")),
                                   tr("&Done"),
                                   this);
    connect(m_doneButton, &QAbstractButton::clicked,
            this, &AudienceSelector::setAudience);

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

    // Group buttons in an environment-aware layout
    m_bottomButtonBox = new QDialogButtonBox(this);
    m_bottomButtonBox->addButton(m_doneButton, QDialogButtonBox::AcceptRole);
    m_bottomButtonBox->addButton(m_cancelButton, QDialogButtonBox::RejectRole);


    // Layout
    m_mainLayout = new QVBoxLayout();
    m_mainLayout->addLayout(m_upperLayout);
    m_mainLayout->addWidget(m_bottomButtonBox);
    this->setLayout(m_mainLayout);


    // Ctrl+Enter is the same as the "Done" button
    m_doneAction = new QAction(this);
    QList<QKeySequence> doneShortcuts;
    doneShortcuts << QKeySequence("Ctrl+Return")
                  << QKeySequence("Ctrl+Enter");
    m_doneAction->setShortcuts(doneShortcuts);
    connect(m_doneAction, &QAction::triggered,
            this, &AudienceSelector::setAudience);
    this->addAction(m_doneAction);

    // ESC is the same as the "Cancel" button
    m_cancelAction = new QAction(this);
    m_cancelAction->setShortcut(Qt::Key_Escape);
    connect(m_cancelAction, &QAction::triggered,
            this, &QWidget::close);
    this->addAction(m_cancelAction);


/////////////////////////////////////////////////////////////////////////////


    // Setup To/Cc menu to be shown on the Publisher
    m_publicAction = new QAction(tr("Public"), this);
    m_publicAction->setCheckable(true);
    connect(m_publicAction, &QAction::toggled,
            this, &AudienceSelector::onPublicToggled);

    m_followersAction = new QAction(tr("Followers"), this);
    m_followersAction->setCheckable(true);
    connect(m_followersAction, &QAction::toggled,
            this, &AudienceSelector::onFollowersToggled);

    m_listsMenu = new QMenu(tr("Lists"), this);
    m_listsMenu->setDisabled(true); // Disabled until lists are received, if any
    connect(m_listsMenu, &QMenu::triggered,
            this, &AudienceSelector::onListToggled);

    m_selectorMenu = new QMenu(QStringLiteral("to-menu"), this);
    m_selectorMenu->addAction(m_publicAction);
    m_selectorMenu->addAction(m_followersAction);
    m_selectorMenu->addMenu(m_listsMenu);
    m_selectorMenu->addSeparator();
    m_selectorMenu->addAction(tr("People..."),
                              this, SLOT(show()));


    qDebug() << "AudienceSelector created" << titlePart;
}


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



/*
 * Reset lists and widgets to default status
 *
 */
void AudienceSelector::resetLists()
{
    m_peopleWidget->resetWidget();

    m_selectedListWidget->clear();
    restoreSelected();
}

void AudienceSelector::deletePrevious()
{
    foreach (QListWidgetItem *item, m_previousItems)
    {
        delete item;
    }
    m_previousItems.clear();
}


void AudienceSelector::saveSelected()
{
    qDebug() << "AudienceSelector::saveSelected()";
    // Clear and delete all first
    this->deletePrevious();

    const int totalItems = m_selectedListWidget->count();
    for (int counter = 0; counter < totalItems; ++counter)
    {
        m_previousItems.append(m_selectedListWidget->item(counter)->clone());
    }
}


void AudienceSelector::restoreSelected()
{
    qDebug() << "AudienceSelector::restoreSelected()";
    foreach (QListWidgetItem *item, m_previousItems)
    {
        m_selectedListWidget->addItem(item->clone());
    }
}


QMenu *AudienceSelector::getSelectorMenu()
{
    return m_selectorMenu;
}


void AudienceSelector::setDefaultAudience(bool toPublic)
{
    // Check "public" if "public posts" is set in the preferences
    m_publicAction->setChecked(toPublic);

    // Cc: Followers by default
    m_followersAction->setChecked(m_selectorType == QStringLiteral("cc"));

    // Uncheck the person lists
    foreach (QAction *action, m_listsMenu->actions())
    {
        action->setChecked(false);
    }


    // Clear individual recipients
    deletePrevious();
    resetLists();
}


void AudienceSelector::clearPublicAndFollowers()
{
    setPublic(false);
    setFollowers(false);
}


void AudienceSelector::setPublic(bool state)
{
    m_publicAction->setChecked(state);
}


bool AudienceSelector::isPublicSelected()
{
    return m_publicAction->isChecked();
}


void AudienceSelector::setFollowers(bool state)
{
    m_followersAction->setChecked(state);
}


bool AudienceSelector::isFollowersSelected()
{
    return m_followersAction->isChecked();
}


void AudienceSelector::setListsMenu(QVariantList newLists)
{
    // First, clear the menu
    m_listsMenu->clear(); // clear() should delete the existing actions

    if (newLists.length() > 0) // If there are some lists, enable the menu
    {
        m_listsMenu->setEnabled(true);
    }


    foreach (QVariant list, newLists)
    {
        const QVariantMap listMap = list.toMap();

        QAction *listAction = new QAction(listMap.value("displayName").toString(),
                                          this);
        listAction->setCheckable(true);
        listAction->setData(listMap.value("id"));

        m_listsMenu->addAction(listAction);
    }
}


void AudienceSelector::checkListWithId(QString id)
{
    foreach (QAction *listAction, m_listsMenu->actions())
    {
        if (listAction->data().toString() == id)
        {
            qDebug() << "Checking matching list:" << listAction->text();
            listAction->setChecked(true);

            emit audienceChanged();

            break;
        }
    }
}


QString AudienceSelector::updatedAudienceLabels()
{
    QString audienceString;

    const int individualsCount = m_selectedListWidget->count();
    for (int counter=0; counter < individualsCount; ++counter)
    {
        QListWidgetItem *item = m_selectedListWidget->item(counter);

        audienceString.append("<a href=\""
                              + item->data(Qt::UserRole + 3).toString()
                              + "\">"
                              + item->data(Qt::UserRole + 1).toString()
                              + "</a>, ");
    }

    if (!audienceString.isEmpty())
    {
        audienceString.remove(-2, 2); // Remove last comma
        audienceString.append(QStringLiteral("<br />"));
    }


    // Person lists
    foreach (QAction *action, m_listsMenu->actions())
    {
        if (action->isChecked())
        {
            audienceString.append(QString::fromUtf8("\342\236\224 ") // arrow sign in front
                                  + action->text()
                                  + "<br />");
        }
    }

    // Public
    if (m_publicAction->isChecked())
    {
        audienceString.append("<b>+" + tr("Public") + "</b><br />");
    }

    // Followers
    if (m_followersAction->isChecked())
    {
        audienceString.append("<b>+" + tr("Followers") + "</b><br />");
    }

    return audienceString;
}


/*
 * Create an array of key:value maps, listing who will receive a post, like:
 *
 * {
 *     "objectType": "collection",
 *     "id":         "http://activityschema.org/collection/public"
 * }
 *
 * {
 *     "objectType": "group",
 *     "id":         "https://pump.example/api/user/group/someGroupId123"
 * }
 *
 * {
 *     "objectType": "person",
 *     "id":         "acct:somecontact@pumpserver.example"
 * }
 *
 */
QVariantList AudienceSelector::getAudienceList(bool *onlyToFollowers)
{
    QVariantList audienceList;
    QVariantMap audienceItemMap;

    // Public is checked
    if (isPublicSelected())
    {
        audienceItemMap.clear();
        audienceItemMap.insert("objectType", "collection");
        audienceItemMap.insert("id", "http://activityschema.org/collection/public");

        audienceList.append(audienceItemMap);
        *onlyToFollowers = false;
    }

    // Followers is checked
    if (isFollowersSelected())
    {
        audienceItemMap.clear();
        audienceItemMap.insert("objectType", "collection");
        audienceItemMap.insert("id", m_pumpController->currentFollowersUrl());

        audienceList.append(audienceItemMap);
    }

    // Individual people
    const int individualsCount = m_selectedListWidget->count();
    for (int counter=0; counter < individualsCount; ++counter)
    {
        QListWidgetItem *item = m_selectedListWidget->item(counter);

        audienceItemMap.clear();
        audienceItemMap.insert("objectType", "person");
        audienceItemMap.insert("id",
                               "acct:" + item->data(Qt::UserRole + 2).toString());

        audienceList.append(audienceItemMap);
        *onlyToFollowers = false;
    }

    // Lists
    foreach (QAction *listAction, m_listsMenu->actions())
    {
        if (listAction->isChecked())
        {
            audienceItemMap.clear();
            audienceItemMap.insert("objectType", "collection");
            audienceItemMap.insert("id", listAction->data());

            audienceList.append(audienceItemMap);
            *onlyToFollowers = false;
        }
    }


    // Groups -- FIXME -- TODO

    return audienceList;
}


int AudienceSelector::getRecipientsCount()
{
    return m_selectedListWidget->count();
}



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



/*
 * Copy a contact to the list of Selected
 *
 * The contact string comes in a signal from PeopleWidget
 *
 */
void AudienceSelector::copyToSelected(QIcon contactIcon, QString contactString,
                                      QString contactName, QString contactId,
                                      QString contactUrl)
{
    if (!contactString.isEmpty())
    {
        const int itemExists = m_selectedListWidget->findItems(contactString,
                                                               Qt::MatchExactly)
                                                    .size();
        if (itemExists == 0)
        {
            QListWidgetItem *item = new QListWidgetItem(contactIcon,
                                                        contactString);
            item->setData(Qt::UserRole + 1, contactName);
            item->setData(Qt::UserRole + 2, contactId);
            item->setData(Qt::UserRole + 3, contactUrl);

            m_selectedListWidget->addItem(item);
        }
        else
        {
            qDebug() << "AudienceSelector::copyToSelected() "
                        "ignoring already added recipient";
        }
    }
}



/*
 * The "Done" button: emit signal with list of selected people
 *
 */
void AudienceSelector::setAudience()
{
    saveSelected();  // To restore the list later, if the dialog is shown again
    emit audienceChanged();

    this->hide();  // Don't close(), because that resets the lists =)
}


void AudienceSelector::onListToggled(QAction *listAction)
{
    qDebug() << "List checked:" << listAction->isChecked()
             << m_selectorType << listAction->text();

    emit audienceChanged();
}


void AudienceSelector::onPublicToggled(bool checked)
{
    emit audienceChanged();

    if (checked)
    {
        emit publicSelected();
    }
}


void AudienceSelector::onFollowersToggled(bool checked)
{
    emit audienceChanged();

    if (checked)
    {
        emit followersSelected();
    }
}



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



void AudienceSelector::closeEvent(QCloseEvent *event)
{
    this->resetLists();
    this->hide();

    event->accept();
}

void AudienceSelector::hideEvent(QHideEvent *event)
{
    QSettings settings;
    if (settings.isWritable())
    {
        settings.setValue("AudienceSelector/audienceWindowSize", this->size());
    }

    event->accept();
}
