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

Publisher::Publisher(PumpController *pumpController,
                     GlobalObject *globalObject,
                     QWidget *parent) : QWidget(parent)
{
    m_pumpController = pumpController;
    connect(m_pumpController, &PumpController::postPublished,
            this, &Publisher::onPublishingOk);
    connect(m_pumpController, &PumpController::postPublishingFailed,
            this, &Publisher::onPublishingFailed);

    // After receiving the list of lists, update the "Lists" submenus
    connect(m_pumpController, &PumpController::listsListReceived,
            this, &Publisher::updateListsMenus);


    m_globalObject = globalObject;
    connect(m_globalObject, &GlobalObject::messagingModeRequested,
            this, &Publisher::startMessageForContact);
    connect(m_globalObject, &GlobalObject::postEditRequested,
            this, &Publisher::setEditingMode);


    m_postType = "note";
    m_editingMode = false; // False unless set from setEditingMode
                           // after clicking "Edit" in a post


    m_toAudienceSelector = new AudienceSelector(m_pumpController, "to", this);
    connect(m_toAudienceSelector, &AudienceSelector::audienceChanged,
            this, &Publisher::updateAudienceToLabels);

    m_ccAudienceSelector = new AudienceSelector(m_pumpController, "cc", this);
    connect(m_ccAudienceSelector, &AudienceSelector::audienceChanged,
            this, &Publisher::updateAudienceCcLabels);

    // Track Public/Followers in To/Cc on one list to uncheck on the other
    connect(m_toAudienceSelector, &AudienceSelector::publicSelected,
            this, &Publisher::onToPublicSelected);
    connect(m_toAudienceSelector, &AudienceSelector::followersSelected,
            this, &Publisher::onToFollowersSelected);
    connect(m_ccAudienceSelector, &AudienceSelector::publicSelected,
            this, &Publisher::onCcPublicSelected);
    connect(m_ccAudienceSelector, &AudienceSelector::followersSelected,
            this, &Publisher::onCcFollowersSelected);


    QString titleTooltip = "<b></b>" + tr("Setting a title helps make the "
                                          "Meanwhile feed more informative");

    QFont titleFont;
    titleFont.setPointSize(titleFont.pointSize() + 1);
    titleFont.setBold(true);
    m_titleLabel = new QLabel(tr("Title") + ":", this);
    m_titleLabel->setFont(titleFont);
    m_titleLabel->setToolTip(titleTooltip);

    m_titleLineEdit = new QLineEdit(this);
    m_titleLineEdit->setPlaceholderText(tr("Add a brief title for the post here "
                                           "(recommended)"));
    m_titleLineEdit->setFont(titleFont);
    m_titleLineEdit->setToolTip(titleTooltip);


    m_pictureLabel = new QLabel(this);
    m_pictureLabel->setAlignment(Qt::AlignCenter);
    m_pictureLabel->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
    m_pictureLabel->hide();


    m_mediaInfoLabel = new QLabel(this);
    m_mediaInfoLabel->setWordWrap(true);
    m_mediaInfoLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
    m_mediaInfoLabel->setSizePolicy(QSizePolicy::Ignored,
                                    QSizePolicy::Preferred);

    m_selectMediaButton = new QPushButton(this);
    m_selectMediaButton->setSizePolicy(QSizePolicy::MinimumExpanding,
                                       QSizePolicy::Maximum);
    connect(m_selectMediaButton, &QAbstractButton::clicked,
            this, &Publisher::onSelectMediaFilePressed);
    m_selectMediaButton->hide();


    m_removeMediaButton = new QPushButton(QIcon::fromTheme("edit-delete",
                                                           QIcon(":/images/button-delete.png")),
                                          tr("Remove"),
                                          this);
    m_removeMediaButton->setToolTip("<b></b>"
                                    + tr("Cancel the attachment, and go "
                                         "back to a regular note"));
    m_removeMediaButton->setSizePolicy(QSizePolicy::MinimumExpanding,
                                       QSizePolicy::Maximum);
    connect(m_removeMediaButton, &QAbstractButton::clicked,
            this, &Publisher::cancelMediaMode);
    m_removeMediaButton->hide();


    m_uploadProgressBar = new QProgressBar(this);
    m_uploading = false;


    // Set default pixmap and "media not set" message
    this->setEmptyMediaData();
    m_lastUsedDirectory = QDir::homePath();


    // Composer
    m_composerBox = new Composer(m_globalObject,
                                 true,  // forPublisher = true
                                 this);
    m_composerBox->setSizePolicy(QSizePolicy::Minimum,
                                 QSizePolicy::MinimumExpanding);
    connect(m_composerBox, &Composer::focusReceived,
            this, &Publisher::setFullMode);
    connect(m_composerBox, &Composer::editingFinished,
            this, &Publisher::sendPost);
    connect(m_composerBox, &Composer::editingCancelled,
            this, &Publisher::setMinimumMode);
    connect(m_composerBox, &Composer::nickInserted,
            this, &Publisher::addNickToRecipients);
    connect(m_composerBox, &QTextEdit::textChanged,
            this, &Publisher::updateCharacterCounter);
    connect(m_composerBox, &Composer::fileDropped,
            this, &Publisher::onFileDropped);

    // Pressing Enter in title goes to message body
    connect(m_titleLineEdit, SIGNAL(returnPressed()),
            m_composerBox, SLOT(setFocus()));

    // Likewise, pressing UP at the start of the body goes to title
    connect(m_composerBox, SIGNAL(focusTitleRequested()),
            m_titleLineEdit, SLOT(setFocus()));


#ifdef HAVE_KCHARSELECT
    m_charPickerButton = m_composerBox->getCharPickerButton();
#endif

    // Add formatting button exported from Composer
    m_toolsButton = m_composerBox->getToolsButton();

    // Add menu and submenus to access drafts from the Draft Manager
    m_draftsManager = new DraftsManager(m_globalObject, this);
    connect(m_draftsManager, &DraftsManager::draftSelected,
            this, &Publisher::onDraftSelected);
    connect(m_draftsManager, &DraftsManager::saveDraftRequested,
            this, &Publisher::onSaveDraftRequested);
    connect(m_composerBox, &Composer::cancelSavingDraftRequested,
            this, &Publisher::onCancelSavingDraftRequested);
    // Set focus back to the composer upon selecting "Manage drafts"
    connect(m_draftsManager, &DraftsManager::windowShown,
            this, &Publisher::setFullMode);

    m_draftsButton = new QPushButton(QIcon::fromTheme("document-save-as",
                                                      QIcon(":/images/button-edit.png")),
                                     tr("Drafts"),
                                     this);
    m_draftsMenu = m_draftsManager->getDraftMenu();
    m_draftsButton->setMenu(m_draftsMenu);


    // To... menu
    m_toSelectorMenu = m_toAudienceSelector->getSelectorMenu();
    m_toSelectorButton = new QPushButton(QIcon::fromTheme("system-users",
                                                          QIcon(":/images/button-users.png")),
                                         tr("To..."),
                                         this);
    m_toSelectorButton->setToolTip("<b></b>"
                                   + tr("Select who will see this post"));
    m_toSelectorButton->setMenu(m_toSelectorMenu);


    // Cc... menu
    m_ccSelectorMenu = m_ccAudienceSelector->getSelectorMenu();
    m_ccSelectorButton = new QPushButton(QIcon::fromTheme("system-users",
                                                          QIcon(":/images/button-users.png")),
                                         tr("Cc..."),
                                         this);
    m_ccSelectorButton->setToolTip("<b></b>"
                                   + tr("Select who will get a copy of this post"));
    m_ccSelectorButton->setMenu(m_ccSelectorMenu);


    QFont audienceLabelsFont; // "To" column will be normal, "Cc" will be italic
    audienceLabelsFont.setPointSize(audienceLabelsFont.pointSize() - 1);

    // These will hold the names of the people and lists selected for the To or Cc fields, if any
    m_toAudienceLabel = new QLabel(this);
    m_toAudienceLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    m_toAudienceLabel->setFont(audienceLabelsFont);  // Normal
    m_toAudienceLabel->setWordWrap(true);
    m_toAudienceLabel->setOpenExternalLinks(true);
    connect(m_toAudienceLabel, &QLabel::linkHovered,
            this, &Publisher::showHighlightedUrl);

    m_ccAudienceLabel = new QLabel(this);
    m_ccAudienceLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    audienceLabelsFont.setItalic(true);
    m_ccAudienceLabel->setFont(audienceLabelsFont);  // Italic
    m_ccAudienceLabel->setWordWrap(true);
    m_ccAudienceLabel->setOpenExternalLinks(true);
    connect(m_ccAudienceLabel, &QLabel::linkHovered,
            this, &Publisher::showHighlightedUrl);



    // Menu to set the picture/audio/video modes, under an "Add..." button
    m_addMediaImageAction = new QAction(QIcon::fromTheme("camera-photo",
                                                         QIcon(":/images/attached-image.png")),
                                        tr("Picture"),
                                        this);
    connect(m_addMediaImageAction, &QAction::triggered,
            this, &Publisher::setPictureMode);

    m_addMediaAudioAction = new QAction(QIcon::fromTheme("audio-input-microphone",
                                                         QIcon(":/images/attached-audio.png")),
                                        tr("Audio"),
                                        this);
    connect(m_addMediaAudioAction, &QAction::triggered,
            this, &Publisher::setAudioMode);

    m_addMediaVideoAction = new QAction(QIcon::fromTheme("camera-web",
                                                         QIcon(":/images/attached-video.png")),
                                        tr("Video"),
                                        this);
    connect(m_addMediaVideoAction, &QAction::triggered,
            this, &Publisher::setVideoMode);

    m_addMediaFileAction = new QAction(QIcon::fromTheme("application-octet-stream",
                                                        QIcon(":/images/attached-file.png")),
                                       tr("Other", "as in other kinds of files"),
                                       this);
    connect(m_addMediaFileAction, &QAction::triggered,
            this, &Publisher::setFileMode);


    // The menu itself
    m_addMediaMenu = new QMenu(QStringLiteral("add-media-menu"), this);
    m_addMediaMenu->addAction(m_addMediaImageAction);
    m_addMediaMenu->addAction(m_addMediaAudioAction);
    m_addMediaMenu->addAction(m_addMediaVideoAction);
    m_addMediaMenu->addAction(m_addMediaFileAction);

    // The "add..." button holding the menu
    m_addMediaButton = new QPushButton(QIcon::fromTheme("mail-attachment",
                                                        QIcon(":/images/list-add.png")),
                                       tr("Ad&d..."),
                                       this);
    m_addMediaButton->setToolTip("<b></b>"
                                 + tr("Upload media, like pictures or videos"));
    m_addMediaButton->setMenu(m_addMediaMenu);


    // Character counter (optional)
    m_charCounterLabel = new QLabel(QStringLiteral("0"), this);
    m_charCounterLabel->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);


    // Info label for "sending" and other status information
    m_statusInfoLabel = new QLabel(this);
    m_statusInfoLabel->setSizePolicy(QSizePolicy::Maximum,
                                     QSizePolicy::Maximum);
    m_statusInfoLabel->setAlignment(Qt::AlignCenter);
    m_statusInfoLabel->setWordWrap(true);
    audienceLabelsFont.setBold(false);
    audienceLabelsFont.setItalic(false);
    m_statusInfoLabel->setFont(audienceLabelsFont);
    connect(m_composerBox, &Composer::errorHappened,
            m_statusInfoLabel, &QLabel::setText);


    // To send the post
    m_postButton = new QPushButton(QIcon::fromTheme("mail-send",
                                                    QIcon(":/images/button-post.png")),
                                   tr("Post", "verb"),
                                   this);
    m_postButton->setToolTip("<b></b>"
                             + tr("Hit Control+Enter to post with the keyboard"));
    connect(m_postButton, &QAbstractButton::clicked,
            this, &Publisher::sendPost);

    m_cancelButton = new QPushButton(QIcon::fromTheme("dialog-cancel",
                                                      QIcon(":/images/button-cancel.png")),
                                     tr("Cancel"),
                                     this);
    m_cancelButton->setToolTip("<b></b>"
                               + tr("Cancel the post"));
    connect(m_cancelButton, &QAbstractButton::clicked,
            m_composerBox, &Composer::cancelPost);


    // Now the layout, starting with the Title field and "media mode" stuff
    m_titleLayout = new QHBoxLayout();
    m_titleLayout->addWidget(m_titleLabel,    0);
    m_titleLayout->addSpacing(4);
    m_titleLayout->addWidget(m_titleLineEdit, 2);
    m_titleLayout->addSpacing(4);
