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

MiscHelpers::MiscHelpers(QObject *parent) : QObject(parent)
{
    // Creating object not required, all static functions
}




QString MiscHelpers::getCachedAvatarFilename(QString url)
{
    QString localFilename;

    if (!url.isEmpty())
    {
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
        localFilename = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
#else
        localFilename = QStandardPaths::standardLocations(QStandardPaths::DataLocation).first();
#endif
        localFilename.append("/avatars/");
        localFilename.append(url.trimmed().toUtf8().toBase64());

        QString fileExtension = url;
        fileExtension.remove(QRegExp(".*\\.")); // remove all but the extension

        localFilename.append(".");
        localFilename.append(fileExtension);
    }

    return localFilename;
}







QString MiscHelpers::getCachedImageFilename(QString url)
{
    QString localFilename;

    if (!url.isEmpty())
    {
        url = url.trimmed();
        if (url.startsWith("http://"))
        {
            url.remove(0, 7); // remove http://
        }
        if (url.startsWith("https://"))
        {
            url.remove(0, 8); // remove https://
        }

        // Convert to percentEncoding before, to avoid having "/" and such in the filename
        QByteArray base64url = url.toUtf8().toPercentEncoding().toBase64();
        base64url.truncate(255); // Limit filename length! Just in case the URL is VERY long


#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
        localFilename = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
#else
        localFilename = QStandardPaths::standardLocations(QStandardPaths::DataLocation).first();
#endif
        localFilename.append("/images/");
        localFilename.append(base64url);
    }

    return localFilename;
}


/*
 * Generate suggested filename for an attachment,
 * based on author ID, original extension, etc.
 *
 */
QString MiscHelpers::getSuggestedFilename(QString authorId,
                                          QString postType,
                                          QString postTitle,
                                          QString fileUrl)
{
    // Get original filename in server
    QString originalFilename = fileUrl.split("/").last();

    QString suggestedFilename = authorId;
    suggestedFilename.append("_" + postType);
    if (!postTitle.trimmed().isEmpty())
    {
        suggestedFilename.append("_" + postTitle);
    }

    suggestedFilename.replace("@", "-"); // Avoid certain chars from author ID
    suggestedFilename.replace(".", "-");
    suggestedFilename.replace("/", "-");
    suggestedFilename.remove("?");
    suggestedFilename.remove("!");
    suggestedFilename.remove("*");
    suggestedFilename.remove(":");
    suggestedFilename.remove(";");
    suggestedFilename.remove("\"");

    suggestedFilename.replace(" ", "_"); // Avoid spaces in filename

    suggestedFilename.append("_" + originalFilename); // Something like A53r2w.png

    // Extension will be .bin for generic files, at the moment

    return suggestedFilename;
}



/*
 * Return MIME content type, like image/png, audio/ogg, etc.
 *
 * using libmagic
 *
 */
QString MiscHelpers::getFileMimeType(QString fileUri)
{
    qDebug() << "getFileMimeType() file:" << fileUri;

    magic_t magicCookie = magic_open(MAGIC_MIME_TYPE);
    if (magicCookie == NULL)
    {
        qDebug() << "libmagic init error!";
        return QString();
    }

    if (magic_load(magicCookie, NULL) != 0)
    {
        qDebug() << "magic_load() error! No system-wide libmagic DB?";
        // Try loading from a subdirectory instead (in mswindows, maybe osx)
        if (magic_load(magicCookie, "plugins/magic") != 0)
        {
            qDebug() << "magic_load() error; Can't load plugins/magic.mgc";
            magic_close(magicCookie);
            return QString();
        }
    }

    const char *magicMimeString;
    magicMimeString = magic_file(magicCookie, fileUri.toLocal8Bit());

    QString mimeType = QString(magicMimeString);
    qDebug() << "File MIME type:" << mimeType;

    magic_close(magicCookie);

    return mimeType;
}



/*
 * Return width of an image
 *
 */
int MiscHelpers::getImageWidth(QString fileURI)
{
    QImageReader imageReader(fileURI);

    return imageReader.size().width();
}


bool MiscHelpers::isImageAnimated(QString fileUri)
{
    //qDebug() << "QMovie::supportedFormats()" << QMovie::supportedFormats();
    bool isAnimated = false;

    QImageReader imageReader(fileUri);
    if (imageReader.supportsAnimation())
    {
        QMovie movie;
        movie.setFileName(fileUri);
        qDebug() << "Image format SUPPORTS animation; Frames:" << movie.frameCount();
        if (movie.frameCount() > 1) // FIXME: doesn't work with .MNG, so it returns 0
        {
            qDebug() << "Image IS animated.";
            isAnimated = true;
        }
    }


    return isAnimated;
}



QString MiscHelpers::fixLongName(QString name)
{
    // very TMP optimization of LOOONG names / FIXME
    if (name.length() > 16)
    {
        name.replace("@", "@ ");
        name.replace(".", ". ");
    }

    return name;
}



