/*
    csshelper.cpp

    This file is part of KMail, the KDE mail client.
    SPDX-FileCopyrightText: 2003 Marc Mutz <mutz@kde.org>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "csshelperbase.h"
#include "header/headerstyleplugin.h"
#include "utils/iconnamecache.h"

#include <QApplication>
#include <QPaintDevice>
#include <QPalette>
#include <QUrl>

#define USE_HTML_STYLE_COLOR 1
namespace MessageViewer
{
namespace
{
// some QColor manipulators that hide the ugly QColor API w.r.t. HSV:
inline QColor darker(const QColor &c)
{
    int h;
    int s;
    int v;
    c.getHsv(&h, &s, &v);
    return QColor::fromHsv(h, s, v * 4 / 5);
}

inline QColor desaturate(const QColor &c)
{
    int h;
    int s;
    int v;
    c.getHsv(&h, &s, &v);
    return QColor::fromHsv(h, s / 8, v);
}

inline QColor fixValue(const QColor &c, int newV)
{
    int h;
    int s;
    int v;
    c.getHsv(&h, &s, &v);
    return QColor::fromHsv(h, s, newV);
}

inline int getValueOf(const QColor &c)
{
    int h;
    int s;
    int v;
    c.getHsv(&h, &s, &v);
    return v;
}

static const struct {
    CSSHelperBase::InlineMessageType type;
    const char *cssName;
} inlineMessageStyles[] = {{CSSHelperBase::Positive, "inlineMessagePositive"},
                           {CSSHelperBase::Information, "inlineMessageInformation"},
                           {CSSHelperBase::Warning, "inlineMessageWarning"},
                           {CSSHelperBase::Error, "inlineMessageError"}};
}

CSSHelperBase::CSSHelperBase(const QPaintDevice *pd)
    : mPaintDevice(pd)
{
    recalculatePGPColors();
    const QString imgSrcShow = QStringLiteral("quicklistClosed.png");
    const QString imgSrcHide = QStringLiteral("quicklistOpened.png");
    imgShowUrl = QUrl::fromLocalFile(MessageViewer::IconNameCache::instance()->iconPathFromLocal(imgSrcShow)).url();
    imgHideUrl = QUrl::fromLocalFile(MessageViewer::IconNameCache::instance()->iconPathFromLocal(imgSrcHide)).url();
}

CSSHelperBase::~CSSHelperBase() = default;

void CSSHelperBase::recalculatePGPColors()
{
    // determine the frame and body color for PGP messages from the header color
    // if the header color equals the background color then the other colors are
    // also set to the background color (-> old style PGP message viewing)
    // else
    // the brightness of the frame is set to 4/5 of the brightness of the header
    // and in case of a light background color
    // the saturation of the body is set to 1/8 of the saturation of the header
    // while in case of a dark background color
    // the value of the body is set to the value of the background color

    // Check whether the user uses a light color scheme
    const int vBG = getValueOf(mBackgroundColor);
    const bool lightBG = vBG >= 128;
    if (cPgpOk1H == mBackgroundColor) {
        cPgpOk1F = mBackgroundColor;
        cPgpOk1B = mBackgroundColor;
    } else {
        cPgpOk1F = darker(cPgpOk1H);
        cPgpOk1B = lightBG ? desaturate(cPgpOk1H) : fixValue(cPgpOk1H, vBG);
    }
    if (cPgpOk0H == mBackgroundColor) {
        cPgpOk0F = mBackgroundColor;
        cPgpOk0B = mBackgroundColor;
    } else {
        cPgpOk0F = darker(cPgpOk0H);
        cPgpOk0B = lightBG ? desaturate(cPgpOk0H) : fixValue(cPgpOk0H, vBG);
    }
    if (cPgpWarnH == mBackgroundColor) {
        cPgpWarnF = mBackgroundColor;
        cPgpWarnB = mBackgroundColor;
    } else {
        cPgpWarnF = darker(cPgpWarnH);
        cPgpWarnB = lightBG ? desaturate(cPgpWarnH) : fixValue(cPgpWarnH, vBG);
    }
    if (cPgpErrH == mBackgroundColor) {
        cPgpErrF = mBackgroundColor;
        cPgpErrB = mBackgroundColor;
    } else {
        cPgpErrF = darker(cPgpErrH);
        cPgpErrB = lightBG ? desaturate(cPgpErrH) : fixValue(cPgpErrH, vBG);
    }
    if (cPgpEncrH == mBackgroundColor) {
        cPgpEncrF = mBackgroundColor;
        cPgpEncrB = mBackgroundColor;
    } else {
        cPgpEncrF = darker(cPgpEncrH);
        cPgpEncrB = lightBG ? desaturate(cPgpEncrH) : fixValue(cPgpEncrH, vBG);
    }
}

QString CSSHelperBase::addEndBlockQuote(int numberBlock) const
{
    QString blockQuote;
    for (int i = 0; i < numberBlock; ++i) {
        blockQuote += QLatin1String("</blockquote>");
    }
    return blockQuote;
}

QString CSSHelperBase::addStartBlockQuote(int numberBlock) const
{
    QString blockQuote;
    for (int i = 0; i < numberBlock; ++i) {
        blockQuote += QLatin1String("<blockquote>");
    }
    return blockQuote;
}

QString CSSHelperBase::extraScreenCss(const QString &headerFont) const
{
    if (mHeaderPlugin) {
        return mHeaderPlugin->extraScreenCss(headerFont);
    }
    return {};
}

QString CSSHelperBase::extraPrintCss(const QString &headerFont) const
{
    if (mHeaderPlugin) {
        return mHeaderPlugin->extraPrintCss(headerFont);
    }
    return {};
}

QString CSSHelperBase::extraCommonCss(const QString &headerFont) const
{
    QString result;
    if (mHeaderPlugin) {
        result = mHeaderPlugin->extraCommonCss(headerFont);
        if (result.isEmpty()) {
            // Add default value
            result = QStringLiteral(
                "div.header table {\n"
                "  width: 100% ! important;\n"
                "  border-width: 0px ! important;\n"
                "  line-height: normal;\n"
                "}\n\n");
        }
    }
    return result;
}

QString CSSHelperBase::cssDefinitions(bool fixed) const
{
    return commonCssDefinitions() + QLatin1String("@media screen {\n\n") + screenCssDefinitions(this, fixed)
        + QLatin1String(
               "}\n"
               "@media print {\n\n")
        + printCssDefinitions(fixed) + QLatin1String("}\n");
}

QString CSSHelperBase::htmlHead(bool fixedFont) const
{
    Q_UNUSED(fixedFont)
    return QStringLiteral(
        "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
        "<html><head><title></title></head>\n"
        "<body>\n");
}

QString CSSHelperBase::quoteFontTag(int level) const
{
    if (level < 0) {
        level = 0;
    }
    static const int numQuoteLevels = 3;
    const int effectiveLevel = mRecycleQuoteColors ? level % numQuoteLevels + 1 : qMin(level + 1, numQuoteLevels);
    if (level >= numQuoteLevels) {
        return QStringLiteral("<div class=\"deepquotelevel%1\">").arg(effectiveLevel);
    } else {
        return QStringLiteral("<div class=\"quotelevel%1\">").arg(effectiveLevel);
    }
}

QString CSSHelperBase::fullAddressList() const
{
    QString css = QStringLiteral(
        "input[type=checkbox].addresslist_checkbox {display: none}\n"
        ".addresslist_label_short {border: 1px; border-radius: 5px; padding: 0px 10px 0px 10px; white-space: nowrap}\n"
        ".addresslist_label_full {border: 1px; border-radius: 5px; padding: 0px 10px 0px 10px; white-space: nowrap}\n");
    css += QStringLiteral(".addresslist_label_short {background-image:url(%1);\nbackground-repeat: no-repeat}\n").arg(imgShowUrl);
    css += QStringLiteral(".addresslist_label_full {background-image:url(%1);\nbackground-repeat: no-repeat}\n\n").arg(imgHideUrl);
    for (const QString &str : {QStringLiteral("Cc"), QStringLiteral("To"), QStringLiteral("Bcc")}) {
        css += QStringLiteral(
                   "input ~ span.fullFull%1AddressList {display: block}\n"
                   "input ~ span.shortFull%1AddressList {display: none}\n"
                   "input:checked ~ span.fullFull%1AddressList {display: none}\n"
                   "input:checked ~ span.shortFull%1AddressList {display: block}\n\n")
                   .arg(str);
    }
    return css;
}

QString CSSHelperBase::nonQuotedFontTag() const
{
    return QStringLiteral("<div class=\"noquote\">");
}

QFont CSSHelperBase::bodyFont(bool fixed, bool print) const
{
    return fixed ? (print ? mFixedPrintFont : mFixedFont) : (print ? mPrintFont : mBodyFont);
}

int CSSHelperBase::fontSize(bool fixed, bool print) const
{
    return bodyFont(fixed, print).pointSize();
}

namespace
{
int pointsToPixel(const QPaintDevice *pd, int pointSize)
{
    return (pointSize * pd->logicalDpiY() + 36) / 72;
}
}

void CSSHelperBase::setHeaderPlugin(const HeaderStylePlugin *headerPlugin)
{
    mHeaderPlugin = headerPlugin;
}

static const char *const quoteFontSizes[] = {"85", "80", "75"};

QString CSSHelperBase::printCssDefinitions(bool fixed) const
{
    const QString headerFont = defaultPrintHeaderFont();

    const QFont printFont = bodyFont(fixed, true /* print */);
    QString quoteCSS;
    if (printFont.italic()) {
        quoteCSS += QLatin1String("  font-style: italic ! important;\n");
    }
    if (printFont.bold()) {
        quoteCSS += QLatin1String("  font-weight: bold ! important;\n");
    }
    if (!quoteCSS.isEmpty()) {
        quoteCSS = QLatin1String("div.noquote {\n") + quoteCSS + QLatin1String("}\n\n");
    }
    quoteCSS += quoteCssDefinition();

    QStringList inlineMessageCss;
    inlineMessageCss.reserve(MESSAGE_TYPE_COUNT);
    for (const auto &msgStyle : inlineMessageStyles) {
        inlineMessageCss.push_back(QLatin1String("div.") + QString::fromLatin1(msgStyle.cssName));
    }

    return QStringLiteral(
               "body {\n"
               "  font-family: \"%1\" ! important;\n"
               "  font-size: %2pt ! important;\n"
               "  color: #000000 ! important;\n"
               "  background-color: #ffffff ! important\n"
               "}\n\n")
               .arg(printFont.family(), QString::number(printFont.pointSize()))
        + linkColorDefinition()
        + QStringLiteral(
              "tr.textAtmH,\n"
              "tr.signInProgressH,\n"
              "tr.rfc822H,\n"
              "tr.encrH,\n"
              "tr.signOkKeyOkH,\n"
              "tr.signOkKeyBadH,\n"
              "tr.signWarnH,\n"
              "tr.signErrH,\n"
              "div.header {\n"
              "%1"
              "}\n\n"

              "%2"

              "div.spamheader {\n"
              "  display:none ! important;\n"
              "}\n\n"

              "%3 {\n"
              "  border: 2px solid #ffffff ! important;\n"
              "  line-height: normal;\n"
              "}\n\n"

              "div.senderpic{\n"
              "  font-size:0.8em ! important;\n"
              "  border:1px solid black ! important;\n"
              "  background-color:%2 ! important;\n"
              "}\n\n"

              "div.senderstatus{\n"
              "  text-align:center ! important;\n"
              "}\n\n"

              "div.noprint {\n"
              "  display:none ! important;\n"
              "}\n\n")
              .arg(headerFont, extraPrintCss(headerFont), inlineMessageCss.join(QLatin1String(", ")))
        + quoteCSS + fullAddressList();
}