#ifdef HAVE_KCHARSELECT
    m_titleLayout->addWidget(m_charPickerButton, 0);
    m_titleLayout->addSpacing(4);
#endif
    m_titleLayout->addWidget(m_toolsButton,   0);
    m_titleLayout->addSpacing(2);
    m_titleLayout->addWidget(m_draftsButton,  0);

    m_mediaLayout = new QGridLayout();
    m_mediaLayout->setVerticalSpacing(0);
    m_mediaLayout->setContentsMargins(0, 0, 0, 0);
    m_mediaLayout->addWidget(m_pictureLabel,        1, 0, 2, 4);
    m_mediaLayout->addWidget(m_mediaInfoLabel,      1, 4, 1, 4, Qt::AlignTop);
    m_mediaLayout->addWidget(m_selectMediaButton,   2, 4, 1, 2, Qt::AlignBottom | Qt::AlignLeft);
    m_mediaLayout->addWidget(m_uploadProgressBar,   2, 6, 1, 2, Qt::AlignBottom | Qt::AlignRight);
    m_mediaLayout->addWidget(m_removeMediaButton,   2, 6, 1, 2, Qt::AlignBottom | Qt::AlignRight);

    m_buttonsLayout = new QGridLayout();
    m_buttonsLayout->setVerticalSpacing(0);
    m_buttonsLayout->setContentsMargins(0, 0, 0, 0);
    m_buttonsLayout->addWidget(m_toSelectorButton,       0, 0, 1, 1, Qt::AlignLeft);
    m_buttonsLayout->addWidget(m_ccSelectorButton,       0, 1, 1, 1, Qt::AlignLeft);
    m_buttonsLayout->addWidget(m_addMediaButton,         0, 3, 1, 2, Qt::AlignCenter);
    m_buttonsLayout->addWidget(m_statusInfoLabel,        0, 5, 3, 2, Qt::AlignCenter);
    m_buttonsLayout->addWidget(m_postButton,             0, 7, 1, 1);

    // The 2 labels holding Public, Followers, Lists and people's names
    m_buttonsLayout->addWidget(m_toAudienceLabel,        1, 0, 2, 1);
    m_buttonsLayout->addWidget(m_ccAudienceLabel,        1, 1, 2, 1);

    // Character counter
    m_buttonsLayout->addWidget(m_charCounterLabel,       1, 3, 1, 2, Qt::AlignCenter);

    // The "Cancel post" button
    m_buttonsLayout->addWidget(m_cancelButton,           1, 7, 1, 1);