/*
 * Return a pretty string with the size of a file, like
 * "33 KiB", "512 bytes" or "3,2 MiB"
 *
 */
QString MiscHelpers::fileSizeString(QString fileURI)
{
    QFileInfo fileInfo(fileURI);
    double fileSize = fileInfo.size();

    QString sizeUnit = tr("bytes");
    if (fileSize > 1024) // if > 1024 bytes, transform to KiB
    {
        fileSize /= 1024.0;
        sizeUnit = "KiB";
    }
    if (fileSize > 1024) // if > 1024 KiB, transform to MiB
    {
        fileSize /= 1024.0;
        sizeUnit = "MiB";
    }


    // Return with 0 padding and 2 decimal precision
    return QString("%1 %2").arg(QLocale::system().toString(fileSize, 'f', 2))
                           .arg(sizeUnit);
}

/*
 * Localized resolution string
 *
 */
QString MiscHelpers::resolutionString(int width, int height)
{
    return QString("%1 x %2").arg(QLocale::system().toString(width))
                             .arg(QLocale::system().toString(height));
}




/*
 * Parse a string of HTML and replace the URL in each <img src=""> tag with
 * the corresponding locally cached filename.
 *
 * Return also the string list of the URL's to download
 *
 */
QStringList MiscHelpers::htmlWithReplacedImages(QString originalHtml, int postWidth)
{
    // if no <img tag is found, just return the original HTML
    if (!originalHtml.contains("<img", Qt::CaseInsensitive))
    {
        //qDebug() << "MiscHelpers::htmlWithReplacedImages(); HTML does NOT contain <img> tags...";
        return QStringList(originalHtml);
    }

    //qDebug() << "MiscHelpers::htmlWithReplacedImages(); HTML contains some <img> tags...";
    QString newHtml = originalHtml;
    newHtml.remove("\n"); // Remove in case misbehaving applications added any

    QStringList imageList;
    QString imgSrc;

    QRegExp regExp("\\<img(.+)src=(\"|\\')([^\"\\']+)(\"|\\')(.*)\\>");
    regExp.setMinimal(true);

    int matchedLength = 0;
    int stringPos = 0;
    while (matchedLength != -1)
    {
        stringPos = regExp.indexIn(newHtml, stringPos);
        matchedLength = regExp.matchedLength();


        //qDebug() << "#######\n\nregExp match = " << regExp.cap(0);
        //qDebug() << "Groups:" << regExp.cap(1) << " // " << regExp.cap(2)
        //         << " // " << regExp.cap(3) << " // " << regExp.cap(4)
        //         << " // " << regExp.cap(5);
        //qDebug() << "Matched length is:" << matchedLength;

        imgSrc = regExp.cap(3);
        if (!imgSrc.isEmpty()) // if not an empty string, add to the list, and replace HTML
        {
            // If the url had parameters, they _might_ have &amp; in place of "&"
            imgSrc.replace("&amp;", "&"); // Put them back

            // Check if http part (scheme) is missing from URL, and add it
            if (!imgSrc.startsWith("http"))
            {
                imgSrc.prepend("http:");
            }

            // Add URL to list
            imageList.append(imgSrc);

            QString cachedImageFilename = getCachedImageFilename(imgSrc);

            int imageWidth = getImageWidth(cachedImageFilename);
            // if width is bigger than the post, make it smaller to fit
            if (imageWidth > postWidth - 32)
            {
                // Some margins, to account for a scrollbar or a tab space before the image
                imageWidth = postWidth - 32;
            }

            newHtml.replace(stringPos,
                            matchedLength,
                            "<img src=\"" + cachedImageFilename +  "\" "
                            "width=\"" + QString("%1").arg(imageWidth) + "\" />");
        }

        stringPos += matchedLength; // FIXME: error control
    }

    imageList.prepend(newHtml); // The modified HTML goes before the image list
    //qDebug() << "Returned HTML and images:\n" << imageList << "\n#################";

    return imageList;
}


/*
 * Basic cleanup of HTML stuff
 *
 */
