/*
 * Copyright (C) 2014-2026 CZ.NIC
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations including
 * the two.
 */

#include <QByteArray>
#include <QDate>
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QStringBuilder>
#include <QUrl>

#include "src/datovka_shared/html/html_export.h"
#include "src/datovka_shared/isds/box_interface.h"
#include "src/datovka_shared/isds/box_interface2.h"
#include "src/datovka_shared/isds/message_interface.h"
#include "src/datovka_shared/isds/message_interface2.h"
#include "src/datovka_shared/isds/json_conversion.h"
#include "src/datovka_shared/isds/type_conversion.h"
#include "src/datovka_shared/isds/type_description.h"
#include "src/datovka_shared/isds/to_text_conversion.h"
#include "src/datovka_shared/utility/date_time.h"

/* HTML macros for application UI */
#define divStart QLatin1String("<div>")
#define textInfoLine(text) \
	(QLatin1String("<div>") + (text) + QLatin1String("</div>"))
#define sectionLabel(title) \
	(QLatin1String("<h3>") + (title) + QLatin1String("</h3>"))
#define divEnd QLatin1String("</div>")

/* HTML macros for PDF export */
#define tableInfoStartPdf() \
	(QLatin1String("<table width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"))
#define messageTableInfoPdf(title, value) \
	(QLatin1String("<tr><td width=\"30%\" align=\"right\" style=\"padding-right:10px;\">") + (title).toHtmlEscaped() + \
	QLatin1String(": ") + QLatin1String("</td><td>") + \
	(value).toHtmlEscaped() + QLatin1String("</td></tr>"))
#define textLabelPdf(title) \
	(QLatin1String("<tr><td colspan=\"2\"><h3>") + \
	(title).toHtmlEscaped() + \
	QLatin1String("</h3></td></tr><tr><td colspan=\"2\"></td></tr>"))
#define lineSeparatorPdf() \
	(QLatin1String("<tr><td colspan=\"2\" style=\"border-bottom:1px solid black;\">") + \
	QLatin1String("</td></tr><tr><td colspan=\"2\"></td></tr>"))
#define tableInfoEndPdf() (QLatin1String("</table>"))

#define DATETIME_VIEW_FORMAT "d.M.yyyy H:mm:ss"
#define DATETIME_PDF_FORMAT "d.M.yyyy <XX> H:mm:ss"

static const QString csvSeparator(",");
static const QString endLine("\n");

/*!
 * @brief Date time view string.
 *
 * @param[in] dateTime Date time structure.
 * @return Date time view string.
 */
static
QString dateTimeToString(const QDateTime &dateTime)
{
	if (dateTime.isValid()) {
		return dateTime.toString(DATETIME_VIEW_FORMAT);
	}
	return QString();
}

/*!
 * @brief Date time view string for PDF.
 *
 * @param[in] dateTime Date time structure.
 * @return Date time view string for PDF.
 */
static
QString dateTimeToStringPdf(const QDateTime &dateTime)
{
	if (dateTime.isValid()) {
		return dateTime.toString(DATETIME_PDF_FORMAT).replace("<XX>",
		    Html::Export::tr("at"));
	}
	return QString();
}

/*!
 * @brief Return attachment size string.
 *
 * @param[in] dmAttachmentSize Attachment size.
 * @return Attachment size string.
 */
static
QString getSizeStr(qint64 dmAttachmentSize)
{
	QString size = QString::number(dmAttachmentSize);
	return (size == "0") ? "~1 kB" : "~" + size + " kB";
}

/*!
 * @brief Create HTML PDF header.
 *
 * @param[in] title Document title.
 * @param[in] msgId Message ID string.
 * @return HTML header.
 */
static
QString createPdfHeader(const QString &title, const QString &msgId)
{
	return "<table width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td>" +
	    QStringLiteral("<img src=:/datovka-pdf.png/>") +
	    "</td><td align=\"right\" valign=\"bottom\"><h3>" +
	    title + "<br/>" +
	    + "ID: " + msgId +
	    "</h3></td></tr></table>";
}

/*!
 * @brief Generate HTML entries from a name + value pairs.
 *
 * @param[in] pairList List of name + value pairs.
 * @return HTML text.
 */
static
QString pairListToHtml(const QList< QPair<QString, QString> > &pairList)
{
	typedef QPair<QString, QString> EntryPair;

	QString html;

	for (const EntryPair &p : pairList) {
		html += strongInfoLine(p.first, p.second);
	}

	return html;
}

/*!
 * @brief Return HTML or plain message author description.
 *
 * @param[in] jsonObj JSON object.
 * @param[in] html True if return data as html string.
 * @return HTML or plain string containing message author information.
 */
static
QString getMessageAuthor(const QJsonObject &jsonObj, bool html)
{
	if (html) {
		QString authorInfo = strongInfoLine(Html::Export::tr("User Type"),
		    Isds::Description::descrSenderType(
			Isds::str2SenderType(
			    jsonObj.value("userType").toString())));
		QString value = jsonObj.value("authorName").toString();
		if (!value.isEmpty()) {
			authorInfo += strongInfoLine(
			    Html::Export::tr("Full Name"), value);
		}
		return authorInfo;
	} else {
		QString authorInfo = Isds::Description::descrSenderType(
		    Isds::str2SenderType(
		    jsonObj.value("userType").toString()));
		QString value = jsonObj.value("authorName").toString();
		if (!value.isEmpty()) {
			authorInfo += + " " + value;
		}
		return authorInfo;
	}
}

