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

Comment::Comment(PumpController *pumpController,
                 GlobalObject *globalObject,
                 ASObject *commentObject,
                 QWidget *parent) : QFrame(parent)
{
    m_pumpController = pumpController;
    m_globalObject = globalObject;

    m_commentIsDeleted = false;
    m_commentHasImages = false;

    commentObject->setParent(this); // Reparent the passed object

    QSizePolicy sizePolicy;
    sizePolicy.setHeightForWidth(false);
    sizePolicy.setWidthForHeight(false);
    sizePolicy.setHorizontalPolicy(QSizePolicy::Ignored);
    sizePolicy.setVerticalPolicy(QSizePolicy::Maximum); // Previously QSizePolicy::Preferred
    this->setSizePolicy(sizePolicy);
    this->setMinimumSize(10, 10); // Ensure something's visible at any time
    this->setMaximumHeight(4096);


    m_commentId = commentObject->getId();
    m_objectType = commentObject->getType();
    m_commentAuthorId = commentObject->author()->getId();

    bool commentIsOwn;
    if (m_commentAuthorId == m_pumpController->currentUserId())
    {
        commentIsOwn = true; // Comment is ours!

        // Different frame style depending on whether the comment is ours or not
        this->setFrameStyle(QFrame::Sunken | QFrame::StyledPanel);
    }
    else
    {
        commentIsOwn = false;

        this->setFrameStyle(QFrame::Raised | QFrame::StyledPanel);
    }



    // Avatar pixmap
    m_avatarButton = new AvatarButton(commentObject->author(),
                                      m_pumpController,
                                      m_globalObject,
                                      m_globalObject->getCommentAvatarSize(),
                                      this);


    QFont commentsFont;
    commentsFont.fromString(m_globalObject->getCommentsFont());


    // Name, with ID as tooltip
    QFont metadataFont;
    metadataFont.setPointSize(metadataFont.pointSize() - 1); // 1 point less than default
    metadataFont.setBold(true);
    if (commentIsOwn)
    {
        metadataFont.setItalic(true);
    }


    m_fullNameLabel = new QLabel(commentObject->author()->getNameWithFallback(),
                                 this);
    m_fullNameLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
    m_fullNameLabel->setFont(metadataFont);


    // Timestamps
    metadataFont.setBold(false);
    metadataFont.setItalic(true);
    m_timestampLabel = new HClabel(QString(), this);
    m_timestampLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
    m_timestampLabel->setWordWrap(false); // ON by default in HClabel
    m_timestampLabel->setFont(metadataFont);



    // Like and Delete "buttons"
    metadataFont.setBold(true);
    metadataFont.setItalic(false);

    m_likeLabel = new QLabel(QStringLiteral("*like*"), this);
    m_likeLabel->setContextMenuPolicy(Qt::NoContextMenu);
    m_likeLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
    m_likeLabel->setFont(metadataFont);
    m_likeLabel->setToolTip("<b></b>"
                            + tr("Like or unlike this comment"));
    connect(m_likeLabel, &QLabel::linkActivated,
            this, &Comment::likeComment);



    m_quoteLabel = new QLabel(" <a href=\"quote://\">"
                              + tr("Quote", "This is a verb, infinitive")
                              + "</a> ",
                              this);
    // Spaces around the link help the linkHovered signal work better
    m_quoteLabel->setContextMenuPolicy(Qt::NoContextMenu);
    m_quoteLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
    m_quoteLabel->setFont(metadataFont);
    m_quoteLabel->setToolTip(QStringLiteral("<b></b>")
                             + tr("Reply quoting this comment"));
    connect(m_quoteLabel, &QLabel::linkHovered, // FIXME: issues with this signal
            this, &Comment::saveCommentSelectedText);
    connect(m_quoteLabel, &QLabel::linkActivated,
            this, &Comment::quoteComment);


    m_editLabel = new QLabel("<a href=\"edit://\">" + tr("Edit") + "</a>",
                             this);
    m_editLabel->setContextMenuPolicy(Qt::NoContextMenu);
    m_editLabel->setAlignment(Qt::AlignTop | Qt::AlignRight);
    m_editLabel->setFont(metadataFont);
    m_editLabel->setToolTip("<b></b>"
                            + tr("Modify this comment"));
    connect(m_editLabel, &QLabel::linkActivated,
            this, &Comment::editComment);


    m_deleteLabel = new QLabel("<a href=\"delete://\">"
                               + tr("Delete") + "</a>",
                               this);
    m_deleteLabel->setContextMenuPolicy(Qt::NoContextMenu);
    m_deleteLabel->setAlignment(Qt::AlignTop | Qt::AlignRight);
    m_deleteLabel->setFont(metadataFont);
    m_deleteLabel->setToolTip(QStringLiteral("<b></b>")
                              + tr("Erase this comment"));
    connect(m_deleteLabel, &QLabel::linkActivated,
            this, &Comment::deleteComment);


    // The likes count
    m_likesCountLabel = new HClabel(QString(), this);
    m_likesCountLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
    metadataFont.setBold(false);
    m_likesCountLabel->setFont(metadataFont);


    // Main content, the comment itself
    m_contentLabel = new QLabel(this);
    m_contentLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    m_contentLabel->setFont(commentsFont);
    m_contentLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
    // To help screen readers add Qt::TextSelectableByKeyboard flag too;
    // Not adding for now, because it doesn't really help much. This will work better with Qt5
    m_contentLabel->setWordWrap(true);
    m_contentLabel->setOpenExternalLinks(true);
    m_contentLabel->setTextFormat(Qt::RichText);
    m_contentLabel->setSizePolicy(sizePolicy);
    m_contentLabel->setMaximumHeight(4096);
    connect(m_contentLabel, &QLabel::linkHovered,
            this, &Comment::showUrlInfo);


    // This is used to draw a colored vertical line, as a hint
    m_hintWidget = new QWidget(this);
    m_hintWidget->hide();



    // Layout
    m_leftLayout = new QVBoxLayout();
    m_leftLayout->setContentsMargins(0, 0, 0, 0);
    m_leftLayout->setAlignment(Qt::AlignLeft);
    m_leftLayout->addWidget(m_avatarButton,    0, Qt::AlignLeft | Qt::AlignTop);
    m_leftLayout->addWidget(m_likesCountLabel, 0, Qt::AlignHCenter | Qt::AlignTop);
    m_leftLayout->addStretch();

    m_rightTopLayout = new QHBoxLayout();
    m_rightTopLayout->addWidget(m_fullNameLabel,  0, Qt::AlignLeft);
    m_rightTopLayout->addWidget(m_timestampLabel, 0, Qt::AlignLeft);
    m_rightTopLayout->addWidget(m_likeLabel,      0, Qt::AlignLeft);
    m_rightTopLayout->addWidget(m_quoteLabel,     0, Qt::AlignLeft);
    m_rightTopLayout->addStretch(1);
    if (commentIsOwn)
    {
        m_rightTopLayout->addWidget(m_editLabel,   0, Qt::AlignRight);
        m_rightTopLayout->addWidget(m_deleteLabel, 0, Qt::AlignRight);
    }
    else
    {
        // Since these widgets are initialized with a parent, hide them when not needed
        m_editLabel->hide();
        m_deleteLabel->hide();
    }

    m_rightLayout = new QVBoxLayout();
    m_rightLayout->addLayout(m_rightTopLayout, 0);
    m_rightLayout->addSpacing(4); // 4px vertical space separation
    m_rightLayout->addWidget(m_contentLabel,   1, Qt::AlignTop);
    m_rightLayout->addSpacing(1); // and 1px more as margin


    m_mainLayout = new QHBoxLayout();
    m_mainLayout->setContentsMargins(2, 2, 2, 2);
    m_mainLayout->addWidget(m_hintWidget);
    m_mainLayout->addLayout(m_leftLayout);
    m_mainLayout->addLayout(m_rightLayout);
    this->setLayout(m_mainLayout);


    this->updateDataFromObject(commentObject);

    qDebug() << "Comment created" << m_commentId;
}