#ifdef GROUPSUPPORT
    m_groupIdLabel = new QLabel("TO GROUP (ID):", this);
    m_buttonsLayout->addWidget(m_groupIdLabel,      3, 0, 1, 1);
    m_groupIdLineEdit = new QLineEdit(this);
    m_buttonsLayout->addWidget(m_groupIdLineEdit,   3, 1, 1, 7);
#endif


    m_mainLayout = new QVBoxLayout();
    m_mainLayout->setContentsMargins(0, 0, 0, 0);
    m_mainLayout->setSpacing(0);
    m_mainLayout->addLayout(m_titleLayout,   0);
    m_mainLayout->addSpacing(1);
    m_mainLayout->addLayout(m_mediaLayout,   0);
    m_mainLayout->addWidget(m_composerBox,   3);
    m_mainLayout->addLayout(m_buttonsLayout, 0);
    this->setLayout(m_mainLayout);


    this->setMinimumMode();

    qDebug() << "Publisher created";
}


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


/*
 * Set To:Public if "public posts as default" option for audience is enabled
 *
 */
void Publisher::syncFromConfig()
{
    m_toAudienceSelector->setPublic(m_globalObject->getPublicPostsByDefault());
}


/*
 * Set default "no photo" pixmap and "picture/audio/video not set" message
 *
 * Clear the filename and content type variables too
 */
void Publisher::setEmptyMediaData()
{
    m_pictureLabel->setToolTip(QString());

    m_mediaInfoLabel->clear();

    m_mediaFilename.clear();
    m_mediaContentType.clear();
}


/*
 * Set title and content from the outside, for automation (dbus interface)
 *
 */
void Publisher::setTitleAndContent(QString title, QString content)
{
    QString statusText;

    if (emptyContents())
    {
        m_titleLineEdit->setText(title.trimmed());
        m_composerBox->setText(content.trimmed());
        m_composerBox->moveCursor(QTextCursor::End);

        statusText = tr("Note started from another application."); // FIXME?
    }
    else
    {
        statusText = tr("Ignoring new note request from another application."); // TMP message FIXME
    }

    m_statusInfoLabel->setText(statusText);
}



QVariantMap Publisher::getAudienceMap()
{
    QVariantMap fullAudienceMap;

    // Used to warn the user when posting only to followers having none
    m_onlyToFollowers = true; // Will be set to false if something else is enabled

    fullAudienceMap.insert("to", m_toAudienceSelector->getAudienceList(&m_onlyToFollowers));
    fullAudienceMap.insert("cc", m_ccAudienceSelector->getAudienceList(&m_onlyToFollowers));


#ifdef GROUPSUPPORT
    // Cc: Groups
    if (!m_groupIdLineEdit->text().trimmed().isEmpty())
    {
        m_onlyToFollowers = false;

        QVariantMap groupMap;
        groupMap.insert("objectType", "group");
        groupMap.insert("id", m_groupIdLineEdit->text().trimmed());

        QVariantList ccList = fullAudienceMap.value("cc").toList();
        ccList.append(groupMap);

        fullAudienceMap.insert("cc", groupMap);
    }
#endif


    // Last check: if nothing is checked, add Cc:Followers
    if (fullAudienceMap.isEmpty()) // FIXME: maybe fail and warn the user instead
    {
        QVariantMap ccFollowersMap;
        ccFollowersMap.insert("objectType", "collection");
        ccFollowersMap.insert("id", m_pumpController->currentFollowersUrl());

        QVariantList ccList = fullAudienceMap.value("cc").toList();
        ccList.append(ccFollowersMap);

        fullAudienceMap.insert("cc", ccFollowersMap);
    }

    return fullAudienceMap;
}


void Publisher::setAudienceFromMap(QVariantMap audienceMap)
{
    // First, uncheck the ones that might be enabled due to config
    m_toAudienceSelector->setDefaultAudience(false);
    m_ccAudienceSelector->setDefaultAudience(false);
    m_ccAudienceSelector->setFollowers(false);

    qDebug() << "***** SETTING AUDIENCE FROM DRAFT MAP:";
    qDebug() << audienceMap;

    foreach (const QString listType, audienceMap.keys()) // "to", "cc", ATM
    {
        qDebug() << "LIST:" << listType;
        const QVariantList audienceList = audienceMap.value(listType).toList();

        foreach (const QVariant audienceItem, audienceList)
        {
            QVariantMap audienceItemMap = audienceItem.toMap();
            qDebug() << "Audience item:" << audienceItemMap;

            QString id = audienceItemMap.value("id").toString();
            if (id.isEmpty())
            {
                // Avoid for example accepting empty Followers ID as valid
                break;
            }

            QString objectType = audienceItemMap.value("objectType").toString();
            if (objectType == "collection")
            {
                if (id == QStringLiteral("http://activityschema.org/collection/public"))
                {
                    if (listType == "to")
                    {
                        m_toAudienceSelector->setPublic(true);
                    }
                    else if (listType == "cc")
                    {
                        m_ccAudienceSelector->setPublic(true);
                    }
                    qDebug() << "Checking PUBLIC";
                }
                else if (id == m_pumpController->currentFollowersUrl())
                {
                    if (listType == "to")
                    {
                        m_toAudienceSelector->setFollowers(true);
                    }
                    else if (listType == "cc")
                    {
                        m_ccAudienceSelector->setFollowers(true);
                    }
                    qDebug() << "Checking FOLLOWERS";
                }
                else
                {
                    // Determine if any of the person lists needs to be checked
                    qDebug() << "Looking for LISTS to check, with ID:" << id;
                    if (listType == "to")
                    {
                        m_toAudienceSelector->checkListWithId(id);
                    }
                    else if (listType == "cc")
                    {
                        m_ccAudienceSelector->checkListWithId(id);
                    }
                }
            }
            else if (objectType == "person")
            {
                id = ASPerson::cleanupId(id);
                QPair<QString,QString> personData = m_globalObject->getDataForNick(id);

                this->addNickToRecipients(id,
                                          personData.first, personData.second,
                                          listType);
            }
            else if (objectType == "group")
            {
                // TODO: Check corresponding groups, when group support is complete
                // GROUPSUPPORT -- FIXME -- TODO
            }
        }
    }
}