/*!
 * @brief Return HTML or plain message author 2 description.
 *
 * @param[in] jsonVal JSON data.
 * @param[in] html True if return data as html string.
 * @return HTML or plain string containing message author 2 information.
 */
static
QString getMessageAuthor2(const QJsonValue &jsonVal, bool html)
{
	bool iOk = false;
	const Isds::DmMessageAuthor auth =
	    Isds::Json::dmMessageAuthorFromJsonVal(jsonVal, &iOk);
	if (Q_UNLIKELY(!iOk)) {
		return QString();
	}

	if (html) {
		return pairListToHtml(Isds::textListMessageAuthor(auth));
	} else {

		QString authorInfo =
		    Isds::Description::descrSenderType(auth.userType());
		QString value = auth.personName().givenNames()
		    + " " + auth.personName().lastName();
		if (!value.isEmpty()) {
			authorInfo += + " " + value;
		}
		return authorInfo;
	}
}

/*!
 * @brief Return HTML formatted message author description.
 *
 * @param[in] jsonData JSON document data.
 * @param[in] html true if return data as html string.
 * @return HTML formatted or plain string containing message author information.
 */
static
QString authorDescription(const QByteArray &jsonData, bool html)
{
	QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);

	if (jsonDoc.isEmpty() || !jsonDoc.isObject()) {
		return QString();
	}

	QJsonObject jsonObj = jsonDoc.object();

	{
		QJsonValue jsonVal = jsonObj.value("message_author");
		if (jsonVal.isObject()) {
			return getMessageAuthor(jsonVal.toObject(), html);
		}
	}

	{
		QJsonValue jsonVal = jsonObj.value("message_author2");
		if (jsonVal.isObject()) {
			return getMessageAuthor2(jsonVal, html);
		}
	}

	return QString();
}

/*!
 * @brief Create delegation string.
 *
 * @param[in] dmLegalTitleLawStr Law.
 * @param[in] dmLegalTitleYearStr Year.
 * @param[in] dmLegalTitleSect Section.
 * @param[in] dmLegalTitlePar Paragraph.
 * @param[in] dmLegalTitlePoint Letter.
 * @return Delegation string.
 */
static
QString createDelegation(const QString &dmLegalTitleLawStr,
    const QString &dmLegalTitleYearStr, const QString &dmLegalTitleSect,
    const QString &dmLegalTitlePar, const QString &dmLegalTitlePoint)
{
	QString str;
	if (!dmLegalTitleLawStr.isEmpty() && dmLegalTitleLawStr != "0") {
		str += dmLegalTitleLawStr % QStringLiteral("/");
	}
	if (!dmLegalTitleYearStr.isEmpty() && dmLegalTitleYearStr != "0") {
		str += dmLegalTitleYearStr % QStringLiteral(" Sb. ");
	}
	if (!dmLegalTitleSect.isEmpty()) {
		str += QStringLiteral("§ ") % dmLegalTitleSect;
	}
	if (!dmLegalTitlePar.isEmpty()) {
		str += QStringLiteral(" ") % Html::Export::tr("paragraph")
		    % QStringLiteral(" ") % dmLegalTitlePar;
	}
	if (!dmLegalTitlePoint.isEmpty()) {
		str += QStringLiteral(" ") % Html::Export::tr("letter")
		    % QStringLiteral(" ") % dmLegalTitlePoint;
	}
	return str;
}

/*!
 * @brief Create ref and ident string.
 *
 * @param[in] dmSenderRefNumber Sender ref string.
 * @param[in] dmSenderIdent Sender ident string.
 * @param[in] dmRecipientRefNumber Recipient ref string.
 * @param[in] dmRecipientIdent Recipient ident string.
 * @return Ref and ident string.
 */
static
QString createRefs(const QString &dmSenderRefNumber,
    const QString &dmSenderIdent, const QString &dmRecipientRefNumber,
    const QString &dmRecipientIdent)
{
	QString str;
	if (!dmSenderRefNumber.isEmpty()) {
		str += strongInfoLine(Html::Export::tr("Our Reference Number"),
		    dmSenderRefNumber);
	}
	if (!dmSenderIdent.isEmpty()) {
		str += strongInfoLine(Html::Export::tr("Our File Mark"),
		    dmSenderIdent);
	}
	if (!dmRecipientRefNumber.isEmpty()) {
		str += strongInfoLine(Html::Export::tr("Your Reference Number"),
		    dmRecipientRefNumber);
	}
	if (!dmRecipientIdent.isEmpty()) {
		str += strongInfoLine(Html::Export::tr("Your File Mark"),
		    dmRecipientIdent);
	}
	return str;
}

/*!
 * @brief Create additional section string.
 *
 * @param[in] dmLegalTitleLawStr Law.
 * @param[in] dmLegalTitleYearStr Year.
 * @param[in] dmLegalTitleSect Section.
 * @param[in] dmLegalTitlePar Paragraph.
 * @param[in] dmLegalTitlePoint Letter.
 * @param[in] dmSenderRefNumber Sender ref string.
 * @param[in] dmSenderIdent Sender ident string.
 * @param[in] dmRecipientRefNumber Recipient ref string.
 * @param[in] dmRecipientIdent Recipient ident string.
 * @param[in] dmToHands To hands name.
 * @param[in] dmPersonalDelivery Personal delivery flag.
 * @return Additional section string.
 */