Comment::~Comment()
{
    qDebug() << "Comment destroyed" << m_commentId;
}


/*
 * Fill in or replace data such as timestamps, title, contents,
 * number of likes and shares, etc, from object data
 *
 */
void Comment::updateDataFromObject(ASObject *object)
{
    // Timestamps
    m_createdAt = object->getCreatedAt();
    m_updatedAt = object->getUpdatedAt();
    QString timestampTooltip = tr("Posted on %1")
                               .arg(Timestamp::localTimeDate(m_createdAt));
    if (m_createdAt != m_updatedAt)
    {
        timestampTooltip.append("<br /><br />"
                                + tr("Modified on %1")
                                  .arg(Timestamp::localTimeDate(m_updatedAt)));
    }
    m_timestampLabel->setToolTip(timestampTooltip); // Precise time on tooltip
    this->setFuzzyTimestamps();


    this->setLikesCount(object->getLikesCount(),
                        object->getLastLikesList());

    m_commentIsLiked = object->isLiked();
    this->fixLikeLabelText();


    // The comment itself
    m_commentOriginalText = object->getContent();
    m_pendingImagesList = MiscHelpers::htmlWithReplacedImages(m_commentOriginalText,
                                                              128); // Arbitrary (initial) width
    m_pendingImagesList.removeFirst(); // First one is the HTML with images replaced
    this->getPendingImages();


    this->setCommentContents();
}