QString CSSHelperBase::linkColorDefinition() const
{
    const QString linkColor = mLinkColor.name();
    if (mUseBrowserColor) {
#ifdef USE_HTML_STYLE_COLOR
        const QString bgColor = mBackgroundColor.name();
        const QString background = QStringLiteral("  background: %1 ! important;\n").arg(bgColor);

        return QStringLiteral(
                   "div#headerbox a:link {\n"
                   "  color: %1 ! important;\n"
                   "  text-decoration: none ! important;\n"
                   "}\n\n"
                   "div#header a:link {\n"
                   "  color: %1 ! important;\n"
                   "  text-decoration: none ! important;\n"
                   "}\n\n"
                   "div.header {\n"
                   "    %2"
                   "}\n\n"
                   "div#headerbox {\n"
                   "    %2"
                   "}\n\n")
            .arg(linkColor, background);
#else
        return QStringLiteral(
                   "div#headerbox a:link {\n"
                   "  color: %1 ! important;\n"
                   "  text-decoration: none ! important;\n"
                   "}\n\n"
                   "div#header a:link {\n"
                   "  color: %1 ! important;\n"
                   "  text-decoration: none ! important;\n"
                   "}\n\n")
            .arg(linkColor);
#endif
    } else {
        return QStringLiteral(
                   "a {\n"
                   "  color: %1 ! important;\n"
                   "  text-decoration: none ! important;\n"
                   "}\n\n")
            .arg(linkColor);
    }
}