static
QString createAdditional(const QString &dmLegalTitleLawStr,
    const QString &dmLegalTitleYearStr, const QString &dmLegalTitleSect,
    const QString &dmLegalTitlePar, const QString &dmLegalTitlePoint,
    const QString &dmSenderRefNumber, const QString &dmSenderIdent,
    const QString &dmRecipientRefNumber, const QString &dmRecipientIdent,
    const QString &dmToHands, Isds::Type::NilBool dmPersonalDelivery)
{
	QString str = createDelegation(dmLegalTitleLawStr,
	    dmLegalTitleYearStr, dmLegalTitleSect,
	    dmLegalTitlePar, dmLegalTitlePoint);
	if (!str.isEmpty()) {
		str = strongInfoLine(Html::Export::tr("Mandate"), str);
	}
	str += createRefs(dmSenderRefNumber, dmSenderIdent,
	    dmRecipientRefNumber, dmRecipientIdent);
	if (!dmToHands.isEmpty()) {
		str += strongInfoLine(Html::Export::tr("To Hands"),
		    dmToHands);
	}
	if (dmPersonalDelivery == Isds::Type::BOOL_TRUE) {
		str += strongInfoLine(Html::Export::tr("Personal Delivery"),
		    Html::Export::tr("yes"));
	}
	return str;
}

/*!
 * @brief Create sender or recipient string.
 *
 * @param[in] sender True if sender.
 * @param[in] name Databox name.
 * @param[in] address Address.
 * @param[in] dbId Databox ID.
 * @param[in] dbType Databox type.
 * @param[in] msgAuthorJsonStr Author JSON data.
 * @return Sender or recipient string.
 */
static
QString createSenderRecipient(bool sender, const QString &name,
    const QString &address, const QString &dbId,
    Isds::Type::DbType dbType, const QByteArray &msgAuthorJsonStr)
{
	QString str = sectionLabel(sender ? Html::Export::tr("Sender") :
	    Html::Export::tr("Recipient"));
	str += strongInfoLine(Html::Export::tr("Name"), name);
	str += strongInfoLine(Html::Export::tr("Address"), address);
	str += strongInfoLine(Html::Export::tr("Data-Box ID"), dbId);
	if (sender) {
		str += strongInfoLine(Html::Export::tr("Data-Box Type"),
		    Isds::Description::descrDbType(dbType));
		if (!msgAuthorJsonStr.isEmpty()) {
			str += "<h4>" + Html::Export::tr("Message Author") + "</h4>";
			str += authorDescription(msgAuthorJsonStr, true);
		}
	}
	return str;
}

