/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the Collabora Office project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 */

#include <sal/config.h>

#include <string_view>

#include <pdf/XmpMetadata.hxx>
#include <tools/XmlWriter.hxx>

namespace vcl::pdf
{
namespace
{
constexpr const char* constPadding = "                                        "
                                     "                                        "
                                     "                                        "
                                     "                                        "
                                     "                                        "
                                     "\n";
}

XmpMetadata::XmpMetadata() = default;

void XmpMetadata::write()
{
    mpMemoryStream = std::make_unique<SvMemoryStream>(4096 /*Initial*/, 64 /*Resize*/);

    // Header
    mpMemoryStream->WriteOString(std::string_view(reinterpret_cast<char const*>(
        u8"<?xpacket begin=\"\uFEFF\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n")));

    {
        tools::XmlWriter aXmlWriter(mpMemoryStream.get());
        aXmlWriter.startDocument(2, false);
        aXmlWriter.startElement("x"_ostr, "xmpmeta"_ostr, "adobe:ns:meta/"_ostr);
        aXmlWriter.startElement("rdf"_ostr, "RDF"_ostr,
                                "http://www.w3.org/1999/02/22-rdf-syntax-ns#"_ostr);

        // PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
        if (mnPDF_A > 0)
        {
            OString sPdfVersion = OString::number(mnPDF_A);

            aXmlWriter.startElement("rdf:Description");
            aXmlWriter.attribute("rdf:about", "");
            aXmlWriter.attribute("xmlns:pdfaid", "http://www.aiim.org/pdfa/ns/id/");

            aXmlWriter.startElement("pdfaid:part");
            aXmlWriter.content(sPdfVersion);
            aXmlWriter.endElement();

            if (mnPDF_A == 4)
            {
                aXmlWriter.startElement("pdfaid:rev");
                aXmlWriter.content("2020");
                aXmlWriter.endElement();
            }

            if (!msConformance.isEmpty())
            {
                aXmlWriter.startElement("pdfaid:conformance");
                aXmlWriter.content(msConformance);
                aXmlWriter.endElement();
            }
            aXmlWriter.endElement();
        }

        // Dublin Core properties
        if (!msTitle.isEmpty() || !msAuthor.isEmpty() || !msSubject.isEmpty()
            || !maContributor.empty() || !msCoverage.isEmpty() || !msIdentifier.isEmpty()
            || !maPublisher.empty() || !maRelation.empty() || !msRights.isEmpty()
            || !msSource.isEmpty() || !msType.isEmpty())
        {
            aXmlWriter.startElement("rdf:Description");
            aXmlWriter.attribute("rdf:about", "");
            aXmlWriter.attribute("xmlns:dc", "http://purl.org/dc/elements/1.1/");

            aXmlWriter.startElement("dc:format");
            aXmlWriter.content("application/pdf");
            aXmlWriter.endElement();

            aXmlWriter.startElement("dc:date");
            aXmlWriter.startElement("rdf:Seq");
            aXmlWriter.startElement("rdf:li");
            aXmlWriter.content(m_sCreateDate);
            aXmlWriter.endElement();
            aXmlWriter.endElement();
            aXmlWriter.endElement();

            if (!msTitle.isEmpty())
            {
                // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
                aXmlWriter.startElement("dc:title");
                aXmlWriter.startElement("rdf:Alt");
                aXmlWriter.startElement("rdf:li");
                aXmlWriter.attribute("xml:lang", "x-default");
                aXmlWriter.content(msTitle);
                aXmlWriter.endElement();
                aXmlWriter.endElement();
                aXmlWriter.endElement();
            }
            if (!msAuthor.isEmpty())
            {
                aXmlWriter.startElement("dc:creator");
                aXmlWriter.startElement("rdf:Seq");
                aXmlWriter.startElement("rdf:li");
                aXmlWriter.content(msAuthor);
                aXmlWriter.endElement();
                aXmlWriter.endElement();
                aXmlWriter.endElement();
            }
            if (!msSubject.isEmpty())
            {
                aXmlWriter.startElement("dc:description");
                aXmlWriter.startElement("rdf:Alt");
                aXmlWriter.startElement("rdf:li");
                aXmlWriter.attribute("xml:lang", "x-default");
                aXmlWriter.content(msSubject);
                aXmlWriter.endElement();
                aXmlWriter.endElement();
                aXmlWriter.endElement();
            }
            if (!maContributor.empty())
            {
                aXmlWriter.startElement("dc:contributor");
                aXmlWriter.startElement("rdf:Bag");
                for (const OString& rContributor : maContributor)
                {
                    aXmlWriter.startElement("rdf:li");
                    aXmlWriter.content(rContributor);
                    aXmlWriter.endElement();
                }
                aXmlWriter.endElement();
                aXmlWriter.endElement();
            }
            if (!msCoverage.isEmpty())
            {
                aXmlWriter.startElement("dc:coverage");
                aXmlWriter.content(msCoverage);
                aXmlWriter.endElement();
            }
            if (!msIdentifier.isEmpty())
            {
                aXmlWriter.startElement("dc:identifier");
                aXmlWriter.content(msIdentifier);
                aXmlWriter.endElement();
            }
            if (!maPublisher.empty())
            {
                aXmlWriter.startElement("dc:publisher");
                aXmlWriter.startElement("rdf:Bag");
                for (const OString& rPublisher : maPublisher)
                {
                    aXmlWriter.startElement("rdf:li");
                    aXmlWriter.content(rPublisher);
                    aXmlWriter.endElement();
                }
                aXmlWriter.endElement();
                aXmlWriter.endElement();
            }
            if (!maRelation.empty())
            {
                aXmlWriter.startElement("dc:relation");
                aXmlWriter.startElement("rdf:Bag");
                for (const OString& rRelation : maRelation)
                {
                    aXmlWriter.startElement("rdf:li");
                    aXmlWriter.content(rRelation);
                    aXmlWriter.endElement();
                }
                aXmlWriter.endElement();
                aXmlWriter.endElement();
            }
            if (!msRights.isEmpty())
            {
                aXmlWriter.startElement("dc:rights");
                aXmlWriter.startElement("rdf:Alt");
                aXmlWriter.startElement("rdf:li");
                aXmlWriter.attribute("xml:lang", "x-default");
                aXmlWriter.content(msRights);
                aXmlWriter.endElement();
                aXmlWriter.endElement();
                aXmlWriter.endElement();
            }
            if (!msSource.isEmpty())
            {
                aXmlWriter.startElement("dc:source");
                aXmlWriter.content(msSource);
                aXmlWriter.endElement();
            }
            if (!msType.isEmpty())
            {
                aXmlWriter.startElement("dc:type");
                aXmlWriter.content(msType);
                aXmlWriter.endElement();
            }
            aXmlWriter.endElement();
        }

        // PDF/UA
        if (mnPDF_UA > 0)
        {
            if (mnPDF_A != 0)
            { // tdf#157517 PDF/A extension schema is required
                aXmlWriter.startElement("rdf:Description");
                aXmlWriter.attribute("rdf:about", "");
                aXmlWriter.attribute("xmlns:pdfaExtension",
                                     "http://www.aiim.org/pdfa/ns/extension/");
                aXmlWriter.attribute("xmlns:pdfaSchema", "http://www.aiim.org/pdfa/ns/schema#");
                aXmlWriter.attribute("xmlns:pdfaProperty", "http://www.aiim.org/pdfa/ns/property#");
                aXmlWriter.startElement("pdfaExtension:schemas");
                aXmlWriter.startElement("rdf:Bag");
                aXmlWriter.startElement("rdf:li");
                aXmlWriter.attribute("rdf:parseType", "Resource");
                aXmlWriter.startElement("pdfaSchema:namespaceURI");
                aXmlWriter.content("http://www.aiim.org/pdfua/ns/id/");
                aXmlWriter.endElement();
                aXmlWriter.startElement("pdfaSchema:prefix");
                aXmlWriter.content("pdfuaid");
                aXmlWriter.endElement();
                aXmlWriter.startElement("pdfaSchema:schema");
                aXmlWriter.content("PDF/UA identification schema");
                aXmlWriter.endElement();
                aXmlWriter.startElement("pdfaSchema:property");
                aXmlWriter.startElement("rdf:Seq");

                aXmlWriter.startElement("rdf:li");
                aXmlWriter.attribute("rdf:parseType", "Resource");
                aXmlWriter.startElement("pdfaProperty:category");
                aXmlWriter.content("internal");
                aXmlWriter.endElement();
                aXmlWriter.startElement("pdfaProperty:description");
                aXmlWriter.content("PDF/UA version identifier");
                aXmlWriter.endElement();
                aXmlWriter.startElement("pdfaProperty:name");
                aXmlWriter.content("part");
                aXmlWriter.endElement();
                aXmlWriter.startElement("pdfaProperty:valueType");
                aXmlWriter.content("Integer");
                aXmlWriter.endElement();
                aXmlWriter.endElement(); // rdf:li

                aXmlWriter.startElement("rdf:li");
                aXmlWriter.attribute("rdf:parseType", "Resource");
                aXmlWriter.startElement("pdfaProperty:category");
                aXmlWriter.content("internal");
                aXmlWriter.endElement();
                aXmlWriter.startElement("pdfaProperty:description");
                aXmlWriter.content("PDF/UA amendment identifier");
                aXmlWriter.endElement();
                aXmlWriter.startElement("pdfaProperty:name");
                aXmlWriter.content("amd");
                aXmlWriter.endElement();
                aXmlWriter.startElement("pdfaProperty:valueType");
                aXmlWriter.content("Text");
                aXmlWriter.endElement();
                aXmlWriter.endElement(); // rdf:li

                aXmlWriter.startElement("rdf:li");
                aXmlWriter.attribute("rdf:parseType", "Resource");
                aXmlWriter.startElement("pdfaProperty:category");
                aXmlWriter.content("internal");
                aXmlWriter.endElement();
                aXmlWriter.startElement("pdfaProperty:description");
                aXmlWriter.content("PDF/UA corrigenda identifier");
                aXmlWriter.endElement();
                aXmlWriter.startElement("pdfaProperty:name");
                aXmlWriter.content("corr");
                aXmlWriter.endElement();
                aXmlWriter.startElement("pdfaProperty:valueType");
                aXmlWriter.content("Text");
                aXmlWriter.endElement();
                aXmlWriter.endElement(); // rdf:li

                aXmlWriter.endElement(); // rdf:Seq
                aXmlWriter.endElement(); // pdfaSchema:property
                aXmlWriter.endElement(); // rdf:li
                aXmlWriter.endElement(); // rdf:Bag
                aXmlWriter.endElement(); // pdfaExtension:schemas
                aXmlWriter.endElement(); // rdf:Description
            }
            OString sPdfUaVersion = OString::number(mnPDF_UA);
            aXmlWriter.startElement("rdf:Description");
            aXmlWriter.attribute("rdf:about", "");
            aXmlWriter.attribute("xmlns:pdfuaid", "http://www.aiim.org/pdfua/ns/id/");

            aXmlWriter.startElement("pdfuaid:part");
            aXmlWriter.content(sPdfUaVersion);
            aXmlWriter.endElement();

            if (mnPDF_UA == 2)
            {
                aXmlWriter.startElement("pdfuaid:rev");
                aXmlWriter.content("2024");
                aXmlWriter.endElement();
            }
            aXmlWriter.endElement();
        }

        // PDF properties
        if (!msProducer.isEmpty() || !msKeywords.isEmpty() || !msPDFVersion.isEmpty())
        {
            aXmlWriter.startElement("rdf:Description");
            aXmlWriter.attribute("rdf:about", "");
            aXmlWriter.attribute("xmlns:pdf", "http://ns.adobe.com/pdf/1.3/");
            if (!msProducer.isEmpty())
            {
                aXmlWriter.startElement("pdf:Producer");
                aXmlWriter.content(msProducer);
                aXmlWriter.endElement();
            }
            if (!msKeywords.isEmpty())
            {
                aXmlWriter.startElement("pdf:Keywords");
                aXmlWriter.content(msKeywords);
                aXmlWriter.endElement();
            }
            if (!msPDFVersion.isEmpty())
            {
                aXmlWriter.startElement("pdf:PDFVersion");
                aXmlWriter.content(msPDFVersion);
                aXmlWriter.endElement();
            }
            aXmlWriter.endElement();
        }

        // XMP Basic schema
        aXmlWriter.startElement("rdf:Description");
        aXmlWriter.attribute("rdf:about", "");
        aXmlWriter.attribute("xmlns:xmp", "http://ns.adobe.com/xap/1.0/");
        if (!m_sCreatorTool.isEmpty())
        {
            aXmlWriter.startElement("xmp:CreatorTool");
            aXmlWriter.content(m_sCreatorTool);
            aXmlWriter.endElement();
        }
        aXmlWriter.startElement("xmp:CreateDate");
        aXmlWriter.content(m_sCreateDate);
        aXmlWriter.endElement();

        aXmlWriter.startElement("xmp:ModifyDate");
        aXmlWriter.content(m_sCreateDate);
        aXmlWriter.endElement();

        aXmlWriter.startElement("xmp:MetadataDate");
        aXmlWriter.content(m_sCreateDate);
        aXmlWriter.endElement();

        aXmlWriter.endElement();
        aXmlWriter.endElement();
        aXmlWriter.endElement();
        aXmlWriter.endDocument();
    }

    // add padding (needed so the metadata can be changed in-place"
    for (sal_Int32 nSpaces = 1; nSpaces <= 21; nSpaces++)
        mpMemoryStream->WriteOString(constPadding);

    mpMemoryStream->WriteOString("<?xpacket end=\"w\"?>\n");
    mbWritten = true;
}

sal_uInt64 XmpMetadata::getSize()
{
    if (!mbWritten)
        write();
    return mpMemoryStream->GetSize();
}

const void* XmpMetadata::getData()
{
    if (!mbWritten)
        write();
    return mpMemoryStream->GetData();
}

} // end vcl::pdf

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
