/*
 *   This file is part of Dianara
 *   Copyright 2012-2016  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)
{
    this->pController = pumpController;
    this->globalObj = globalObject;

    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);


    this->commentId = commentObject->getId();
    this->objectType = commentObject->getType();

    this->commentAuthorId = commentObject->author()->getId();
    if (commentAuthorId == pController->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);
    }

    this->commentIsDeleted = false;
    this->commentHasImages = false;


    // Avatar pixmap
    avatarButton = new AvatarButton(commentObject->author(),
                                    this->pController,
                                    this->globalObj,
                                    this->globalObj->getCommentAvatarSize(),
                                    this);


    QFont commentsFont;
    commentsFont.fromString(globalObj->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);
    }


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


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



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

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



    quoteLabel = new QLabel("<a href=\"quote://\">"
                            + tr("Quote", "This is a verb, infinitive")
                            + "</a>",
                            this);
    quoteLabel->setContextMenuPolicy(Qt::NoContextMenu);
    quoteLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
    quoteLabel->setFont(metadataFont);
    quoteLabel->setToolTip("<b></b>"
                           + tr("Reply quoting this comment"));
    connect(quoteLabel, SIGNAL(linkHovered(QString)),
            this, SLOT(saveCommentSelectedText()));
    connect(quoteLabel, SIGNAL(linkActivated(QString)),
            this, SLOT(quoteComment()));


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


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


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


    // Main content, the comment itself
    contentLabel = new QLabel(this);
    contentLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    contentLabel->setFont(commentsFont);
    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
    contentLabel->setWordWrap(true);
    contentLabel->setOpenExternalLinks(true);
    contentLabel->setTextFormat(Qt::RichText);
    contentLabel->setSizePolicy(sizePolicy);
    contentLabel->setMaximumHeight(4096);
    connect(contentLabel, SIGNAL(linkHovered(QString)),
            this, SLOT(showUrlInfo(QString)));


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



    // Layout
    leftLayout = new QVBoxLayout();
    leftLayout->setContentsMargins(0, 0, 0, 0);
    leftLayout->setAlignment(Qt::AlignLeft);
    leftLayout->addWidget(avatarButton,    0, Qt::AlignLeft | Qt::AlignTop);
    leftLayout->addWidget(likesCountLabel, 0, Qt::AlignHCenter | Qt::AlignTop);
    leftLayout->addStretch();

    rightTopLayout = new QHBoxLayout();
    rightTopLayout->addWidget(fullNameLabel,  0, Qt::AlignLeft);
    rightTopLayout->addWidget(timestampLabel, 0, Qt::AlignLeft);
    rightTopLayout->addWidget(likeLabel,      0, Qt::AlignLeft);
    rightTopLayout->addWidget(quoteLabel,     0, Qt::AlignLeft);
    rightTopLayout->addStretch(1);
    if (commentIsOwn)
    {
        rightTopLayout->addWidget(editLabel,   0, Qt::AlignRight);
        rightTopLayout->addWidget(deleteLabel, 0, Qt::AlignRight);
    }
    else
    {
        // Since these widgets are initialized with a parent, hide them when not needed
        editLabel->hide();
        deleteLabel->hide();
    }

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


    mainLayout = new QHBoxLayout();
    mainLayout->setContentsMargins(2, 2, 2, 2);
    mainLayout->addWidget(hintWidget);
    mainLayout->addLayout(leftLayout);
    mainLayout->addLayout(rightLayout);
    this->setLayout(mainLayout);


    this->updateDataFromObject(commentObject);

    qDebug() << "Comment created" << this->commentId;
}


Comment::~Comment()
{
    qDebug() << "Comment destroyed" << this->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
    this->createdAt = object->getCreatedAt();
    this->updatedAt = object->getUpdatedAt();
    QString timestampTooltip = tr("Posted on %1")
                               .arg(Timestamp::localTimeDate(createdAt));
    if (createdAt != updatedAt)
    {
        timestampTooltip.append("<br>"
                                + tr("Modified on %1")
                                  .arg(Timestamp::localTimeDate(updatedAt)));
    }
    timestampLabel->setToolTip(timestampTooltip); // Precise time on tooltip
    this->setFuzzyTimestamps();


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

    if (object->isLiked() == "true")
    {
        commentIsLiked = true;
    }
    else
    {
        commentIsLiked = false;
    }
    this->fixLikeLabelText();


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


    this->setCommentContents();
}



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


void Comment::setLikesCount(QString count, QVariantList namesVariantList)
{
    // FIXME: count should be int
    if (count != "0")
    {
        QString likesString = ASObject::personStringFromList(namesVariantList,
                                                             count.toInt());

        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);
        }


        likesCountLabel->setBaseText(QString::fromUtf8("\342\231\245")  // Heart symbol
                                   + QString(" %1").arg(count));
        // set tooltip as HTML, so it gets wordwrapped
        likesCountLabel->setToolTip("<b></b>" + likesString);
        likesCountLabel->show();
    }
    else
    {
        likesCountLabel->clear();
        likesCountLabel->setToolTip("");
        likesCountLabel->hide();
    }
}


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

    this->timestampLabel->setBaseText(timestamp);
}

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


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

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

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

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


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

    this->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 then
    if (!this->commentHasImages)
    {
        this->contentLabel->setMaximumHeight(height);
    }
}



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

        connect(pController, SIGNAL(imageStored(QString)),
                this, SLOT(redrawImages(QString)));
    }
}


QString Comment::getObjectId()
{
    return this->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);

    this->hintWidget->setStyleSheet(css);
    this->hintWidget->setFixedWidth(4); // 4 px
    this->hintWidget->show();
}


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

    this->commentIsDeleted = true;

    if (!this->commentOriginalText.isEmpty())
    {
        this->commentOriginalText.prepend("<hr>"); // --------
    }
    this->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://")
    {
        commentIsLiked = true;
    }
    else // unlike://
    {
        commentIsLiked = false;
    }

    this->pController->likePost(this->commentId,
                                this->objectType,
                                this->commentAuthorId,
                                this->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()
{
    this->commentSelectedText = contentLabel->selectedText();
    // TMP / FIXME: issues here; the signal is not emitted every time
    qDebug() << "### comment SELECTED TEXT: " << 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 (commentSelectedText.isEmpty())
    {
        // Quote full comment
        quotedComment = MiscHelpers::quotedText(this->fullNameLabel->text(),
                                                this->contentLabel->text());
    }
    else
    {
        // Quote the selection only
        commentSelectedText.prepend("[...] ");
        commentSelectedText.append(" [...]");
        quotedComment = MiscHelpers::quotedText(this->fullNameLabel->text(),
                                                this->commentSelectedText);
    }

    commentSelectedText.clear();

    emit commentQuoteRequested(quotedComment);
}



void Comment::editComment()
{
    emit commentEditRequested(this->commentId,
                              this->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"), "", 1, 1);

    if (confirmation == 0)
    {
        qDebug() << "Deleting comment" << this->commentId;
        this->pController->deletePost(this->commentId, this->objectType);

        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())
    {
        this->pController->showTransientMessage(url);

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




/*
 * Redraw comment contents after receiving downloaded images
 *
 */
void Comment::redrawImages(QString imageUrl)
{
    if (pendingImagesList.contains(imageUrl))
    {
        this->pendingImagesList.removeAll(imageUrl);
        if (pendingImagesList.isEmpty()) // If there are no more, disconnect
        {
            disconnect(pController, SIGNAL(imageStored(QString)),
                       this, SLOT(redrawImages(QString)));

            setCommentContents();
        }
    }
}




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


/*
 * Ensure url info in statusbar is hidden when the mouse leaves the comment
 *
 */
void Comment::leaveEvent(QEvent *event)
{
    this->pController->showTransientMessage("");

    event->accept();
}


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

    this->onResize();

    event->accept();
}