QString Html::Export::htmlInfoPdf(Html::Export::ExportType type,
    const Isds::Envelope &envelope, const QList<Isds::Document> &attachments,
    const QByteArray &msgAuthorJsonStr)
{
	QString html;

	/* Header */
	if (type == TYPE_DELIVERY_INFO) {
		if (envelope.dmAcceptanceTime().isNull()) {
			html += createPdfHeader(tr("Delivery Info"),
			    envelope.dmID());
		} else {
			html += createPdfHeader(tr("Acceptance Info"),
			    envelope.dmID());
		}
	} else {
		html += createPdfHeader(tr("Message Envelope"),
		    envelope.dmID());
	}

	html += tableInfoStartPdf();
	html += lineSeparatorPdf();
	if (type == TYPE_DELIVERY_INFO) {
		if (envelope.dmAcceptanceTime().isNull()) {
			html += textLabelPdf(tr("Message Delivery Info"));
		} else {
			html += textLabelPdf(tr("Message Acceptance Info"));
		}
	} else {
		html += textLabelPdf(tr("Message Envelope"));
	}

	/* General information section */
	html += messageTableInfoPdf(tr("Subject"), envelope.dmAnnotation());
	html += messageTableInfoPdf(tr("Message ID"), envelope.dmID());
	if (!envelope.dmType().isNull()) {
		html += messageTableInfoPdf(tr("Message Type"),
		    Isds::Description::descrDmTypeChar(envelope.dmType()));
	}
	if (type == TYPE_DELIVERY_INFO) {
		html += messageTableInfoPdf(tr("Message State"),
		    QString::number(envelope.dmMessageStatus()) + " -- " +
		    Isds::Description::descrDmState(envelope.dmMessageStatus()));
		if (!envelope.dmAcceptanceTime().isNull()) {
			html += messageTableInfoPdf(tr("Acceptance Time"),
			    dateTimeToStringPdf(envelope.dmAcceptanceTime()));
		} else {
			html += messageTableInfoPdf(tr("Delivery Time"),
			    dateTimeToStringPdf(envelope.dmDeliveryTime()));
		}
	} else {
		html += messageTableInfoPdf(tr("Attachment Size"),
		    getSizeStr(envelope.dmAttachmentSize()));
	}

	html += lineSeparatorPdf();

	/* Sender info section */
	html += messageTableInfoPdf(tr("Sender"),
	    envelope.dmSender()
	    + QStringLiteral(", ")
	    + envelope.dmSenderAddress());
	html += messageTableInfoPdf(tr("Data-Box ID"),
	    envelope.dbIDSender());
	html += messageTableInfoPdf(tr("Data-Box Type"),
	    Isds::Description::descrDbType(envelope.dmSenderType()));
	if (!msgAuthorJsonStr.isEmpty()) {
		html += messageTableInfoPdf(tr("Message Author"),
		    authorDescription(msgAuthorJsonStr, false));
	}

	html += lineSeparatorPdf();

	/* Recipient info section */
	html += messageTableInfoPdf(tr("Recipient"),
	    envelope.dmRecipient()
	    + QStringLiteral(", ")
	    + envelope.dmRecipientAddress());
	html += messageTableInfoPdf(tr("Data-Box ID"),
	    envelope.dbIDRecipient());
	html += messageTableInfoPdf(tr("Data-Box Type"), QString());

	html += lineSeparatorPdf();

	/* Additional info section */
	QString tmp = createDelegation(envelope.dmLegalTitleLawStr(),
	    envelope.dmLegalTitleYearStr(), envelope.dmLegalTitleSect(),
	    envelope.dmLegalTitlePar(), envelope.dmLegalTitlePoint());
	if (tmp.isEmpty()) {
		tmp = tr("not specified");
	}
	html += messageTableInfoPdf(tr("Mandate"), tmp);
	tmp = tr("not specified");
	html += messageTableInfoPdf(tr("Our Reference Number"),
	    envelope.dmSenderRefNumber().isEmpty() ? tmp :
	    envelope.dmSenderRefNumber());
	html += messageTableInfoPdf(tr("Our File Mark"),
	    envelope.dmSenderIdent().isEmpty() ? tmp :
	    envelope.dmSenderIdent());
	html += messageTableInfoPdf(tr("Your Reference Number"),
	    envelope.dmRecipientRefNumber().isEmpty() ? tmp :
	    envelope.dmRecipientRefNumber());
	html += messageTableInfoPdf(tr("Your File Mark"),
	    envelope.dmRecipientIdent().isEmpty() ? tmp :
	    envelope.dmRecipientIdent());
	html += messageTableInfoPdf(tr("To Hands"),
	    envelope.dmToHands().isEmpty() ? tmp : envelope.dmToHands());
	html += messageTableInfoPdf(tr("Personal Delivery"),
	    (envelope.dmPersonalDelivery() == Isds::Type::BOOL_TRUE) ?
	        tr("yes") : tr("no"));
	html += messageTableInfoPdf(tr("Prohibited Acceptance through Fiction"),
	    (envelope.dmAllowSubstDelivery() != Isds::Type::BOOL_TRUE) ?
	        tr("yes") : tr("no"));

	/* Events info section */
	if (type == TYPE_DELIVERY_INFO) {
		html += lineSeparatorPdf();
		html += textLabelPdf(tr("Message Events") + QStringLiteral(":"));
		foreach (const Isds::Event &event, envelope.dmEvents()) {
			html += messageTableInfoPdf(
			    dateTimeToStringPdf(event.time()),
			    event.descr());
		}
	}

	/* Delivery info section */
	if (type == TYPE_ENVELOPE_INFO) {
		html += lineSeparatorPdf();
		html += messageTableInfoPdf(tr("Delivery Time"),
		    dateTimeToStringPdf(envelope.dmDeliveryTime()));
		html += messageTableInfoPdf(tr("Acceptance Time"),
		    dateTimeToStringPdf(envelope.dmAcceptanceTime()));
		html += messageTableInfoPdf(tr("Message State"),
		    QString::number(envelope.dmMessageStatus()) + " -- " +
		    Isds::Description::descrDmState(envelope.dmMessageStatus()));
	}

	/* Attachments info section */
	if (type == TYPE_ENVELOPE_INFO) {
		html += lineSeparatorPdf();
		html += textLabelPdf(tr("Message Attachments") + QStringLiteral(":"));
		int i = 0;
		foreach (const Isds::Document &doc, attachments) {
			i++;
			html += messageTableInfoPdf(QString::number(i),
			    doc.fileDescr());
		}
	}

	html += lineSeparatorPdf();
	html += tableInfoEndPdf();

	return html;
}

QString Html::Export::htmlDeliveryInfoApp(const Isds::Envelope &envelope,
    const QByteArray &msgAuthorJsonStr)
{
	QString html(divStart);

	/* General info section */
	html += sectionLabel(tr("General"));
	html += strongInfoLine(tr("Message ID"), envelope.dmID());
	html += strongInfoLine(tr("Subject"), envelope.dmAnnotation());
	if (!envelope.dmType().isNull()) {
		html += strongInfoLine(tr("Message Type"),
		    Isds::Description::descrDmTypeChar(envelope.dmType()));
	}
	html += strongInfoLine(tr("Message State"),
	    QString::number(envelope.dmMessageStatus()) + " -- " +
	    Isds::Description::descrDmState(envelope.dmMessageStatus()));
	if (!envelope.dmAcceptanceTime().isNull()) {
		html += strongInfoLine(tr("Acceptance Time"),
		    dateTimeToString(envelope.dmAcceptanceTime()));
	} else {
		html += strongInfoLine(tr("Delivery Time"),
		    dateTimeToString(envelope.dmDeliveryTime()));
	}

	/* Sender info section */
	html += createSenderRecipient(true, envelope.dmSender(),
	envelope.dmSenderAddress(),envelope.dbIDSender(),
	envelope.dmSenderType(), msgAuthorJsonStr);

	/* Recipient info section */
	html += createSenderRecipient(false, envelope.dmRecipient(),
	envelope.dmRecipientAddress(),envelope.dbIDRecipient(),
	envelope.dmSenderType(), msgAuthorJsonStr);

	/* Additional info section */
	QString tmp = createAdditional(envelope.dmLegalTitleLawStr(),
	    envelope.dmLegalTitleYearStr(), envelope.dmLegalTitleSect(),
	    envelope.dmLegalTitlePar(), envelope.dmLegalTitlePoint(),
	    envelope.dmSenderRefNumber(), envelope.dmSenderIdent(),
	    envelope.dmRecipientRefNumber(), envelope.dmRecipientIdent(),
	    envelope.dmToHands(), envelope.dmPersonalDelivery());
	if (!tmp.isEmpty()) {
		html += sectionLabel(tr("Additional"));
		html += tmp;
	}

	/* Events info section */
	html += sectionLabel(tr("Events"));
	foreach (const Isds::Event &event, envelope.dmEvents()) {
		html += strongInfoLine(
		    dateTimeToString(event.time()),
		    event.descr());
	}

	html += divEnd;

	return html;
}