QString CSSHelperBase::quoteCssDefinition() const
{
    QString quoteCSS;
    QString blockQuote;
    for (int i = 0; i < 9; ++i) {
        blockQuote += QStringLiteral("blockquote ");
        quoteCSS += QStringLiteral(
                        "%2{\n"
                        "  margin: 4pt 0 4pt 0;\n"
                        "  padding: 0 0 0 1em;\n"
                        "  border-left: 2px solid %1;\n"
                        "  unicode-bidi: plaintext\n"
                        "}\n\n")
                        .arg(quoteColorName(i), blockQuote);
    }
    quoteCSS += QStringLiteral(
        ".quotemarks{\n"
        "  color:transparent;\n"
        "  font-size:0px;\n"
        "}\n\n");
    quoteCSS += QStringLiteral(
        ".quotemarksemptyline{\n"
        "  color:transparent;\n"
        "  font-size:0px;\n"
        "  line-height: 12pt;\n"
        "}\n\n");
    return quoteCSS;
}

QString CSSHelperBase::defaultPrintHeaderFont() const
{
    const QString headerFont = QStringLiteral(
                                   "  font-family: \"%1\" ! important;\n"
                                   "  font-size: %2pt ! important;\n")
                                   .arg(mPrintFont.family())
                                   .arg(mPrintFont.pointSize());
    return headerFont;
}

