/***************************************************************************
 *   Copyright (C) 2006-2007 by Pino Toscano <pino@kde.org>                *
 *                                                                         *
 *   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.                                   *
 ***************************************************************************/

#include "guiutils.h"

// qt/kde includes
#include <KIconLoader>
#include <KLocalizedString>
#include <KMessageBox>
#include <QFileDialog>
#include <QPainter>
#include <QStandardPaths>
#include <QSvgRenderer>
#include <QTextDocument>

// local includes
#include "core/action.h"
#include "core/annotations.h"
#include "core/document.h"

#include <memory>

struct GuiUtilsHelper {
    GuiUtilsHelper()
    {
    }

    QSvgRenderer *svgStamps();

    QList<KIconLoader *> il;
    std::unique_ptr<QSvgRenderer> svgStampFile;
};

QSvgRenderer *GuiUtilsHelper::svgStamps()
{
    if (!svgStampFile.get()) {
        const QString stampFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("okular/pics/stamps.svg"));
        if (!stampFile.isEmpty()) {
            svgStampFile = std::make_unique<QSvgRenderer>(stampFile);
            if (!svgStampFile->isValid()) {
                svgStampFile.reset();
            }
        }
    }
    return svgStampFile.get();
}

Q_GLOBAL_STATIC(GuiUtilsHelper, s_data)