QString Html::Export::htmlMessageInfoApp(const Isds::Envelope &envelope,
    const QByteArray &msgAuthorJsonStr, const QStringList &rmPathList,
    bool isReceived, bool shortVersion, bool appendEvents)
{
	QString html(divStart);

	/* General info section */
	html += sectionLabel(tr("General"));
	html += strongInfoLine(tr("Message ID"), envelope.dmID());
	html += strongInfoLine(tr("Subject"), envelope.dmAnnotation());
	html += strongInfoLine(tr("Attachment Size"),
	    getSizeStr(envelope.dmAttachmentSize()));
	if (!envelope.dmType().isNull()) {
		html += strongInfoLine(tr("Message Type"),
		    Isds::Description::descrDmTypeChar(envelope.dmType()));
	}
	html += strongInfoLine(tr("Prohibited Acceptance through Fiction"),
	    (envelope.dmAllowSubstDelivery() != Isds::Type::BOOL_TRUE) ?
	        tr("yes") : tr("no"));

	/* Sender & recipient info section */
	if (shortVersion) {
		if (isReceived) {
			/* Sender info section */
			html += createSenderRecipient(true, envelope.dmSender(),
			envelope.dmSenderAddress(),envelope.dbIDSender(),
			envelope.dmSenderType(), msgAuthorJsonStr);
		} else {
			/* Recipient info section */
			html += createSenderRecipient(false, envelope.dmRecipient(),
			envelope.dmRecipientAddress(),envelope.dbIDRecipient(),
			envelope.dmSenderType(), msgAuthorJsonStr);
		}
	} else {
		/* Sender info section */
		html += createSenderRecipient(true, envelope.dmSender(),
		envelope.dmSenderAddress(),envelope.dbIDSender(),
		envelope.dmSenderType(), msgAuthorJsonStr);
		/* Recipient info section */
		html += createSenderRecipient(false, envelope.dmRecipient(),
		envelope.dmRecipientAddress(),envelope.dbIDRecipient(),
		envelope.dmSenderType(), msgAuthorJsonStr);
	}

	/* Additional info section */
	QString tmp = createAdditional(envelope.dmLegalTitleLawStr(),
	    envelope.dmLegalTitleYearStr(), envelope.dmLegalTitleSect(),
	    envelope.dmLegalTitlePar(), envelope.dmLegalTitlePoint(),
	    envelope.dmSenderRefNumber(), envelope.dmSenderIdent(),
	    envelope.dmRecipientRefNumber(), envelope.dmRecipientIdent(),
	    envelope.dmToHands(), envelope.dmPersonalDelivery());
	if (!tmp.isEmpty()) {
		html += sectionLabel(tr("Additional"));
		html += tmp;
	}

	/* Delivery info section */
	html += sectionLabel(tr("Message State"));
	if (shortVersion) {
		if (!envelope.dmAcceptanceTime().isNull()) {
			html += strongInfoLine(tr("Acceptance Time"),
			    dateTimeToString(envelope.dmAcceptanceTime()));
		} else {
			html += strongInfoLine(tr("Delivery Time"),
			    dateTimeToString(envelope.dmDeliveryTime()));
		}
	} else {
		html += strongInfoLine(tr("Delivery Time"),
		    dateTimeToString(envelope.dmDeliveryTime()));
		html += strongInfoLine(tr("Acceptance Time"),
		    dateTimeToString(envelope.dmAcceptanceTime()));
	}
	html += strongInfoLine(tr("Message State"),
	    QString::number(envelope.dmMessageStatus()) + " -- " +
	    Isds::Description::descrDmState(envelope.dmMessageStatus()));

	/* Events info section */
	if (appendEvents && !envelope.dmEvents().isEmpty()) {
		html += sectionLabel(tr("Events"));
		foreach (const Isds::Event &event, envelope.dmEvents()) {
			html += strongInfoLine(
			    dateTimeToString(event.time()),
			    event.descr());
		}
	}

	/* Records management info section */
	if (!shortVersion && !rmPathList.isEmpty()) {
		html += sectionLabel(tr("Records Management Location"));
		foreach (const QString &location, rmPathList) {
			html += strongInfoLine(tr("Location"), location);
		}
	}

	html += divEnd;

	return html;
}

QString Html::Export::htmlShortMessageInfoApp(const Isds::Envelope &envelope,
    bool isReceived)
{
	QString html(divStart);

	/* Short envelope info */
	html += sectionLabel(tr("General"));
	html += strongInfoLine(tr("Message ID"), envelope.dmID());
	html += strongInfoLine(tr("Subject"), envelope.dmAnnotation());
	html += strongInfoLine((isReceived) ? tr("Sender") : tr("Recipient"),
	    isReceived ? envelope.dmSender() + " (" + envelope.dbIDSender() + ")" :
	    envelope.dmRecipient() + " (" + envelope.dbIDRecipient() + ")");
	html += strongInfoLine(tr("Acceptance Time"),
	    dateTimeToString(envelope.dmAcceptanceTime()));
	html += strongInfoLine(tr("Attachment Size"),
	    getSizeStr(envelope.dmAttachmentSize()));
	html += createRefs(envelope.dmSenderRefNumber(),
	    envelope.dmSenderIdent(), envelope.dmRecipientRefNumber(),
	    envelope.dmRecipientIdent());

	html += divEnd;

	return html;
}

