#!/usr/bin/env python3
"""Create a relay-compatible variant of a Konica Minolta vendor PPD.

The vendor C750i/C751i series PPDs are multi-model PostScript/PJL drivers and
include the C451i as a ``*Model`` choice.  They are applicable to the 451i, but
their ``*cupsFilter`` invokes ``KMbeuEmpPS.pl``, which emits a @PJL / Konica
Minolta PostScript stream for a direct printer connection.  The PaperCut
Mobility Print queue behind the relay accepts only application/pdf and
image/urf, so the vendor filter cannot be used there.

This tool makes a derived PPD that:

  * sets ``*DefaultModel`` to C451i (or --model);
  * removes the KMbeuEmpPS.pl ``*cupsFilter`` line;
  * adds the same PDF/URF pass-through filter lines as the server's generic
    AirPrint PPD, so CUPS sends PDF/URF to the relay.

The PPD's option set (Model, SelectColor, KMDuplex, PageSize, InputSlot,
MediaType, finishing, ...) is preserved.  Use papercut_relay_backend.py to map
the options Mobility Print understands (SelectColor/KMDuplex/PageSize, plus
ColorModel/PageSize/Duplex from the generic PPD) to IPP attributes.  Vendor
options that Mobility Print does not advertise (trays, media type, finishing)
will not reach the printer.

Usage:
    python3 -I make_relay_ppd.py KMbeuC750iux.ppd -o KMbeuC451i-relay.ppd
    python3 -I make_relay_ppd.py KMbeuC751iux.ppd --model C451i -o out.ppd

The output is a derived file; keep the original vendor PPD unchanged.
"""

from __future__ import annotations

import argparse
import os
import re
import sys

FILTER_RE = re.compile(
    r'^\*cupsFilter:\s*"application/vnd\.cups-postscript\s+0\s+'
    r'KMbeuEmpPS\.pl"\s*$'
)

PASS_THROUGH = [
    '*cupsFilter2: "application/vnd.cups-pdf application/pdf 0 maxsize(0) -"',
    '*cupsFilter: "application/vnd.cups-pdf 0 -"',
    '*cupsFilter2: "image/urf image/urf 10 -"',
    '*cupsFilter: "image/urf 10 -"',
]


def split_eol(line: str):
    if line.endswith("\r\n"):
        return line[:-2], "\r\n"
    if line.endswith("\n"):
        return line[:-1], "\n"
    return line, ""


def main(argv=None) -> int:
    parser = argparse.ArgumentParser(
        description="Create a relay-compatible KM C451i PPD."
    )
    parser.add_argument("ppd", help="vendor PPD (KMbeuC750iux.ppd or C751iux)")
    parser.add_argument("--model", default="C451i",
                        help="*Model choice to select as default")
    parser.add_argument("-o", "--out", default="-",
                        help="output PPD ('-' for stdout)")
    args = parser.parse_args(argv)

    try:
        with open(args.ppd, "r", encoding="latin-1", newline="") as fh:
            lines = fh.readlines()
    except OSError as exc:
        print(f"error: cannot read {args.ppd}: {exc}", file=sys.stderr)
        return 1

    model_re = re.compile(r"^\*Model " + re.escape(args.model) + r"/")
    if not any(model_re.match(split_eol(line)[0]) for line in lines):
        print(
            f"error: {args.ppd} has no '*Model {args.model}' choice",
            file=sys.stderr,
        )
        return 1

    out_path = args.out
    if out_path == "-":
        ppd_name = f"KMbeu{args.model}-relay.ppd"
    else:
        ppd_name = os.path.basename(out_path)

    out = []
    replaced_filter = False
    replaced_default = False
    replaced_meta = set()
    for line in lines:
        body, eol = split_eol(line)
        if not replaced_filter and FILTER_RE.match(body):
            out.extend(item + eol for item in PASS_THROUGH)
            replaced_filter = True
            continue
        if not replaced_default and body.startswith("*DefaultModel:"):
            out.append(f"*DefaultModel: {args.model}" + eol)
            replaced_default = True
            continue
        if body.startswith("*ModelName:") and "ModelName" not in replaced_meta:
            out.append(
                f'*ModelName: "KONICA MINOLTA {args.model}SeriesPS/P"'
                + eol
            )
            replaced_meta.add("ModelName")
            continue
        if body.startswith("*ShortNickName:") and "ShortNickName" not in replaced_meta:
            out.append(f'*ShortNickName: "KONICA MINOLTA {args.model}"' + eol)
            replaced_meta.add("ShortNickName")
            continue
        if body.startswith("*NickName:") and "NickName" not in replaced_meta:
            out.append(
                f'*NickName:\t"KONICA MINOLTA {args.model}SeriesPS(P) relay"'
                + eol
            )
            replaced_meta.add("NickName")
            continue
        if body.startswith("*Product:") and "Product" not in replaced_meta:
            out.append(f'*Product: "(KONICA MINOLTA {args.model})"' + eol)
            replaced_meta.add("Product")
            continue
        if body.startswith("*PCFileName:") and "PCFileName" not in replaced_meta:
            out.append(f'*PCFileName:\t"{ppd_name}"' + eol)
            replaced_meta.add("PCFileName")
            continue
        out.append(line)

    if not replaced_filter:
        print(
            "error: no '*cupsFilter: \"application/vnd.cups-postscript 0 "
            "KMbeuEmpPS.pl\"' line found",
            file=sys.stderr,
        )
        return 1
    if not replaced_default:
        print("error: no '*DefaultModel:' line found", file=sys.stderr)
        return 1

    text = "".join(out)
    if args.out == "-":
        sys.stdout.write(text)
        print(
            f"wrote relay PPD to stdout (model={args.model}, "
            "KMbeuEmpPS.pl disabled)",
            file=sys.stderr,
        )
    else:
        try:
            with open(args.out, "w", encoding="latin-1", newline="") as fh:
                fh.write(text)
            os.chmod(args.out, 0o644)
        except OSError as exc:
            print(f"error: cannot write {args.out}: {exc}", file=sys.stderr)
            return 1
        print(
            f"wrote {args.out} (model={args.model}, "
            "KMbeuEmpPS.pl disabled)",
            file=sys.stderr,
        )
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