void Comment::fixLikeLabelText()
{
    if (m_commentIsLiked)
    {
        m_likeLabel->setText("<a href=\"unlike://\">" + tr("Unlike") +"</a>");
    }
    else
    {
        m_likeLabel->setText("<a href=\"like://\">" + tr("Like") +"</a>");
    }
}


void Comment::setLikesCount(int count, QVariantList namesVariantList)
{
    if (count != 0)
    {
        QString likesString = ASObject::personStringFromList(namesVariantList,
                                                             count);

        if (count == 1)
        {
            likesString = tr("%1 likes this comment",
                             "Singular: %1=name of just "
                             "1 person").arg(likesString);
        }
        else // Several people
        {
            likesString = tr("%1 like this comment",
                             "Plural: %1=list of people like John, "
                             "Jane, Smith").arg(likesString);
        }


        m_likesCountLabel->setBaseText(QString::fromUtf8("\342\231\245 ") // Heart symbol
                                       + QString::number(count));
        // Set tooltip as HTML, so it gets wordwrapped
        m_likesCountLabel->setToolTip(QStringLiteral("<b></b>") + likesString);
        m_likesCountLabel->show();
    }
    else
    {
        m_likesCountLabel->clear();
        m_likesCountLabel->setToolTip(QString());
        m_likesCountLabel->hide();
    }
}


void Comment::setFuzzyTimestamps()
{
    QString timestamp = Timestamp::fuzzyTime(m_createdAt);
    if (m_createdAt != m_updatedAt) // Comment has been edited
    {
        timestamp.prepend(QStringLiteral("**")); // FIXME, somehow show edit time
    }

    m_timestampLabel->setBaseText(timestamp);
}

void Comment::syncAvatarFollowState()
{
    m_avatarButton->syncFollowState();
}


/*
 * Set the contents of the comment, parsing images, etc.
 *
 */
void Comment::setCommentContents()
{
    const int imageWidth = m_contentLabel->width() - 20; // Kinda TMP

    QStringList commentImageList = MiscHelpers::htmlWithReplacedImages(m_commentOriginalText,
                                                                       imageWidth);

    if (commentImageList.size() > 1) // First is the HTML itself
    {
        m_commentHasImages = true;
    }

                                         // Comment's HTML with images replaced
    const QString commentContents = commentImageList.takeAt(0);
    m_contentLabel->setText(commentContents);
}


void Comment::onResize()
{
    m_contentLabel->ensurePolished();
    const int height = m_contentLabel->heightForWidth(m_contentLabel->width());

    m_contentLabel->setMinimumHeight(height);

    /* Forcing maximum height cuts images and is not really needed when
     * images are present, thanks to fixes elsewhere, so enforce it only
     * when there are none
     *
     */
    if (!m_commentHasImages)
    {
        m_contentLabel->setMaximumHeight(height);
    }
}



void Comment::getPendingImages()
{
    if (!m_pendingImagesList.isEmpty())
    {
        foreach (const QString imageUrl, m_pendingImagesList)
        {
            m_pumpController->enqueueImageForDownload(imageUrl);
        }

        connect(m_pumpController, &PumpController::imageStored,
                this, &Comment::redrawImages);
    }
}


QString Comment::getObjectId()
{
    return m_commentId;
}


/*
 * A thin line on left side as hint to indicate comment is yours
 * or from the author of the parent post
 *
 */
void Comment::setHint(QString color)
{
    if (color.isEmpty())
    {
        color = "palette(highlight)";
    }

    // Transparent to your color to transparent gradient
    QString css = QString("QWidget "
                          "{ background-color: "
                          "  qlineargradient(spread:pad, "
                          "  x1:0, y1:0, x2:1, y2:0, "
                          "  stop:0 rgba(0, 0, 0, 0), "
                          "  stop:0.25 %1, stop:0.75 %1, "
                          "  stop:1 rgba(0, 0, 0, 0) ); "
                          "}").arg(color);

    m_hintWidget->setStyleSheet(css);
    m_hintWidget->setFixedWidth(4); // 4 px -- FIXME: should not be hardcoded
    m_hintWidget->show();
}