QString CSSHelperBase::defaultScreenHeaderFont() const
{
    const QString headerFont = QStringLiteral(
                                   "  font-family: \"%1\" ! important;\n"
                                   "  font-size: %2px ! important;\n")
                                   .arg(mBodyFont.family())
                                   .arg(pointsToPixel(mPaintDevice, mBodyFont.pointSize()));
    return headerFont;
}

QString CSSHelperBase::screenCssDefinitions(const CSSHelperBase *helper, bool fixed) const
{
    const QString bgColor = mBackgroundColor.name();
    const QString headerFont = defaultScreenHeaderFont();
#ifdef USE_HTML_STYLE_COLOR
    const QString fgColor = mUseBrowserColor ? QStringLiteral("black") : mForegroundColor.name();
    const QString background = mUseBrowserColor ? QString() : QStringLiteral("  background-color: %1 ! important;\n").arg(bgColor);
    const QString signWarnBColorName = mUseBrowserColor ? QStringLiteral("white") : cPgpWarnB.name();
    const QString cPgpErrBColorName = mUseBrowserColor ? QStringLiteral("white") : cPgpErrB.name();
    const QString cPgpEncrBColorName = mUseBrowserColor ? QStringLiteral("white") : cPgpEncrB.name();
    const QString cPgpOk1BColorName = mUseBrowserColor ? QStringLiteral("white") : cPgpOk1B.name();
    const QString cPgpOk0BColorName = mUseBrowserColor ? QStringLiteral("white") : cPgpOk0B.name();
#else
    const QString fgColor = mForegroundColor.name();
    const QString background = QStringLiteral("  background-color: %1 ! important;\n").arg(bgColor);
    const QString signWarnBColorName = cPgpWarnB.name();
    const QString cPgpErrBColorName = cPgpErrB.name();
    const QString cPgpEncrBColorName = cPgpEncrB.name();
    const QString cPgpOk1BColorName = cPgpOk1B.name();
    const QString cPgpOk0BColorName = cPgpOk0B.name();
#endif
    const QString bodyFontSize = QString::number(pointsToPixel(helper->mPaintDevice, fontSize(fixed))) + QLatin1String("px");
    const QPalette &pal = QApplication::palette();

    QString quoteCSS;
    if (bodyFont(fixed).italic()) {
        quoteCSS += QLatin1String("  font-style: italic ! important;\n");
    }
    if (bodyFont(fixed).bold()) {
        quoteCSS += QLatin1String("  font-weight: bold ! important;\n");
    }
    if (!quoteCSS.isEmpty()) {
        quoteCSS = QLatin1String("div.noquote {\n") + quoteCSS + QLatin1String("}\n\n");
    }

    // CSS definitions for quote levels 1-3
    for (int i = 0; i < 3; ++i) {
        quoteCSS += QStringLiteral(
                        "div.quotelevel%1 {\n"
                        "  color: %2 ! important;\n")
                        .arg(QString::number(i + 1), quoteColorName(i));
        if (mQuoteFont.italic()) {
            quoteCSS += QStringLiteral("  font-style: italic ! important;\n");
        }
        if (mQuoteFont.bold()) {
            quoteCSS += QStringLiteral("  font-weight: bold ! important;\n");
        }
        if (mShrinkQuotes) {
            quoteCSS += QLatin1String("  font-size: ") + QString::fromLatin1(quoteFontSizes[i]) + QLatin1String("% ! important;\n");
        }
        quoteCSS += QStringLiteral("}\n\n");
    }

    // CSS definitions for quote levels 4+
    for (int i = 0; i < 3; ++i) {
        quoteCSS += QStringLiteral(
                        "div.deepquotelevel%1 {\n"
                        "  color: %2 ! important;\n")
                        .arg(QString::number(i + 1), quoteColorName(i));
        if (mQuoteFont.italic()) {
            quoteCSS += QStringLiteral("  font-style: italic ! important;\n");
        }
        if (mQuoteFont.bold()) {
            quoteCSS += QStringLiteral("  font-weight: bold ! important;\n");
        }
        if (mShrinkQuotes) {
            quoteCSS += QStringLiteral("  font-size: 70% ! important;\n");
        }
        quoteCSS += QLatin1String("}\n\n");
    }

    quoteCSS += quoteCssDefinition();

    // CSS definitions for inline message boxes
    QString inlineMessageCss;
    for (const auto &msgStyle : inlineMessageStyles) {
        const auto c = cInlineMessage[msgStyle.type];
        inlineMessageCss += QStringLiteral(
                                R"(
            div.%1 {
                border: 1px solid rgba(%2, %3, %4) ! important;
                border-radius: 2px;
                box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.5);
                background-color: rgba(%2, %3, %4, 0.2) ! important;
            }
            div.%1 a:link {
                color: %5 ! important;
                text-decoration: none ! important;
            }
         )")
                                .arg(QString::fromLatin1(msgStyle.cssName))
                                .arg(c.red())
                                .arg(c.green())
                                .arg(c.blue())
                                .arg(mLinkColor.name());
    }

    return QStringLiteral(
               "body {\n"
               "  font-family: \"%1\" ! important;\n"
               "  font-size: %2 ! important;\n"
               "  color: %3 ! important;\n"
               "%4"
               "}\n\n")
               .arg(bodyFont(fixed).family(), bodyFontSize, fgColor, background)
        + linkColorDefinition()
        + QStringLiteral(
              "a.white {\n"
              "  color: white ! important;\n"
              "}\n\n"

              "a.black {\n"
              "  color: black ! important;\n"
              "}\n\n"

              "table.textAtm { background-color: %1 ! important; }\n\n"

              "tr.textAtmH {\n"
              "  background-color: %2 ! important;\n"
              "%3"
              "}\n\n"

              "tr.textAtmB {\n"
              "  background-color: %2 ! important;\n"
              "}\n\n"

              "table.signInProgress,\n"
              "table.rfc822 {\n"
              "  background-color: %2 ! important;\n"
              "}\n\n"

              "tr.signInProgressH,\n"
              "tr.rfc822H {\n"
              "%3"
              "}\n\n")
              .arg(fgColor, bgColor, headerFont)
        + QStringLiteral(
              "table.encr {\n"
              "  background-color: %1 ! important;\n"
              "}\n\n"

              "tr.encrH {\n"
              "  background-color: %2 ! important;\n"
              "  color: %3 ! important;\n"
              "%4"
              "}\n\n"

              "tr.encrB { background-color: %5 ! important; }\n\n")
              .arg(cPgpEncrF.name(), cPgpEncrH.name(), cPgpEncrHT.name(), headerFont, cPgpEncrBColorName)
        + QStringLiteral(
              "table.signOkKeyOk {\n"
              "  background-color: %1 ! important;\n"
              "}\n\n"

              "tr.signOkKeyOkH {\n"
              "  background-color: %2 ! important;\n"
              "  color: %3 ! important;\n"
              "%4"
              "}\n\n"

              "tr.signOkKeyOkB { background-color: %5 ! important; }\n\n")
              .arg(cPgpOk1F.name(), cPgpOk1H.name(), cPgpOk1HT.name(), headerFont, cPgpOk1BColorName)
        + QStringLiteral(
              "table.signOkKeyBad {\n"
              "  background-color: %1 ! important;\n"
              "}\n\n"

              "tr.signOkKeyBadH {\n"
              "  background-color: %2 ! important;\n"
              "  color: %3 ! important;\n"
              "%4"
              "}\n\n"

              "tr.signOkKeyBadB { background-color: %5 ! important; }\n\n")
              .arg(cPgpOk0F.name(), cPgpOk0H.name(), cPgpOk0HT.name(), headerFont, cPgpOk0BColorName)
        + QStringLiteral(
              "table.signWarn {\n"
              "  background-color: %1 ! important;\n"
              "}\n\n"

              "tr.signWarnH {\n"
              "  background-color: %2 ! important;\n"
              "  color: %3 ! important;\n"
              "%4"
              "}\n\n"

              "tr.signWarnB { background-color: %5 ! important; }\n\n")
              .arg(cPgpWarnF.name(), cPgpWarnH.name(), cPgpWarnHT.name(), headerFont, signWarnBColorName)
        + QStringLiteral(
              "table.signErr {\n"
              "  background-color: %1 ! important;\n"
              "}\n\n"

              "tr.signErrH {\n"
              "  background-color: %2 ! important;\n"
              "  color: %3 ! important;\n"
              "%4"
              "}\n\n"

              "tr.signErrB { background-color: %5 ! important; }\n\n")
              .arg(cPgpErrF.name(), cPgpErrH.name(), cPgpErrHT.name(), headerFont, cPgpErrBColorName)
        + inlineMessageCss
        + QStringLiteral(
              "div.header {\n"
              "%1"
              "}\n\n"

              "%2"

              "div.senderpic{\n"
              "  padding: 0px ! important;\n"
              "  font-size:0.8em ! important;\n"
              "  border:1px solid %4 ! important;\n"
              "  background-color:%3 ! important;\n"
              "}\n\n"

              "div.senderstatus{\n"
              "  text-align:center ! important;\n"
              "}\n\n")

              .arg(headerFont, extraScreenCss(headerFont), pal.color(QPalette::Highlight).name(), pal.color(QPalette::Window).name())
        + quoteCSS + fullAddressList();
}