QString Html::Export::htmlDataboxOwnerInfoApp(
    const Isds::DbOwnerInfoExt2 &dbOwnerInfo)
{
	QString html(divStart);

	/* Data-box info. */
	html += sectionLabel(tr("Data-Box Info"));
	html.append(strongInfoLine(tr("Data-Box ID"), dbOwnerInfo.dbID()));
	html.append(strongInfoLine(tr("Data-Box Type"),
	    Isds::dbType2Str(dbOwnerInfo.dbType())));
	html.append(strongInfoLine(tr("Open Addressing"),
	    (dbOwnerInfo.dbOpenAddressing() == Isds::Type::BOOL_TRUE)
	        ? tr("Yes") : tr("No")));
	html.append(strongInfoLine(tr("Data-Box State"),
	    Isds::Description::descrDbState(dbOwnerInfo.dbState())));
	if (!dbOwnerInfo.dbUpperID().isEmpty()) {
		html.append(strongInfoLine(tr("Upper Data-Box ID"),
		    dbOwnerInfo.dbUpperID()));
	}
	if (!dbOwnerInfo.dbIdOVM().isEmpty()) {
		html.append(strongInfoLine(tr("OVM ID"),
		    dbOwnerInfo.dbIdOVM()));
	}
	if (dbOwnerInfo.aifoIsds() != Isds::Type::BOOL_NULL) {
		html.append(strongInfoLine(tr("ROB Identification"),
		    (dbOwnerInfo.aifoIsds() == Isds::Type::BOOL_TRUE)
		    ? tr("identified") : tr("not identified")));
	}

	/* Owner info. */
	html += sectionLabel(tr("Data-Box Owner Info"));
	if (!dbOwnerInfo.firmName().isEmpty()) {
		html.append(strongInfoLine(tr("Company Name"),
		    dbOwnerInfo.firmName()));
	}
	if (!dbOwnerInfo.ic().isEmpty()) {
		html.append(strongInfoLine(tr("IČ"), dbOwnerInfo.ic()));
	}
	if (!dbOwnerInfo.personName().givenNames().isEmpty()) {
		html.append(strongInfoLine(tr("Given Names"),
		    dbOwnerInfo.personName().givenNames()));
	}
	if (!dbOwnerInfo.personName().lastName().isEmpty()) {
		html.append(strongInfoLine(tr("Last Name"),
		    dbOwnerInfo.personName().lastName()));
	}

	/* Owner birth info. */
	if (!dbOwnerInfo.birthInfo().date().toString().isEmpty()) {
		html.append(strongInfoLine(tr("Date of Birth"),
		    dbOwnerInfo.birthInfo().date().toString("d.M.yyyy")));
	}
	if (!dbOwnerInfo.birthInfo().city().isEmpty()) {
		html.append(strongInfoLine(tr("City of Birth"),
		    dbOwnerInfo.birthInfo().city()));
	}
	if (!dbOwnerInfo.birthInfo().county().isEmpty()) {
		html.append(strongInfoLine(tr("County of Birth"),
		    dbOwnerInfo.birthInfo().county()));
	}
	if (!dbOwnerInfo.birthInfo().state().isEmpty()) {
		html.append(strongInfoLine(tr("State of Birth"),
		    dbOwnerInfo.birthInfo().state()));
	}

	/* Owner address. */
	if (!dbOwnerInfo.address().street().isEmpty()) {
		html.append(strongInfoLine(tr("Street of Residence"),
		    dbOwnerInfo.address().street()));
	}
	if (!dbOwnerInfo.address().numberInStreet().isEmpty()) {
		html.append(strongInfoLine(tr("Number in Street"),
		    dbOwnerInfo.address().numberInStreet()));
	}
	if (!dbOwnerInfo.address().numberInMunicipality().isEmpty()) {
		html.append(strongInfoLine(tr("Number in Municipality"),
		    dbOwnerInfo.address().numberInMunicipality()));
	}
	if (!dbOwnerInfo.address().zipCode().isEmpty()) {
		html.append(strongInfoLine(tr("Zip Code"),
		    dbOwnerInfo.address().zipCode()));
	}
	if (!dbOwnerInfo.address().district().isEmpty()) {
		html.append(strongInfoLine(tr("District of Residence"),
		    dbOwnerInfo.address().district()));
	}
	if (!dbOwnerInfo.address().city().isEmpty()) {
		html.append(strongInfoLine(tr("City of Residence"),
		    dbOwnerInfo.address().city()));
	}
	if (!dbOwnerInfo.address().state().isEmpty()) {
		html.append(strongInfoLine(tr("State of Residence"),
		    dbOwnerInfo.address().state()));
	}
	if (!dbOwnerInfo.nationality().isEmpty()) {
		html.append(strongInfoLine(tr("Nationality"),
		    dbOwnerInfo.nationality()));
	}
	if (!dbOwnerInfo.address().code().isEmpty()) {
		html.append(strongInfoLine(tr("RUIAN Address Code"),
		    dbOwnerInfo.address().code()));
	}

	html.append(divEnd);

	return html;
}