void Comment::setCommentDeleted(QString deletedTime)
{
    if (m_commentIsDeleted)
    {
        return; // Was already deleted, so just ignore
    }

    m_commentIsDeleted = true;

    if (!m_commentOriginalText.isEmpty())
    {
        m_commentOriginalText.prepend("<hr>"); // --------
    }

    m_commentOriginalText.prepend("<center>" + deletedTime + "</center>");
    qDebug() << "This comment was deleted on" << deletedTime;

    this->setCommentContents();
    this->onResize();

    this->setDisabled(true);
}



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



void Comment::likeComment(QString clickedLink)
{
    if (clickedLink == "like://")
    {
        m_commentIsLiked = true;
    }
    else // unlike://
    {
        m_commentIsLiked = false;
    }

    m_pumpController->likePost(m_commentId,
                               m_objectType,
                               m_commentAuthorId,
                               m_commentIsLiked);
    this->fixLikeLabelText();
}


/*
 * This will be called when hovering the "Quote" link
 *
 * Store the selected text, so it can be used when actually quoting
 *
 */
void Comment::saveCommentSelectedText()
{
    m_commentSelectedText = m_contentLabel->selectedText();
    // TMP / FIXME: issues here; the signal is not emitted every time
    qDebug() << "### Comment SELECTED TEXT: " << m_contentLabel->selectedText();
}


/*
 * Take the contents of a comment and put them as a quote block
 * in the comment composer.
 *
 * If some text has been selected, saveCommentSelectedText()
 * will have it stored in a variable, used here.
 *
 */
void Comment::quoteComment()
{
    QString quotedComment;

    if (m_commentSelectedText.isEmpty())
    {
        // Quote full comment
        quotedComment = MiscHelpers::quotedText(m_fullNameLabel->text(),
                                                m_contentLabel->text());
    }
    else
    {
        // Quote the selection only
        m_commentSelectedText.prepend("[...] ");
        m_commentSelectedText.append(" [...]");
        quotedComment = MiscHelpers::quotedText(m_fullNameLabel->text(),
                                                m_commentSelectedText);
        m_commentSelectedText.clear();
    }

    emit commentQuoteRequested(quotedComment);
}



void Comment::editComment()
{
    emit commentEditRequested(m_commentId, m_commentOriginalText);
}



void Comment::deleteComment()
{
    int confirmation = QMessageBox::question(this,
                                             tr("WARNING: Delete comment?"),
                                             tr("Are you sure you want to "
                                                "delete this comment?"),
                                             tr("&Yes, delete it"), tr("&No"),
                                             QString(), 1, 1);

    if (confirmation == 0)
    {
        qDebug() << "Deleting comment" << m_commentId;
        m_pumpController->deletePost(m_commentId, m_objectType);

        const QString timeNow = QDateTime::currentDateTimeUtc()
                                          .toString(Qt::ISODate);
        this->setCommentDeleted(ASObject::makeDeletedOnString(timeNow));
    }
    else
    {
        qDebug() << "Confirmation cancelled, not deleting the comment";
    }
}


/*
 * Show the URL of a link hovered in a comment
 *
 */
void Comment::showUrlInfo(QString url)
{
    if (!url.isEmpty())
    {
        m_pumpController->showTransientMessage(url);

        qDebug() << "Link hovered in comment:" << url;
    }
    else
    {
        m_pumpController->showTransientMessage(QString());
    }
}




/*
 * Redraw comment contents after receiving downloaded images
 *
 */
void Comment::redrawImages(QString imageUrl)
{
    if (m_pendingImagesList.contains(imageUrl))
    {
        m_pendingImagesList.removeAll(imageUrl);
        if (m_pendingImagesList.isEmpty()) // If there are no more, disconnect
        {
            disconnect(m_pumpController, &PumpController::imageStored,
                       this, &Comment::redrawImages);
        }

        // Redraw for every image
        setCommentContents();
    }
}




/****************************************************************************/
/****************************** PROTECTED ***********************************/
/****************************************************************************/


/*
 * Ensure URL info in statusbar is hidden when the mouse leaves the comment
 *
 */
void Comment::leaveEvent(QEvent *event)
{
    m_pumpController->showTransientMessage(QString());

    event->accept();
}


void Comment::resizeEvent(QResizeEvent *event)
{
    //qDebug() << "Comment::resizeEvent()"
    //         << event->oldSize() << ">" << event->size();

    this->onResize();

    event->accept();
}

