from decimal import Decimal

from knowit.core import Rule


class ResolutionRule(Rule):
    """Resolution rule."""

    standard_resolutions = (
        480,
        720,
        1080,
        2160,
        4320,
    )
    uncommon_resolutions = (
        240,
        288,
        360,
        576,
    )
    resolutions = list(sorted(standard_resolutions + uncommon_resolutions))
    square = 4. / 3
    wide = 16. / 9

    def execute(self, props, pv_props, context):
        """Return the resolution for the video.

        The resolution is based on a widescreen TV (16:9)
        1920x800 will be considered 1080p since the TV will use 1920x1080 with vertical black bars
        1426x1080 is considered 1080p since the TV will use 1920x1080 with horizontal black bars

        The calculation considers the display aspect ratio and the pixel aspect ratio (not only width and height).
        The upper resolution is selected if there's no perfect match with the following list of resolutions:
            240, 288, 360, 480, 576, 720, 1080, 2160, 4320
        If no interlaced information is available, resolution will be considered Progressive.
        """
        width = props.get('width')
        height = props.get('height')
        if not width or not height:
            return

        try:
            width = width.magnitude
            height = height.magnitude
        except AttributeError:
            pass

        dar = props.get('aspect_ratio', Decimal(width) / height)
        par = props.get('pixel_aspect_ratio', 1)
        scan_type = props.get('scan_type', 'p')[0].lower()

        # selected DAR must be between 4:3 and 16:9
        selected_dar = max(min(dar, self.wide), self.square)

        # mod-16
        stretched_width = int(round(width * par / 16)) * 16

        # mod-8
        calculated_height = int(round(stretched_width / selected_dar / 8)) * 8

        selected_resolution = None
        for r in reversed(self.resolutions):
            if r < calculated_height:
                break

            selected_resolution = r

        if selected_resolution:
            return f'{selected_resolution}{scan_type}'

        msg = f'{width}x{height} - scan_type: {scan_type}, aspect_ratio: {dar}, pixel_aspect_ratio: {par}'
        self.report(msg, context)