void Publisher::setMediaModeWidgets()
{
    m_addMediaButton->setDisabled(true);

    m_pictureLabel->show();

    m_mediaInfoLabel->show();
    m_selectMediaButton->show();
    m_removeMediaButton->show();
}


/*
 * Let users find a file in their folders, for media upload
 *
 */
void Publisher::findMediaFile()
{
    QString findDialogTitle;
    QString mediaTypes;
    QString errorTitle;
    QString errorMessage;

    if (m_postType == QStringLiteral("image"))
    {
        findDialogTitle = tr("Select one image");
        mediaTypes = tr("Image files")
                   + MiscHelpers::fileFilterString(MiscHelpers::imageExtensions());

        errorTitle =  tr("Invalid image");
        errorMessage = tr("The image format cannot be detected.\n"
                          "The extension might be wrong, like a GIF "
                          "image renamed to image.jpg or similar.");
    }
    else if (m_postType == QStringLiteral("audio"))
    {
        findDialogTitle = tr("Select one audio file");
        mediaTypes = tr("Audio files")
                   + MiscHelpers::fileFilterString(MiscHelpers::audioExtensions());

        errorTitle = tr("Invalid audio file");
        errorMessage = tr("The audio format cannot be detected.");
    }
    else if (m_postType == QStringLiteral("video"))
    {
        findDialogTitle = tr("Select one video file");
        mediaTypes = tr("Video files")
                   + MiscHelpers::fileFilterString(MiscHelpers::videoExtensions());

        errorTitle = tr("Invalid video file");
        errorMessage = tr("The video format cannot be detected.");
    }
    else // File
    {
        findDialogTitle = tr("Select one file");
        mediaTypes = QString();

        errorTitle = tr("Invalid file");
        errorMessage = tr("The file type cannot be detected.");
    }


    // Set a possible initial filename, if loading a draft or DnD'ing a file
    QString filename = m_mediaFilename;
    if (filename.isEmpty())
    {
        filename = QFileDialog::getOpenFileName(this,
                                                findDialogTitle,
                                                m_lastUsedDirectory,
                                                mediaTypes
                                                + tr("All files") + " (*)");
    }


    if (!filename.isEmpty())
    {
        qDebug() << "Selected" << filename << "for upload";
        QFileInfo fileInfo(filename);
        m_mediaContentType = MiscHelpers::getFileMimeType(filename);

        // Temporary protection; https://github.com/pump-io/pump.io/issues/1015
        if (fileInfo.size() > 10485760) // 10 MiB; this protects the servers from abuse
        {
            QString bigImageNote;
            if (m_postType == QStringLiteral("image"))
            {
                bigImageNote = QStringLiteral("\n\n")
                               + tr("Since you're uploading an image, you could "
                                    "scale it down a little or save it in a "
                                    "more compressed format, like JPG.");
            }

            QMessageBox::warning(this, tr("File is too big"),
                                 tr("Dianara currently limits file uploads "
                                    "to 10 MiB per post, to prevent possible "
                                    "storage or network problems in the "
                                    "servers.")
                                 + "\n\n"
                                 + tr("This is a temporary measure, since "
                                      "the servers cannot set their "
                                      "own limits yet.")
                                 + bigImageNote
                                 + "\n\n"
                                 + tr("Sorry for the inconvenience."));

            m_mediaFilename.clear();
        }
        else if (!fileInfo.isReadable()) // Make sure attachment can be read
        {
            this->reportUnreadableFile(filename, fileInfo);

            m_mediaFilename.clear();
        }
        else if (!m_mediaContentType.isEmpty())
        {
            m_pictureLabel->setToolTip(QStringLiteral("<b></b>") // wordwrapped
                                       + filename);

            m_mediaFilename = filename;

            QString metaDataString;
            if (m_postType == QStringLiteral("image"))
            {
                QPixmap imagePixmap = QPixmap(filename);
                if (imagePixmap.isNull())
                {
                    QMessageBox::warning(this, errorTitle, errorMessage);
                }
                m_pictureLabel->setPixmap(imagePixmap.scaled(266, 150,  // 16:9
                                                             Qt::KeepAspectRatio,
                                                             Qt::SmoothTransformation));
                metaDataString = "<br><br>"
                                 "<b>" + tr("Resolution",
                                            "Image resolution (size)")
                                 + ":</b> "
                                 + MiscHelpers::resolutionString(imagePixmap.width(),
                                                                 imagePixmap.height());
            }
            else
            {
                QMimeDatabase mimeDb;
                const QString mimeIcon = mimeDb.mimeTypeForFile(filename).iconName();
                if (QIcon::hasThemeIcon(mimeIcon))
                {
                    m_pictureLabel->setPixmap(QIcon::fromTheme(mimeIcon)
                                                    .pixmap(150, 150));
                }
            }

            m_lastUsedDirectory = fileInfo.path();
            qDebug() << "Last used directory:" << m_lastUsedDirectory;


            m_mediaInfoLabel->setText(QString("<b>%1</b>"
                                              "<br />"
                                              "<b>" + tr("Type") + ":</b> %2"
                                              "<br />"
                                              "<b>" + tr("Size") + ":</b> %3")
                                      .arg(fileInfo.fileName(),
                                           m_mediaContentType,
                                           MiscHelpers::fileSizeString(filename))
                                      + metaDataString);

            // If there's no title, set one from the filename
            if (m_titleLineEdit->text().trimmed().isEmpty())
            {
                // But only if configured to do so
                if (m_globalObject->getUseFilenameAsTitle())
                {
                    QString titleFromFilename = fileInfo.fileName(); // .baseName() ?
                    titleFromFilename.replace("_", " ");
                    titleFromFilename.replace(".", " ");
                    m_titleLineEdit->setText(titleFromFilename);
                }
            }

            m_statusInfoLabel->clear(); // Remove possible previous error message
        }
        else
        {
            qDebug() << "Unknown " << m_postType << " format; Extension is probably wrong";
            QMessageBox::warning(this, errorTitle, errorMessage);

            m_mediaContentType.clear();
            m_mediaFilename.clear();
            m_pictureLabel->setToolTip(QString());
        }


        m_uploadProgressBar->hide();
    }
}