QString CSSHelperBase::commonCssDefinitions() const
{
    const QString headerFont = defaultScreenHeaderFont();

    QStringList inlineMessageCss;
    inlineMessageCss.reserve(MESSAGE_TYPE_COUNT);
    for (const auto &msgStyle : inlineMessageStyles) {
        inlineMessageCss.push_back(QLatin1String("div.") + QString::fromLatin1(msgStyle.cssName));
    }

    return QStringLiteral(
               "div.header {\n"
               "  margin-bottom: 10pt ! important;\n"
               "}\n\n"

               "pre.highlightattachment {\n"
               "  white-space: pre-wrap;\n"
               "}\n\n"

               "table.textAtm {\n"
               "  margin-top: 10pt ! important;\n"
               "  margin-bottom: 10pt ! important;\n"
               "}\n\n"

               "tr.textAtmH,\n"
               "tr.textAtmB,\n"
               "tr.rfc822B {\n"
               "  font-weight: normal ! important;\n"
               "}\n\n"

               "tr.signInProgressH,\n"
               "tr.rfc822H,\n"
               "tr.encrH,\n"
               "tr.signOkKeyOkH,\n"
               "tr.signOkKeyBadH,\n"
               "tr.signWarnH,\n"
               "tr.signErrH {\n"
               "  font-weight: bold ! important;\n"
               "}\n\n"

               "tr.textAtmH td,\n"
               "tr.textAtmB td {\n"
               "  padding: 3px ! important;\n"
               "}\n\n"

               "table.rfc822 {\n"
               "  width: 100% ! important;\n"
               "  border: solid 1px black ! important;\n"
               "  margin-top: 10pt ! important;\n"
               "  margin-bottom: 10pt ! important;\n"
               "}\n\n"

               "table.textAtm,\n"
               "table.encr,\n"
               "table.signWarn,\n"
               "table.signErr,\n"
               "table.signOkKeyBad,\n"
               "table.signOkKeyOk,\n"
               "table.signInProgress,\n"

               "%1"

               "%2 {\n"
               "  margin: 0px 5% 10px 5% ! important;\n"
               "  padding: 10px ! important;\n"
               "  text-align: left ! important;\n"
               "  line-height: normal;\n"
               "  min-height: %6px;\n"
               "}\n\n"

               "hr {\n"
               "  border: 0;\n"
               "  height: 0;\n"
               "  border-top: 1px solid rgba(%3, %4, %5, 0.3);\n"
               "}\n\n"

               "div.quotelevelmark {\n"
               "  position: absolute;\n"
               "  margin-left:-10px;\n"
               "}\n\n")
        .arg(extraCommonCss(headerFont), inlineMessageCss.join(QLatin1String(", ")))
        .arg(mForegroundColor.red())
        .arg(mForegroundColor.green())
        .arg(mForegroundColor.blue())
        .arg(QString::number(48));
}

void CSSHelperBase::setBodyFont(const QFont &font)
{
    mBodyFont = font;
}

void CSSHelperBase::setPrintFont(const QFont &font)
{
    mPrintFont = font;
}

QString CSSHelperBase::quoteColorName(int level) const
{
    return quoteColor(level).name();
}

QColor CSSHelperBase::quoteColor(int level) const
{
    const int actualLevel = qMax(level, 0) % 3;
    return mQuoteColor[actualLevel];
}

QColor CSSHelperBase::pgpWarnColor() const
{
    return cPgpWarnH;
}
}
