# 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/.

# src/pdftl/commands/update_info.py

"""Update PDF metadata from a dump (as generated by dump_data)"""

import sys

from pdftl.core.registry import register_operation
from pdftl.exceptions import (
    InvalidArgumentError,
    MissingArgumentError,
    UserCommandLineError,
)
from pdftl.info.parse_dump import parse_dump_data
from pdftl.info.set_info import set_metadata_in_pdf
from pdftl.utils.string import xml_decode_for_info
from pdftl.utils.user_input import filename_completer

_UPDATE_INFO_UTF8_LONG_DESC = """

Updates the document-level metadata and structural
information of the input PDF by reading from a specified
metadata file (or stdin).

This operation is the direct counterpart to
'dump_data_utf8'. It is identical to 'update_info', except
it assumes the input file contains raw UTF-8 strings that
have **not** been XML-escaped.

Use this operation if your metadata source file was created
by 'dump_data_utf8' or if you have manually created a
metadata file that contains raw, unescaped UTF-8 characters.

For a complete description of the input format this command
expects, see the help for 'dump_data'.

"""

_UPDATE_INFO_UTF8_EXAMPLES = [
    {
        "cmd": "in.pdf update_info_utf8 meta.txt output out.pdf",
        "desc": "Update utf-8 metadata according to meta.txt",
    },
]


_UPDATE_INFO_LONG_DESC = """

Updates the document-level metadata and structural
information of the input PDF by reading from a specified
metadata file (or stdin).

This operation consumes the plain-text format generated by
the 'dump_data' command. It correctly parses the XML-escaped
strings created by that command.

This operation can read a full 'dump_data' file and will
apply the following sections to the output PDF:

* **Info:** Updates the Document Info dictionary (Title,
  Author, Subject, Keywords, CreationDate, ModDate, and
  custom keys).

* **Bookmarks:** Deletes all existing bookmarks and replaces
  them with the 'Bookmark' stanzas from the input file.

* **PageMedia:** Applies page-level settings (Rotation,
  CropBox, TrimBox) as defined in 'PageMedia' stanzas.

* **PageLabels:** Replaces the document's page labelleling
  scheme (e.g., 'i, ii, iii') using the 'PageLabel' stanzas
  from the input file.

* **PdfID:** Updates the `PdfID0` top-level field. The
  `PdfID1` is not updated: this is a design limitation in
  the underlying PDF library, presumably in order to comply
  with the PDF specification.

For a complete description of the input format this command
expects, see the help for 'dump_data'.

"""

_UPDATE_INFO_EXAMPLES = [
    {
        "cmd": "in.pdf update_info meta.txt output out.pdf",
        "desc": "Update metadata according to meta.txt",
    },
]


@register_operation(
    "update_info_utf8",
    tags=["info", "metadata"],
    type="single input operation",
    desc="Update PDF metadata from dump_data_utf8 instructions",
    long_desc=_UPDATE_INFO_UTF8_LONG_DESC,
    usage="<input> update_info_utf8 <metadata> output <filename>",
    examples=_UPDATE_INFO_UTF8_EXAMPLES,
    args=(["input_pdf", "operation_args", "get_input"], {}, {"xml_strings": False}),
)
@register_operation(
    "update_info",
    tags=["info", "metadata"],
    type="single input operation",
    desc="Update PDF metadata",
    long_desc=_UPDATE_INFO_LONG_DESC,
    usage="<input> update_info <metadata> output <filename>",
    examples=_UPDATE_INFO_EXAMPLES,
    args=(["input_pdf", "operation_args", "get_input"], {}, {"xml_strings": True}),
)
def update_info(pdf, op_args, get_input, xml_strings=True):
    """Update PDF info. Expects op_args to have valid operation
    arguments (see pdftl help update_info).

    xml_strings controls whether to XML-decode string fields in the
    input.

    """
    if len(op_args) > 1:
        raise InvalidArgumentError(
            f"Unexpected argument(s) for update_info: {' '.join(op_args[1:])}"
        )
    if not op_args:
        raise MissingArgumentError("update_info requires a <metadata> argument")
    meta_filename = op_args[0]
    if meta_filename == "PROMPT":
        meta_filename = get_input(
            "Enter the filename of a metadata file in dump_data format: ",
            completer=filename_completer,
        )
    string_decoder = xml_decode_for_info if xml_strings else lambda x: x
    try:
        with (
            sys.stdin if meta_filename == "-" else open(meta_filename, "rb")
        ) as meta_file:
            meta_dict = parse_dump_data(meta_file.readlines(), string_decoder)
    except OSError as exc:
        raise UserCommandLineError(exc) from exc
    set_metadata_in_pdf(pdf, meta_dict)

    return pdf