/*
 * Disable some widgets while sending a post,
 * including during media upload
 *
 */
void Publisher::toggleWidgetsWhileSending(bool widgetsEnabled)
{
    m_titleLineEdit->setEnabled(widgetsEnabled);
    m_composerBox->setEnabled(widgetsEnabled);

#ifdef HAVE_KCHARSELECT
    m_charPickerButton->setEnabled(widgetsEnabled);
#endif
    m_toolsButton->setEnabled(widgetsEnabled);
    m_draftsButton->setEnabled(widgetsEnabled);

    m_toSelectorButton->setEnabled(widgetsEnabled);
    m_ccSelectorButton->setEnabled(widgetsEnabled);

    // Only re-enable "add" button if this was still a simple note
    if (m_postType == QStringLiteral("note"))
    {
        m_addMediaButton->setEnabled(widgetsEnabled);
    }
    m_selectMediaButton->setEnabled(widgetsEnabled);
    m_removeMediaButton->setEnabled(widgetsEnabled);

    m_postButton->setEnabled(widgetsEnabled);
}


bool Publisher::isFullMode()
{
    return m_fullMode;
}


bool Publisher::emptyContents()
{
    if (!m_titleLineEdit->text().isEmpty())
    {
        return false;
    }

    if (!m_composerBox->toPlainText().isEmpty())
    {
        return false;
    }

    if (!m_mediaFilename.isEmpty())
    {
        return false;
    }

    if (m_toAudienceSelector->getRecipientsCount() > 0
     || m_ccAudienceSelector->getRecipientsCount() > 0)
    {
        return false;
    }

    return true;
}


void Publisher::reportUnreadableFile(QString filename, QFileInfo fileInfo)
{
    QString errorMessage = tr("The selected file cannot be accessed:")
                           + "\n\n"
                           + filename
                           + "\n\n\n";

    if (fileInfo.exists())
    {
        errorMessage.append(tr("It is owned by %1.",
                               "%1 = a username").arg(fileInfo.owner())
                            + "\n\n"
                            + tr("You might not have the necessary "
                                 "permissions."));
    }
    else
    {
        errorMessage.append(tr("File not found."));
    }

    QMessageBox::warning(this, tr("Error"), errorMessage);

    qDebug() << "FILE IS NOT READABLE!! " << filename;
    qDebug() << "Owner: " << fileInfo.owner();
}



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



void Publisher::setMinimumMode()
{
    qDebug() << "setting Publisher to minimum mode";

    m_postButton->setFocus(); // Give focus to button,
                              // in case user shared with Ctrl+Enter

    m_titleLabel->hide();
    m_titleLineEdit->clear();
    m_titleLineEdit->hide();

    m_toolsButton->hide();
#ifdef HAVE_KCHARSELECT
    m_charPickerButton->hide();
#endif
    m_draftsButton->hide();

    // Disable possible scrollbars
    m_composerBox->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    m_composerBox->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    // Disable Ctrl+Shift+V for plaintext paste, to avoid conflict with Commenters
    m_composerBox->setPlainPasteEnabled(false);

    // Keep the publisher from getting focus via tab key, by accident
    m_composerBox->setFocusPolicy(Qt::ClickFocus);


    // ~1 row
    int composerHeight = m_titleLineEdit->fontInfo().pixelSize() * 2; // kinda TMP
    m_composerBox->setMinimumHeight(composerHeight);
    m_composerBox->setMaximumHeight(composerHeight);

    this->setMinimumHeight(composerHeight + 2);
    this->setMaximumHeight(composerHeight + 2);

    // Clear formatting options like bold, italic and underline
    m_composerBox->setCurrentCharFormat(QTextCharFormat());


    m_toAudienceSelector->deletePrevious();
    m_toAudienceSelector->resetLists();
    m_toAudienceSelector->setDefaultAudience(m_globalObject->getPublicPostsByDefault());
    m_toAudienceLabel->setText(QStringLiteral("..."));
    m_toAudienceLabel->repaint(); // Avoid a flicker-like effect later
    m_toAudienceLabel->clear();
    m_toAudienceLabel->hide();
    m_toSelectorButton->hide();

    m_ccAudienceSelector->deletePrevious();
    m_ccAudienceSelector->resetLists();
    m_ccAudienceSelector->setDefaultAudience(false);
    m_ccAudienceLabel->setText(QStringLiteral("..."));
    m_ccAudienceLabel->repaint();
    m_ccAudienceLabel->clear();
    m_ccAudienceLabel->hide();
    m_ccSelectorButton->hide();


    m_statusInfoLabel->clear();
    m_statusInfoLabel->hide();
    m_addMediaButton->hide();

    m_charCounterLabel->hide();

    m_postButton->hide();
    m_cancelButton->hide();


    // Hide "media mode" controls
    this->cancelMediaMode();

    // Clear "editing mode", restore stuff
    if (m_editingMode)
    {
        m_editingMode = false;
        m_editingPostId.clear();

        m_postButton->setText(tr("Post",
                                 "verb")); // Button text back to "Post" as usual
        m_toSelectorButton->setEnabled(true);
        m_ccSelectorButton->setEnabled(true);
    }

    m_draftsManager->updateDraftId(QString()); // Clear draft ID

    m_fullMode = false;

    // Re-enable stuff just in case the POST request never finished
    this->toggleWidgetsWhileSending(true);

    if (m_uploading)
    {
        qDebug() << "Aborting ongoing upload...";
        // Should use m_uploadNetworkReply->abort(), but that causes
        // PumpController to report error and set Publisher to "posting failed"
        m_uploadNetworkReply->deleteLater();
        m_uploading = false; // Need to set it manually since the connect()s
                             // for the QNetworkReply are gone at this point

        m_pumpController->showStatusMessageAndLogIt(tr("Attachment upload "
                                                       "was cancelled."));
    }

#ifdef GROUPSUPPORT
    m_groupIdLabel->hide();
    m_groupIdLineEdit->hide();
    m_groupIdLineEdit->clear();
#endif
}