QString Html::Export::htmlDataboxUserInfoApp(
    const Isds::DbUserInfoExt2 &dbUserInfo)
{
	QString html(divStart);

	/* User info. */
	html += sectionLabel(tr("User Info"));
	if (!dbUserInfo.firmName().isEmpty()) {
		html.append(strongInfoLine(tr("Company Name"),
		    dbUserInfo.firmName()));
	}
	if (!dbUserInfo.ic().isEmpty()) {
		html.append(strongInfoLine(tr("IČ"), dbUserInfo.ic()));
	}
	if (!dbUserInfo.personName().givenNames().isEmpty()) {
		html.append(strongInfoLine(tr("Given Names"),
		    dbUserInfo.personName().givenNames()));
	}
	if (!dbUserInfo.personName().lastName().isEmpty()) {
		html.append(strongInfoLine(tr("Last Name"),
		    dbUserInfo.personName().lastName()));
	}
	if (!dbUserInfo.biDate().isNull()) {
		html.append(strongInfoLine(tr("Date of Birth"),
		    dbUserInfo.biDate().toString("d.M.yyyy")));
	}

	/* User address. */
	if (!dbUserInfo.address().street().isEmpty()) {
		html.append(strongInfoLine(tr("Street"),
		    dbUserInfo.address().street()));
	}
	if (!dbUserInfo.address().numberInStreet().isEmpty()) {
		html.append(strongInfoLine(tr("Number in Street"),
		    dbUserInfo.address().numberInStreet()));
	}
	if (!dbUserInfo.address().numberInMunicipality().isEmpty()) {
		html.append(strongInfoLine(tr("Number in Municipality"),
		    dbUserInfo.address().numberInMunicipality()));
	}
	if (!dbUserInfo.address().district().isEmpty()) {
		html.append(strongInfoLine(tr("District"),
		    dbUserInfo.address().district()));
	}
	if (!dbUserInfo.address().city().isEmpty()) {
		html.append(strongInfoLine(tr("City"),
		    dbUserInfo.address().city()));
	}
	if (!dbUserInfo.address().zipCode().isEmpty()) {
		html.append(strongInfoLine(tr("Zip Code"),
		    dbUserInfo.address().zipCode()));
	}
	if (!dbUserInfo.address().state().isEmpty()) {
		html.append(strongInfoLine(tr("State"),
		    dbUserInfo.address().state()));
	}
	if (!dbUserInfo.address().code().isEmpty()) {
		html.append(strongInfoLine(tr("RUIAN Address Code"),
		    dbUserInfo.address().code()));
	}

	/* User contact address. */
	if (!dbUserInfo.caStreet().isEmpty()) {
		html.append(strongInfoLine(tr("Street of Residence"),
		    dbUserInfo.caStreet()));
	}
	if (!dbUserInfo.caCity().isEmpty()) {
		html.append(strongInfoLine(tr("City of Residence"),
		    dbUserInfo.caCity()));
	}
	if (!dbUserInfo.caZipCode().isEmpty()) {
		html.append(strongInfoLine(tr("Zip Code"),
		    dbUserInfo.caZipCode()));
	}
	if (!dbUserInfo.caState().isEmpty()) {
		html.append(strongInfoLine(tr("State of Residence"),
		    dbUserInfo.caState()));
	}

	/* User other info. */
	html.append(strongInfoLine(tr("User Type"),
	    Isds::Description::descrUserType(dbUserInfo.userType())));
	html.append(strongInfoLine(tr("Permissions"),
	    Isds::Description::htmlDescrPrivileges(dbUserInfo.userPrivils())));
	if (!dbUserInfo.isdsID().isEmpty()) {
		html.append(strongInfoLine(tr("Unique User ISDS ID"),
		    dbUserInfo.isdsID()));
	}
	if (dbUserInfo.aifoIsds() != Isds::Type::BOOL_NULL) {
		html.append(strongInfoLine(tr("ROB Identification"),
		    (dbUserInfo.aifoIsds() == Isds::Type::BOOL_TRUE)
		    ? tr("identified") : tr("not identified")));
	}

	html.append(divEnd);

	return html;
}

QString Html::Export::htmlPwdExpirDateInfoApp(const QString &dateTime)
{
	if (dateTime.isEmpty()) {
		return QString();
	}
	return strongInfoLine(tr("Password Expiration"), dateTime);
}

QString Html::Export::htmlMsgDbLocationInfoApp(const QStringList &dbFiles,
    bool fileUrls)
{
	QString html(divStart);
	html.append(sectionLabel(tr("Local Database Location")));
	if (dbFiles.isEmpty()) {
		html.append(textInfoLine(
		    tr("Data box has no own message database associated.")));
	}
	for (const QString &file : dbFiles) {
		html.append(strongInfoLine(tr("local database file"),
		    fileUrls
		        ? QString("<a href=\"%1\" style=\"color:#0086fb; text-decoration:underline;\">%2</a> ")
		            .arg(QUrl::fromLocalFile(QFileInfo(file).absoluteFilePath()).toString())
		            .arg(QDir::toNativeSeparators(file))
		        : QDir::toNativeSeparators(file)));
	}
	html.append(divEnd);
	return html;
}

QString Html::Export::htmlMessageInfoApp(const QString &title,
    const QList< QPair<QString, int> > &msgCnts)
{
	QString html(divStart);
	html.append(sectionLabel(title));
	for (int i = 0; i < msgCnts.size(); ++i) {
		html.append(strongInfoLine(msgCnts[i].first,
		    QString::number(msgCnts[i].second)));
	}
	html.append(divEnd);
	return html;
}

QString Html::Export::htmlAllMessagesInfoApp(
    const QList< QPair<QString, int> > &recMsgCnts,
    const QList< QPair<QString, int> > &sentMsgCnts)
{
	QString html(divStart);
	html.append(sectionLabel(tr("All Messages")));
	html.append(htmlMessageInfoApp(tr("Received"), recMsgCnts));
	html.append(htmlMessageInfoApp(tr("Sent"), sentMsgCnts));
	html.append(divEnd);
	return html;
}