namespace GuiUtils
{
QString captionForAnnotation(const Okular::Annotation *ann)
{
    Q_ASSERT(ann);

    const bool hasComment = !ann->contents().isEmpty();

    QString ret;
    switch (ann->subType()) {
    case Okular::Annotation::AText:
        if (((Okular::TextAnnotation *)ann)->textType() == Okular::TextAnnotation::Linked)
            ret = i18n("Pop-up Note");
        else {
            if (((Okular::TextAnnotation *)ann)->inplaceIntent() == Okular::TextAnnotation::TypeWriter)
                ret = i18n("Typewriter");
            else
                ret = i18n("Inline Note");
        }
        break;
    case Okular::Annotation::ALine:
        if (((Okular::LineAnnotation *)ann)->linePoints().count() == 2)
            ret = hasComment ? i18n("Straight Line with Comment") : i18n("Straight Line");
        else
            ret = hasComment ? i18n("Polygon with Comment") : i18n("Polygon");
        break;
    case Okular::Annotation::AGeom:
        ret = hasComment ? i18n("Geometry with Comment") : i18n("Geometry");
        break;
    case Okular::Annotation::AHighlight:
        switch (((Okular::HighlightAnnotation *)ann)->highlightType()) {
        case Okular::HighlightAnnotation::Highlight:
            ret = hasComment ? i18n("Highlight with Comment") : i18n("Highlight");
            break;
        case Okular::HighlightAnnotation::Squiggly:
            ret = hasComment ? i18n("Squiggle with Comment") : i18n("Squiggle");
            break;
        case Okular::HighlightAnnotation::Underline:
            ret = hasComment ? i18n("Underline with Comment") : i18n("Underline");
            break;
        case Okular::HighlightAnnotation::StrikeOut:
            ret = hasComment ? i18n("Strike Out with Comment") : i18n("Strike Out");
            break;
        }
        break;
    case Okular::Annotation::AStamp:
        ret = hasComment ? i18n("Stamp with Comment") : i18n("Stamp");
        break;
    case Okular::Annotation::AInk:
        ret = hasComment ? i18n("Freehand Line with Comment") : i18n("Freehand Line");
        break;
    case Okular::Annotation::ACaret:
        ret = i18n("Caret");
        break;
    case Okular::Annotation::AFileAttachment:
        ret = i18n("File Attachment");
        break;
    case Okular::Annotation::ASound:
        ret = i18n("Sound");
        break;
    case Okular::Annotation::AMovie:
        ret = i18n("Movie");
        break;
    case Okular::Annotation::AScreen:
        ret = i18nc("Caption for a screen annotation", "Screen");
        break;
    case Okular::Annotation::AWidget:
        ret = i18nc("Caption for a widget annotation", "Widget");
        break;
    case Okular::Annotation::ARichMedia:
        ret = i18nc("Caption for a rich media annotation", "Rich Media");
        break;
    case Okular::Annotation::A_BASE:
        break;
    }
    return ret;
}

QString authorForAnnotation(const Okular::Annotation *ann)
{
    Q_ASSERT(ann);

    return !ann->author().isEmpty() ? ann->author() : i18nc("Unknown author", "Unknown");
}

QString contentsHtml(const Okular::Annotation *ann)
{
    QString text = ann->contents().toHtmlEscaped();
    text.replace(QLatin1Char('\n'), QLatin1String("<br>"));
    return text;
}

QString prettyToolTip(const Okular::Annotation *ann)
{
    Q_ASSERT(ann);

    QString author = authorForAnnotation(ann);
    QString contents = contentsHtml(ann);

    QString tooltip = QStringLiteral("<qt><b>") + i18n("Author: %1", author) + QStringLiteral("</b>");
    if (!contents.isEmpty())
        tooltip += QStringLiteral("<div style=\"font-size: 4px;\"><hr /></div>") + contents;

    tooltip += QLatin1String("</qt>");

    return tooltip;
}

QPixmap loadStamp(const QString &nameOrPath, int size, bool keepAspectRatio)
{
    const QString name = nameOrPath.toLower();

    // _name is the name of an Okular stamp symbols ( multiple symbols in a single *.svg file)
    QSvgRenderer *r = nullptr;
    if ((r = s_data->svgStamps()) && r->elementExists(name)) {
        const QSize stampSize = r->boundsOnElement(name).size().toSize();
        const QSize pixmapSize = stampSize.scaled(size, size, keepAspectRatio ? Qt::KeepAspectRatioByExpanding : Qt::IgnoreAspectRatio);
        QPixmap pixmap(pixmapSize);
        pixmap.fill(Qt::transparent);
        QPainter p(&pixmap);
        r->render(&p, name);
        p.end();
        return pixmap;
    }

    // _name is a path (do this before loading as icon name to avoid some rare weirdness )
    QPixmap pixmap;
    pixmap.load(nameOrPath);
    if (!pixmap.isNull()) {
        pixmap = pixmap.scaled(size, size, keepAspectRatio ? Qt::KeepAspectRatioByExpanding : Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
        return pixmap;
    }

    // _name is an icon name
    const KIconLoader *il = iconLoader();
    QString path;
    pixmap = il->loadIcon(name, KIconLoader::User, size, KIconLoader::DefaultState, QStringList(), &path, true);
    if (path.isEmpty())
        pixmap = il->loadIcon(name, KIconLoader::NoGroup, size);

    return pixmap; // can be a null pixmap
}

void addIconLoader(KIconLoader *loader)
{
    s_data->il.append(loader);
}

void removeIconLoader(KIconLoader *loader)
{
    s_data->il.removeAll(loader);
}

KIconLoader *iconLoader()
{
    return s_data->il.isEmpty() ? KIconLoader::global() : s_data->il.back();
}

void saveEmbeddedFile(Okular::EmbeddedFile *ef, QWidget *parent)
{
    const QString caption = i18n("Where do you want to save %1?", ef->name());
    const QString path = QFileDialog::getSaveFileName(parent, caption, ef->name());
    if (path.isEmpty())
        return;
    QFile targetFile(path);
    writeEmbeddedFile(ef, parent, targetFile);
}

void writeEmbeddedFile(Okular::EmbeddedFile *ef, QWidget *parent, QFile &target)
{
    if (!target.open(QIODevice::WriteOnly)) {
        KMessageBox::error(parent, i18n("Could not open \"%1\" for writing. File was not saved.", target.fileName()));
        return;
    }
    target.write(ef->data());
    target.close();
}

Okular::Movie *renditionMovieFromScreenAnnotation(const Okular::ScreenAnnotation *annotation)
{
    if (!annotation)
        return nullptr;

    if (annotation->action() && annotation->action()->actionType() == Okular::Action::Rendition) {
        Okular::RenditionAction *renditionAction = static_cast<Okular::RenditionAction *>(annotation->action());
        return renditionAction->movie();
    }

    return nullptr;
}

// from Arthur - qt4
static inline int qt_div_255(int x)
{
    return (x + (x >> 8) + 0x80) >> 8;
}

void colorizeImage(QImage &grayImage, const QColor &color, unsigned int destAlpha)
{
    // Make sure that the image is Format_ARGB32_Premultiplied
    if (grayImage.format() != QImage::Format_ARGB32_Premultiplied)
        grayImage = grayImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);

    // iterate over all pixels changing the alpha component value
    unsigned int *data = reinterpret_cast<unsigned int *>(grayImage.bits());
    unsigned int pixels = grayImage.width() * grayImage.height();
    int red = color.red(), green = color.green(), blue = color.blue();

    int source, sourceSat, sourceAlpha;
    for (unsigned int i = 0; i < pixels; ++i) { // optimize this loop keeping byte order into account
        source = data[i];
        sourceSat = qRed(source);
        int newR = qt_div_255(sourceSat * red), newG = qt_div_255(sourceSat * green), newB = qt_div_255(sourceSat * blue);
        if ((sourceAlpha = qAlpha(source)) == 255) {
            // use destAlpha
            data[i] = qRgba(newR, newG, newB, destAlpha);
        } else {
            // use destAlpha * sourceAlpha product
            if (destAlpha < 255)
                sourceAlpha = qt_div_255(destAlpha * sourceAlpha);
            data[i] = qRgba(newR, newG, newB, sourceAlpha);
        }
    }
}

}