void Publisher::setFullMode()
{
    qDebug() << "setting Publisher to full mode";

    m_titleLabel->show();
    m_titleLineEdit->show();

    m_toolsButton->show();
#ifdef HAVE_KCHARSELECT
    m_charPickerButton->show();
#endif
    m_draftsButton->show();

    m_composerBox->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    m_composerBox->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    m_composerBox->setMaximumHeight(2048);
    m_composerBox->hideInfoMessage();
    this->setMaximumHeight(2048); // i.e. "unlimited"

    // Re-enable ctrl+shift+V for plaintext paste (disabled to avoid conflicts)
    m_composerBox->setPlainPasteEnabled(true);

    // Re-enable normal focus policy, while in full mode
    m_composerBox->setFocusPolicy(Qt::StrongFocus);

    m_toSelectorButton->show();
    m_toAudienceLabel->show();
    updateAudienceToLabels();

    m_ccSelectorButton->show();
    m_ccAudienceLabel->show();
    updateAudienceCcLabels();

    // Avoid re-enabling the media button when re-focusing publisher, but still in media mode...
    if (m_addMediaButton->isHidden())
    {
        m_addMediaButton->setEnabled(true); // If it wasn't hidden, don't re-enable
    }
    m_addMediaButton->show();
    m_statusInfoLabel->show();

    m_charCounterLabel->setVisible(m_globalObject->getShowCharacterCounter());

    m_postButton->show();
    m_cancelButton->show();

    m_composerBox->setFocus(); // In case user used menu or shortcut
                               // instead of clicking on it

    m_fullMode = true;

#ifdef GROUPSUPPORT
    m_groupIdLabel->show();
    m_groupIdLineEdit->show();
#endif
}



void Publisher::setPictureMode()
{
    m_postType = QStringLiteral("image");
    setMediaModeWidgets();

    m_pictureLabel->setPixmap(QIcon::fromTheme("image-x-generic",
                                               QIcon(":/images/attached-image.png"))
                              .pixmap(200, 150)
                              .scaled(200, 150,
                                      Qt::IgnoreAspectRatio,
                                      Qt::SmoothTransformation));

    m_pictureLabel->setToolTip(tr("Picture not set"));


    m_selectMediaButton->setIcon(QIcon::fromTheme("folder-image",
                                                  QIcon(":/images/button-open.png")));
    m_selectMediaButton->setText(tr("Select Picture..."));
    m_selectMediaButton->setToolTip(QStringLiteral("<b></b>")
                                    + tr("Find the picture in your folders"));

    this->findMediaFile(); // Load attachment or show open dialog directly
}


void Publisher::setAudioMode()
{
    m_postType = QStringLiteral("audio");
    setMediaModeWidgets();

    m_pictureLabel->setPixmap(QIcon::fromTheme("audio-x-generic",
                                               QIcon(":/images/attached-audio.png"))
                              .pixmap(200, 150)
                              .scaled(200, 150,
                                      Qt::IgnoreAspectRatio,
                                      Qt::SmoothTransformation));

    m_pictureLabel->setToolTip(tr("Audio file not set"));


    m_selectMediaButton->setIcon(QIcon::fromTheme("folder-sound",
                                                  QIcon(":/images/button-open.png")));
    m_selectMediaButton->setText(tr("Select Audio File..."));
    m_selectMediaButton->setToolTip(QStringLiteral("<b></b>")
                                    + tr("Find the audio file in your folders"));

    this->findMediaFile();
}


void Publisher::setVideoMode()
{
    m_postType = QStringLiteral("video");
    setMediaModeWidgets();

    m_pictureLabel->setPixmap(QIcon::fromTheme("video-x-generic",
                                               QIcon(":/images/attached-video.png"))
                              .pixmap(200, 150)
                              .scaled(200, 150,
                                      Qt::IgnoreAspectRatio,
                                      Qt::SmoothTransformation));

    m_pictureLabel->setToolTip(tr("Video not set"));


    m_selectMediaButton->setIcon(QIcon::fromTheme("folder-video",
                                                  QIcon(":/images/button-open.png")));
    m_selectMediaButton->setText(tr("Select Video..."));
    m_selectMediaButton->setToolTip(QStringLiteral("<b></b>")
                                    + tr("Find the video in your folders"));

    this->findMediaFile();
}

void Publisher::setFileMode()
{
    m_postType = QStringLiteral("file");
    setMediaModeWidgets();

    m_pictureLabel->setPixmap(QIcon::fromTheme("application-octet-stream",
                                               QIcon(":/images/attached-file.png"))
                              .pixmap(200, 150)
                              .scaled(200, 150,
                                      Qt::IgnoreAspectRatio,
                                      Qt::SmoothTransformation));

    m_pictureLabel->setToolTip(tr("File not set"));


    m_selectMediaButton->setIcon(QIcon::fromTheme("folder",
                                                  QIcon(":/images/button-open.png")));
    m_selectMediaButton->setText(tr("Select File..."));
    m_selectMediaButton->setToolTip("<b></b>"
                                    + tr("Find the file in your folders"));

    this->findMediaFile();
}

/*
 * Handle drag-and-drop for files.
 *
 * Determine which kind of file it is, and set the proper post type for it.
 *
 */
void Publisher::onFileDropped(QString fileUrl)
{
    QString fileExtension = fileUrl;
    fileExtension.remove(QRegExp(".*\\.")); // Remove all but the extension

    m_mediaFilename = fileUrl;

    if (MiscHelpers::imageExtensions().contains(fileExtension))
    {
        this->setPictureMode();
    }
    else if (MiscHelpers::audioExtensions().contains(fileExtension))
    {
        this->setAudioMode();
    }
    else if (MiscHelpers::videoExtensions().contains(fileExtension))
    {
        this->setVideoMode();
    }
    else
    {
        this->setFileMode();
    }
}


/*
 * Remove the attachment from the publisher
 *
 */
void Publisher::cancelMediaMode()
{
    m_addMediaButton->setEnabled(true);

    m_pictureLabel->hide();
    m_mediaInfoLabel->hide();
    m_selectMediaButton->hide();
    m_removeMediaButton->hide();
    m_uploadProgressBar->hide();

    this->setEmptyMediaData();

    m_postType = QStringLiteral("note");
}



/*
 * Set Publiser to edit mode, after user clicks on "Edit" in a post
 *
 */
void Publisher::setEditingMode(QString postId, QString postType,
                               QString postTitle, QString postText)
{
    // Prevent the "Edit" option from destroying a post currently being composed!
    if (m_editingMode || m_fullMode)
    {
        QMessageBox::warning(this,
                             tr("Error: Already composing"),
                             tr("You can't edit a post at this time, "
                                "because a post is already being composed."));
        return;
    }

    m_editingMode = true;
    m_editingPostId = postId;
    m_postType = postType;
    setFullMode();


    // Fill in the contents of the post
    m_titleLineEdit->setText(postTitle);
    m_composerBox->setText(postText);
    m_composerBox->moveCursor(QTextCursor::End);


    // Change/disable some controls
    m_postButton->setText(tr("Update"));
    m_toSelectorButton->setDisabled(true);
    m_ccSelectorButton->setDisabled(true);
    m_addMediaButton->setDisabled(true);

    // Clear these, so it doesn't look like the audience is different than when originally posted
    m_toAudienceSelector->clearPublicAndFollowers();
    m_toAudienceLabel->clear();
    m_ccAudienceSelector->clearPublicAndFollowers();
    m_ccAudienceLabel->clear();

    m_statusInfoLabel->setText(tr("Editing post."));
}