QString Html::Export::htmlSignatureInfoApp(const QString &sigResStr,
    const QString &certResStr, const QString &tsResStr)
{
	QString html(divStart);
	html.append(sectionLabel(tr("Signature")));
	if (!sigResStr.isEmpty()) {
		html.append(strongInfoLine(tr("Message Signature"), sigResStr));
	}
	if (!certResStr.isEmpty()) {
		html.append(strongInfoLine(tr("Signing Certificate"), certResStr));
	}
	if (!tsResStr.isEmpty()) {
		html.append(strongInfoLine(tr("Time Stamp"), tsResStr));
	}
	html.append(divEnd);
	return html;
}

QString Html::Overview::createCsvHeader(void)
{
	return QStringLiteral("ID") % csvSeparator %
	    tr("Status") % csvSeparator %
	    tr("Message Type") % csvSeparator %
	    tr("Delivery Time") % csvSeparator %
	    tr("Acceptance Time") % csvSeparator %
	    tr("Subject") % csvSeparator %
	    tr("Sender") % csvSeparator %
	    tr("Sender Address") % csvSeparator %
	    tr("Recipient") % csvSeparator %
	    tr("Recipient Address") % csvSeparator %
	    tr("Sender File Mark") % csvSeparator %
	    tr("Sender Reference Number") % csvSeparator %
	    tr("Recipient File Mark") % csvSeparator %
	    tr("Recipient Reference Number") % csvSeparator %
	    tr("Sender Data-Box ID") % csvSeparator %
	    tr("Recipient Data-Box ID") %
	    endLine;
}

QString Html::Overview::createCsvEntry(const QString &msgId,
    const QStringList &items)
{
	QString line(msgId);
	for (const QString &item : items) {
		line += csvSeparator % item;
	}
	return line % endLine;
}

#define tabLine(key, value) \
	(QLatin1String("<tr><td>") % (key).toHtmlEscaped() % \
	QLatin1String("</td><td>") % \
	(value).toHtmlEscaped() % QLatin1String("</td></tr>"))
#define tabLineSmaller(key, value) \
	(QLatin1String("<tr><td>") % (key).toHtmlEscaped() % \
	QLatin1String("</td><td class=\"smaller\">") % \
	(value).toHtmlEscaped() % QLatin1String("</td></tr>"))
#define tabLineBold(key, value) \
	(QLatin1String("<tr><td>") % (key).toHtmlEscaped() % \
	QLatin1String("</td><td><b>") % \
	(value).toHtmlEscaped() % QLatin1String("</b></td></tr>"))
#define tabLineItalic(key, value) \
	(QLatin1String("<tr><td>") % (key).toHtmlEscaped() % \
	QLatin1String("</td><td><i>") % \
	(value).toHtmlEscaped() % QLatin1String("</i></td></tr>"))
#define tabLineItalicBold(key, value) \
	(QLatin1String("<tr><td>") % (key).toHtmlEscaped() % \
	QLatin1String("</td><td><i><b>") % \
	(value).toHtmlEscaped() % QLatin1String("</b></i></td></tr>"))

QString Html::Overview::createHtmlEntry(const QString &msgId,
    const QStringList &items)
{
	return QStringLiteral("<div><table><tr><td><table>") %
	    tabLineBold(QStringLiteral("Id:"), msgId) %
	    tabLineSmaller(tr("Delivery"), items.at(3)) %
	    tabLineSmaller(tr("Acceptance"), items.at(4)) %
	    QStringLiteral("</table></td><td><table>") %
	    tabLineItalicBold(tr("Subject:"), items.at(2)) %
	    tabLineItalic(tr("Sender:"), items.at(0) + " (" % items.at(5) % ")") %
	    tabLineItalic(tr("Recipient:"), items.at(1) + " (" % items.at(6) % ")") %
	    QStringLiteral("</table></td></tr></table></div>");
}

QString Html::Overview::createHtmlSubtitle(const QString &title)
{
	return QStringLiteral("<h2>") % title % QStringLiteral("</h2>") % endLine;
}

QString Html::Overview::createHtmlFooter(void)
{
	return QStringLiteral("</body>\n</html>");
}

QString Html::Overview::createHtmlHeader(const QDate &fromDate,
    const QDate &toDate)
{
	return QStringLiteral("<!DOCTYPE html\n"
	    "   PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
	    "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
	    "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
	    "<head>\n"
	    "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
	    "<title>") %
	    tr("Correspondence Overview") %
	    QStringLiteral("</title>\n"
	    "<style type=\"text/css\">\n"
	    "   td {padding: 0px 5px; }\n"
	    "   div { border-bottom: solid 1px black;}\n"
	    "   body { font-family:Arial, sans; font-size: 12pt;}\n"
	    "   *.smaller { font-size: smaller; }\n"
	    "</style>\n"
	    "</head>\n"
	    "<body>\n"
	    "<h1>") %
	    tr("Correspondence Overview") %
	    QStringLiteral("</h1>\n"
	    "<table>\n") %
	    tabLine(tr("From date:"), fromDate.toString(Utility::dateDisplayFormat)) %
	    tabLine(tr("To date:"), toDate.toString(Utility::dateDisplayFormat)) %
	    tabLine(tr("Generated:"), QDateTime().currentDateTime().toString(
	        Utility::dateTimeDisplayFormat)) %
	    QStringLiteral("\n</table>") %
	    endLine;
}
