# SPDX-FileCopyrightText: 2022 James R. Barlow
# SPDX-License-Identifier: MPL-2.0
"""Built-in plugin to implement PDF page optimization."""

from __future__ import annotations

import argparse
import logging
from collections.abc import Sequence
from pathlib import Path

from ocrmypdf import Executor, PdfContext, hookimpl
from ocrmypdf._exec import jbig2enc, pngquant
from ocrmypdf._pipeline import get_pdf_save_settings
from ocrmypdf.cli import numeric
from ocrmypdf.optimize import optimize
from ocrmypdf.subprocess import check_external_program

log = logging.getLogger(__name__)


@hookimpl
def add_options(parser):
    optimizing = parser.add_argument_group(
        "Optimization options", "Control how the PDF is optimized after OCR"
    )
    optimizing.add_argument(
        '-O',
        '--optimize',
        type=int,
        choices=range(0, 4),
        default=1,
        help=(
            "Control how PDF is optimized after processing:"
            "0 - do not optimize; "
            "1 - do safe, lossless optimizations (default); "
            "2 - do lossy JPEG and JPEG2000 optimizations; "
            "3 - do more aggressive lossy JPEG and JPEG2000 optimizations. "
            "To enable lossy JBIG2, see --jbig2-lossy."
        ),
    )
    optimizing.add_argument(
        '--jpeg-quality',
        type=numeric(int, 0, 100),
        default=0,
        metavar='Q',
        help=(
            "Adjust JPEG quality level for JPEG optimization. "
            "100 is best quality and largest output size; "
            "1 is lowest quality and smallest output; "
            "0 uses the default."
        ),
    )
    optimizing.add_argument(
        '--jpg-quality',
        type=numeric(int, 0, 100),
        default=0,
        metavar='Q',
        dest='jpeg_quality',
        help=argparse.SUPPRESS,  # Alias for --jpeg-quality
    )
    optimizing.add_argument(
        '--png-quality',
        type=numeric(int, 0, 100),
        default=0,
        metavar='Q',
        help=(
            "Adjust PNG quality level to use when quantizing PNGs. "
            "Values have same meaning as with --jpeg-quality"
        ),
    )
    optimizing.add_argument(
        '--jbig2-lossy',
        action='store_true',
        help=(
            "Enable JBIG2 lossy mode (better compression, not suitable for some "
            "use cases - see documentation). Only takes effect if --optimize 1 or "
            "higher is also enabled."
        ),
    )
    optimizing.add_argument(
        '--jbig2-page-group-size',
        type=numeric(int, 1, 10000),
        default=0,
        metavar='N',
        # Adjust number of pages to consider at once for JBIG2 compression
        help=argparse.SUPPRESS,
    )
    optimizing.add_argument(
        '--jbig2-threshold',
        type=numeric(float, 0.4, 0.9),
        default=0.85,
        metavar='T',
        help=(
            "Adjust JBIG2 symbol code classification threshold "
            "(default 0.85), range 0.4 to 0.9."
        ),
    )


@hookimpl
def check_options(options):
    if options.optimize >= 2:
        check_external_program(
            program='pngquant',
            package='pngquant',
            version_checker=pngquant.version,
            need_version='2.12.2',
            required_for='--optimize {2,3}',
        )

    if options.optimize >= 2:
        # Although we use JBIG2 for optimize=1, don't nag about it unless the
        # user is asking for more optimization
        check_external_program(
            program='jbig2',
            package='jbig2enc',
            version_checker=jbig2enc.version,
            need_version='0.28',
            required_for='--optimize {2,3} | --jbig2-lossy',
            recommended=True if not options.jbig2_lossy else False,
        )

    if options.optimize == 0 and any(
        [options.jbig2_lossy, options.png_quality, options.jpeg_quality]
    ):
        log.warning(
            "The arguments --jbig2-lossy, --png-quality, and --jpeg-quality "
            "will be ignored because --optimize=0."
        )


@hookimpl
def optimize_pdf(
    input_pdf: Path,
    output_pdf: Path,
    context: PdfContext,
    executor: Executor,
    linearize: bool,
) -> tuple[Path, Sequence[str]]:
    save_settings = dict(
        linearize=linearize,
        **get_pdf_save_settings(context.options.output_type),
    )
    result_path = optimize(input_pdf, output_pdf, context, save_settings, executor)
    messages = []
    if context.options.optimize == 0:
        messages.append("Optimization was disabled.")
    else:
        image_optimizers = {
            'jbig2': jbig2enc.available(),
            'pngquant': pngquant.available(),
        }
        for name, available in image_optimizers.items():
            if not available:
                messages.append(
                    f"The optional dependency '{name}' was not found, so some image "
                    f"optimizations could not be attempted."
                )
    return result_path, messages


@hookimpl
def is_optimization_enabled(context: PdfContext) -> bool:
    return context.options.optimize != 0