void Publisher::startMessageForContact(QString id, QString name, QString url)
{
    if (name.trimmed().isEmpty()) // Ensure "name" has some text in it
    {
        name = id;
    }

    if (m_fullMode)  /////// FIXME: maybe use emptyContents() instead
    {
        QMessageBox::warning(this,
                             tr("Error: Already composing"),
                             tr("You can't create a message for %1 at "
                                "this time, because a post is already "
                                "being composed.").arg(name));
        return;
    }

    setFullMode();

    // Unselect Public and Followers from To/Cc
    m_toAudienceSelector->clearPublicAndFollowers();
    m_ccAudienceSelector->clearPublicAndFollowers();


    // Add user name/ID to the "To" field...
    m_toAudienceSelector->copyToSelected(QIcon::fromTheme("user-identity",
                                                          QIcon(":/images/no-avatar.png")),
                                         QString("%1 <%2>").arg(name).arg(id),
                                         name, id, url);
    m_toAudienceSelector->setAudience();


    // Set a default title... FIXME?
    m_titleLineEdit->setText(name + ":");
}

/*
 * Add name + id to selected recipients when auto-completing a nick
 *
 * 'name' can always be expected to have a value
 *
 */
void Publisher::addNickToRecipients(QString id, QString name, QString url,
                                    QString listType)
{
    // If for some reason a name is received without an ID, do nothing
    if (id.trimmed().isEmpty())
    {
        qDebug() << "Trying to add a nick to recipients without an ID!"
                 << name << " - " << url;
        return;
    }

    /* TMP/FIXME: Until audience can be modified when editing, To/Cc buttons
     * are disabled, and so must be the nick autocompletion. Not actually
     * disabled, but nick won't be added to the audience lists, since it
     * wouldn't really do anything.
     *
     */
    if (m_editingMode)
    {
        qDebug() << "Publisher: Can't modify audience in EDITING mode";
        return;
    }

    if (listType == "to")
    {
        m_toAudienceSelector->copyToSelected(QIcon::fromTheme("user-identity",
                                                              QIcon(":/images/no-avatar.png")),
                                             QString("%1 <%2>").arg(name).arg(id),
                                             name, id, url);
        m_toAudienceSelector->setAudience();
    }
    else if (listType == "cc")
    {
        m_ccAudienceSelector->copyToSelected(QIcon::fromTheme("user-identity",
                                                              QIcon(":/images/no-avatar.png")),
                                             QString("%1 <%2>").arg(name).arg(id),
                                             name, id, url);
        m_ccAudienceSelector->setAudience();
    }
}

/*
 * Upon selecting a draft from the Drafts>Load submenu, load it,
 * unless there's content already present
 *
 */
void Publisher::onDraftSelected(QString id, QString title, QString body,
                                QString type, QString attachment,
                                QVariantMap audience, int position)
{
    // Make sure there's nothing in the editor
    if (emptyContents())
    {
        m_postType = type;
        if (m_postType.isEmpty())
        {
            m_postType = QStringLiteral("note");
        }

        m_titleLineEdit->setText(title);
        m_composerBox->setText(body);

        if (m_postType != QStringLiteral("note"))
        {
            m_mediaFilename = attachment;

            if (m_postType == QStringLiteral("image"))
            {
                this->setPictureMode();
            }
            else if (m_postType == QStringLiteral("audio"))
            {
                this->setAudioMode();
            }
            else if (m_postType == QStringLiteral("video"))
            {
                this->setVideoMode();
            }
            else if (m_postType == QStringLiteral("file"))
            {
                this->setFileMode();
            }
        }
        else
        {
            this->cancelMediaMode();
        }

        this->setAudienceFromMap(audience);

        QTextCursor textCursor = m_composerBox->textCursor();
        textCursor.setPosition(position);
        m_composerBox->setTextCursor(textCursor);

        m_draftsManager->updateDraftId(id);

        m_statusInfoLabel->setText(tr("Draft loaded."));
    }
    else
    {
        QMessageBox::warning(this,
                             tr("ERROR: Already composing"),
                             tr("You can't load a draft at this time, because "
                                "a post is already being composed."));
        // A choice to load it anyway could be given, but could cause
        // accidents, so taking the less destructive path for now
    }

    m_composerBox->setFocus();
}


void Publisher::onSaveDraftRequested()
{
    m_draftsManager->saveDraft(m_titleLineEdit->text(),
                               m_composerBox->toHtml(),
                               m_postType,
                               m_mediaFilename,
                               this->getAudienceMap(),
                               m_composerBox->textCursor().position());

    m_statusInfoLabel->setText(tr("Draft saved."));
    m_composerBox->setFocus();
}


void Publisher::onCancelSavingDraftRequested()
{
    this->onSaveDraftRequested();

    m_composerBox->erase();
    this->setMinimumMode();
}


/*
 * After the post is confirmed to have been received by the server
 * re-enable publisher, clear text, etc.
 *
 */
void Publisher::onPublishingOk()
{
    m_statusInfoLabel->clear();
    this->toggleWidgetsWhileSending(true);

    m_composerBox->erase();

    // Done composing message, hide buttons until we get focus again
    setMinimumMode();
}

/*
 * If there was an HTTP error while posting...
 *
 */
void Publisher::onPublishingFailed()
{
    qDebug() << "Posting failed, re-enabling Publisher";
    m_statusInfoLabel->setText(tr("Posting failed.\n\nTry again."));

    this->toggleWidgetsWhileSending(true);
    m_uploadProgressBar->hide();

    // In media mode, show the "remove" button again (hidden during upload)
    if (m_postType != QStringLiteral("note"))
    {
        m_removeMediaButton->show();
    }

    m_composerBox->setFocus();
}


/*
 * These are called when selecting Public or Followers in the menus
 *
 * When selecting "Cc: Followers", "To: Followers" gets unselected, etc.
 *
 */
void Publisher::onToPublicSelected()
{
    m_ccAudienceSelector->setPublic(false);      // Unselect "Cc: Public"
}

void Publisher::onToFollowersSelected()
{
    m_ccAudienceSelector->setFollowers(false);   // Unselect "Cc: Followers"
}

void Publisher::onCcPublicSelected()
{
    m_toAudienceSelector->setPublic(false);      // Unselect "To: Public"
}