QString MiscHelpers::cleanupHtml(QString originalHtml)
{
    QString cleanHtml = originalHtml;

    cleanHtml.replace("\n", " ");  // Remove line breaks, as that results in server error 500


    QRegExp doctypeRE("<!DOCTYPE.*>");
    doctypeRE.setMinimal(true);
    cleanHtml.remove(doctypeRE);

    QRegExp headRE("<html><head>.*</head>");
    headRE.setMinimal(true);
    cleanHtml.remove(headRE);

    QRegExp bodyRE("<body style.*>");
    bodyRE.setMinimal(true);
    cleanHtml.remove(bodyRE);

    //////////////////////////////////////// Remove <span style=...> from links
    QRegExp linkStyleRE("<a href=.*>.*</a>");
    linkStyleRE.setMinimal(true);
    QRegExp spanRE("<span style=.*>(.*)</span>"); // FIXME: remove ONLY color info
    spanRE.setMinimal(true);

    int pos = 0;
    while ((pos = linkStyleRE.indexIn(cleanHtml, pos)) != -1)
    {
        int removedTextOffset = 0;
        if (spanRE.indexIn(cleanHtml, pos) != -1)
        {
            // Replace the whole span tag by just what was inside
            cleanHtml.replace(spanRE.cap(0), spanRE.cap(1));
            //qDebug() << "spanRE capture: " << spanRE.capturedTexts();
            removedTextOffset = spanRE.cap(0).length() - spanRE.cap(1).length();
        }

        pos += linkStyleRE.matchedLength() - removedTextOffset;
    }

    ////////////////////////////////////// Remove style from <ul> <ol> and <li>
    QRegExp ulStyleRE("<ul style.*>");
    ulStyleRE.setMinimal(true);
    cleanHtml.replace(ulStyleRE, "<ul>");

    QRegExp olStyleRE("<ol style.*>");
    olStyleRE.setMinimal(true);
    cleanHtml.replace(olStyleRE, "<ol>");

    QRegExp liStyleRE("<li style.*>");
    liStyleRE.setMinimal(true);
    cleanHtml.replace(liStyleRE, "<li>");


    // FIXME: Maybe try to remove background colors from <p> elements

    cleanHtml.remove("</body></html>");

    return cleanHtml.trimmed();
}

/*
 * Remove only <a href...> and </a> from a HTML text
 *
 */
QString MiscHelpers::htmlWithoutLinks(QString originalHtml)
{
    QString cleanHtml = originalHtml;

    QRegExp linksRE("<a href=.*>");
    linksRE.setMinimal(true);
    linksRE.setCaseSensitivity(Qt::CaseInsensitive);
    cleanHtml.remove(linksRE);

    cleanHtml.remove("</a>", Qt::CaseInsensitive);


    return cleanHtml;
}

QString MiscHelpers::htmlToPlainText(QString html, int charLimit)
{
    QTextDocument textDocument;
    textDocument.setHtml(html);

    QString plainText = textDocument.toPlainText().trimmed();
    plainText.replace("\n", " ");
    plainText = plainText.trimmed();

    if (charLimit != 0 && plainText.length() > charLimit)
    {
        plainText = plainText.left(charLimit).trimmed() + "...";
    }

    return plainText;
}



/*
 * Return some HTML with a blockquote, quote symbols, etc.
 *
 *
 */
QString MiscHelpers::quotedText(QString author, QString content)
{
    QTextDocument textDocument;
    textDocument.setHtml(content);

    content = textDocument.toPlainText().trimmed();
    content.replace("<", "&lt;"); // back to HTML entities
    content.replace(">", "&gt;");
    content.replace("\n", "<br>"); // Important to replace this AFTER < and >

    QString quoteHtml = "&gt;&gt; <b><u>"+ author + ":</u></b>"  // >> + name
                        "<blockquote>"
                        "<small><i>&ldquo;" + content + "&rdquo;</i></small>"
                        "</blockquote> <br>";

    return quoteHtml;
}


/*
 * Limit length of a string to charLimit chars,
 * removing characters from the middle
 *
 */
QString MiscHelpers::elidedText(QString text, int charLimit)
{
    QString shortText = text;

    if (text.length() > charLimit)
    {
        charLimit -= 5;

        shortText = text.left(charLimit / 2);
        shortText.append(" ... ");
        shortText.append(text.right(charLimit / 2));
    }

    return shortText;
}



/*
 * Generate the first part of the HTML of a post
 * that includes media attachments
 *
 */
QString MiscHelpers::mediaHtmlBase(QString postType,
                                   QString attachmentFilename,
                                   QString tooltipMessage,
                                   QString belowMessage,
                                   int imageWidth)
{
    QString html = "<table cellpadding=4 width=100% "
                   "style=\" "
                   "border-style: solid; "
                   "border-width: 1px; "
                   "margin: 2px; \" >"
                   // First row, with gradient
                   "<tr style=\" "
                   "background-color: "
                   "qlineargradient(spread:reflect, "
                   "x1:0, y1:0, x2:0.5, y2:0, "
                   "stop:0 rgba(255, 255, 255, 0), "
                   "stop:1 palette(highlight) ); "
                   "\" >"
                   "<td align=center>"
                   "<a title=\"" + tooltipMessage + "\" href";

    if (postType == "image")
    {
        html.append("=\"image:/" + attachmentFilename + "\" >");
    }
    else
    {
        html.append("=\"attachment:/\" >");
    }

    html.append("<img src=\"" + attachmentFilename +  "\" "
                "width=\"" + QString("%1").arg(imageWidth) + "\" />"
                "</a></td></tr>");

    // Second row, to add a message related to the image or attachment
    html.append("<tr><td align=center><small>"
                + belowMessage +
                "</small></td></tr>"
                "</table>"
                "<br><br>"); // Double line-break, because the content might not start with <p>

    return html;
}
