from distutils.spawn import find_executable
from logging import getLogger
from ploneintranet import api as pi_api
from ploneintranet.docconv.client.utils import BaseConverter
from ploneintranet.docconv.client.utils import BaseSubProcess

import chardet
import re

logger = getLogger(__name__)


aspect_ratio_regex = re.compile(r"Page.* size: *([\d.]+) x ([\d.]+)")
rotation_regex = re.compile(r"Page.* rot: *([\d.]+)")


class ExtractAspectRatioSubProcess(BaseSubProcess):
    """Extract and store the aspect ratio for a single page"""

    pdfinfo_cmd = find_executable("pdfinfo")

    def aspect_ratio(self, page_info):
        try:
            page_info = page_info.decode(chardet.detect(page_info).get("encoding"))
        except UnicodeDecodeError:
            logger.error("Cannot decode binary pdfinfo for page.")
            return
        match = aspect_ratio_regex.search(page_info)
        if not match:
            logger.error("Invalid size pdfinfo for page: %r", page_info)
            return "auto"

        try:
            x, y = float(match.group(1)), float(match.group(2))
        except ValueError:
            x, y = 0, 0

        if not all([x, y]):
            logger.error("Invalid size pdfinfo for page: %r", page_info)
            return "auto"

        if self.is_rotated(page_info):
            # flip height and width if document is rotated 90 or 270 degrees
            return y / x
        return x / y

    def is_rotated(self, page_info):
        match = rotation_regex.search(page_info)
        if not match:
            logger.error("Invalid rotation pdfinfo for page: %r", page_info)
            return False
        # any rotation other than 0 or 180 is considered rotated
        return bool(int(match.group(1)) % 180)

    def __call__(self, pdf_path, page):
        page = str(page)
        cmd = [self.pdfinfo_cmd, "-f", page, "-l", page, "-box", pdf_path]
        page_info = self._run_command(cmd)
        return self.aspect_ratio(page_info)


extract_aspect_ratio = ExtractAspectRatioSubProcess()
if not extract_aspect_ratio.pdfinfo_cmd:
    logger.error(
        "No `pdfinfo` (from `poppler-utils`) command in your PATH. "
        "Aspect ratio determination will not work."
    )


class AspectRatios(BaseConverter):
    """
    Extract aspect ratios for all pages at once
    """

    def pdf_path(self):
        if self.fw.file_type == "application/pdf":
            # context is already a pdf, no need to access a generated version
            blob = self.fw.blob
        else:
            # context is not pdf, so access the generated pdf preview
            blob = pi_api.previews.get_pdf(self.context)
        if not blob:
            logger.info("Cannot find pdf for %r", self.context)
            return
        with blob.open() as f:
            file_path = f.name
        return file_path

    def run(self, num_preview_limit=None):
        if self.settings.num_pages < 1:
            logger.error("PDF has no pages")
        pdf_path = self.pdf_path()
        aspect_ratios = []
        limit = self.settings.num_pages
        if num_preview_limit and num_preview_limit < limit:
            limit = num_preview_limit
        for page in range(1, limit + 1):
            ratio = extract_aspect_ratio(pdf_path, page)
            aspect_ratios.append(ratio)
        self.settings.aspect_ratios = tuple(aspect_ratios)
        return self.settings.aspect_ratios