void Publisher::onCcFollowersSelected()
{
    m_toAudienceSelector->setFollowers(false);   // Unselect "To: Followers"
}


void Publisher::updateAudienceToLabels()
{
    m_toAudienceLabel->setText(m_toAudienceSelector->updatedAudienceLabels());
}

void Publisher::updateAudienceCcLabels()
{
    m_ccAudienceLabel->setText(m_ccAudienceSelector->updatedAudienceLabels());
}


/*
 * Fill the "Lists" submenus with PumpController's lists info
 *
 *
 */
void Publisher::updateListsMenus(QVariantList listsList)
{
    m_toAudienceSelector->setListsMenu(listsList);
    m_ccAudienceSelector->setListsMenu(listsList);
}


/*
 * Show the URL of a contact in the recipients list, in the status bar
 *
 * FIXME: merge with Post::showHighlightedUrl()
 *
 */
void Publisher::showHighlightedUrl(QString url)
{
    if (!url.isEmpty())
    {
        m_pumpController->showTransientMessage(url);
        qDebug() << "Highlighted recipient url in publisher:" << url;
    }
    else
    {
        m_pumpController->showTransientMessage(QString());
    }
}




/*
 * Send the post (note, image, audio...) to the server
 *
 */
void Publisher::sendPost()
{
    qDebug() << "Publisher character count:" << m_composerBox->textCursor()
                                                .document()->characterCount();

    bool textIsEmpty;
    if (m_composerBox->textCursor().document()->characterCount() > 1) // kinda tmp
    {
        textIsEmpty = false;
    }
    else  ///////// FIXME: use a variation of emptyContents()
    {
        textIsEmpty = true;
    }

    bool attachmentReadable = !m_mediaFilename.isEmpty();
    if (attachmentReadable)
    {
        QFileInfo fileInfo(m_mediaFilename);
        // Check that it actually still exists, before sending
        if (!fileInfo.exists() || !fileInfo.isReadable())
        {
            this->reportUnreadableFile(m_mediaFilename, fileInfo);
            attachmentReadable = false;
        }
    }


    // If there's some text in the post, or attached media, send it
    if ( (m_postType == QStringLiteral("note") && !textIsEmpty)
      || (m_postType != QStringLiteral("note") && attachmentReadable)
      || m_editingMode ) // Or editing, then the other stuff doesn't matter
    {
        QString postTitle = m_titleLineEdit->text().trimmed();
        postTitle.replace("\n", " "); // Post title could have newlines if copy-pasted
        if (postTitle.length() > 120) // Limit title length to something sane
        {
            postTitle = postTitle.left(115) + " [...]";
        }

        QString cleanHtmlString = MiscHelpers::cleanupHtml(m_composerBox->toHtml());


        QVariantMap audienceMap = this->getAudienceMap();

        // Warn user if posting only to Followers, but having none
        if (m_onlyToFollowers   // Set in getAudienceMap()
         && m_pumpController->currentFollowersCount() == 0
         && !m_editingMode)
        {
            qDebug() << "WARNING: You have no followers yet; Post to public?";

            int action = QMessageBox::warning(this,
                                 tr("Warning: You have no followers yet"),
                                 tr("You're trying to post to your followers "
                                    "only, but you don't have any followers "
                                    "yet.")
                                 + QStringLiteral(" ")
                                 + tr("If you post like this, no one will be "
                                      "able to see your message.")
                                 + QStringLiteral("\n\n")
                                 + tr("Do you want to make the post public "
                                      "instead of followers-only?")
                                 + QStringLiteral("\n"),
                                 tr("&Yes, make it public"),
                                 tr("&No, post to my followers only"),
                                 tr("&Cancel, go back to the post"),
                                 2, 2); // Cancel is default, and also activated with ESC
            if (action == 0)
            {
                m_toAudienceSelector->setPublic(true);  // Check To:Public
                audienceMap = this->getAudienceMap();   // Process again
                qDebug() << "Checked To:Public";
            }
            else if (action == 2)
            {
                qDebug() << "Posting aborted; back to post composer";
                return;
            }
        }

        // Don't erase just yet!! Just disable until we get "200 OK" from the server.
        this->toggleWidgetsWhileSending(false);

        if (!m_editingMode)
        {
            m_statusInfoLabel->setText(tr("Posting..."));

            if (m_postType == QStringLiteral("note"))
            {
                m_pumpController->postNote(audienceMap,
                                           cleanHtmlString,
                                           postTitle);
            }
            else
            {
                m_uploading = true;
                m_uploadNetworkReply = m_pumpController->postMedia(audienceMap,
                                                                   cleanHtmlString,
                                                                   postTitle,
                                                                   m_mediaFilename,
                                                                   m_postType,
                                                                   m_mediaContentType);
                connect(m_uploadNetworkReply, &QNetworkReply::uploadProgress,
                        this, &Publisher::updateProgressBar);
                connect(m_uploadNetworkReply, &QNetworkReply::finished,
                        this, &Publisher::onUploadFinished);
                // These will be automatically disconnected when
                // the QNetworkReply is automatically deleted

                // The "remove" button uses the same space as the progress bar
                m_removeMediaButton->hide();

                m_uploadProgressBar->setValue(0);
                m_uploadProgressBar->show();
            }
        }
        else
        {
            m_statusInfoLabel->setText(tr("Updating..."));
            m_pumpController->updatePost(m_editingPostId,
                                         m_postType,
                                         cleanHtmlString,
                                         postTitle);
        }
    }
    else
    {
        if (m_postType == "note")
        {
            m_statusInfoLabel->setText(tr("Post is empty."));
        }
        else // Image, audio...
        {
            m_statusInfoLabel->setText(tr("File not selected."));
        }
        qDebug() << "Can't send post: text is empty, or no attachment";
    }
}


void Publisher::onSelectMediaFilePressed()
{
    QString oldMediaFilename = m_mediaFilename;
    m_mediaFilename.clear();

    this->findMediaFile();

    // If the dialog to find a file was cancelled, restore
    if (m_mediaFilename.isEmpty())
    {
        m_mediaFilename = oldMediaFilename;
    }
}



void Publisher::updateProgressBar(qint64 sent, qint64 total)
{
    m_uploadProgressBar->setRange(0, total);
    m_uploadProgressBar->setValue(sent);

    m_uploadProgressBar->setToolTip(tr("%1 KiB of %2 KiB uploaded")
                                    .arg(QLocale::system()
                                                 .toString(sent / 1024))
                                    .arg(QLocale::system()
                                                 .toString(total / 1024)));
}

void Publisher::onUploadFinished()
{
    qDebug() << "File upload finished in some way";
    m_uploading = false;
}


void Publisher::updateCharacterCounter()
{
    int charCount = m_composerBox->textCursor().document()->characterCount() - 1;
    m_charCounterLabel->setText(QLocale::system().toString(charCount));
}
