""" ImageConverter class
    Convert an image to JPEG using CoreImage --
    for example, RAW to JPEG.  Only works if Mac equipped with GPU. """

# reference: https://stackoverflow.com/questions/59330149/coreimage-ciimage-write-jpg-is-shifting-colors-macos/59334308#59334308

import pathlib
import sys

# needed to capture system-level stderr
from wurlitzer import pipes

from .platform import is_macos

if is_macos:
    import Metal
    import objc
    import Quartz
    from Cocoa import NSURL
    from Foundation import NSDictionary

__all__ = ["ImageConversionError", "ImageConverter"]


class ImageConversionError(Exception):
    """Base class for exceptions in this module."""

    pass


class ImageConverter:
    """Convert images to jpeg.  This class is a singleton
    which will re-use the Core Image CIContext to avoid
    creating a new context for every conversion."""

    def __new__(cls, *args, **kwargs):
        """create new object or return instance of already created singleton"""
        if not hasattr(cls, "instance") or not cls.instance:
            cls.instance = super().__new__(cls)

        return cls.instance

    def __init__(self):
        """return existing singleton or create a new one"""

        if hasattr(self, "context"):
            return

        """ initialize CIContext """
        context_options = NSDictionary.dictionaryWithDictionary_(
            {
                "workingColorSpace": Quartz.CoreGraphics.kCGColorSpaceExtendedSRGB,
                "workingFormat": Quartz.kCIFormatRGBAh,
            }
        )
        self.context = Quartz.CIContext.contextWithOptions_(context_options)

    def write_jpeg(self, input_path, output_path, compression_quality=1.0):
        """convert image to jpeg and write image to output_path

        Args:
            input_path: path to input image (e.g. '/path/to/import/file.CR2') as str or pathlib.Path
            output_path: path to exported jpeg (e.g. '/path/to/export/file.jpeg') as str or pathlib.Path
            compression_quality: JPEG compression quality, float in range 0.0 to 1.0; default is 1.0 (best quality)

        Return:
            True if conversion successful, else False

        Raises:
            ValueError if compression quality not in range 0.0 to 1.0
            FileNotFoundError if input_path doesn't exist
            ImageConversionError if error during conversion
        """

        # Set up a dedicated objc autorelease pool for this function call.
        # This is to ensure that all the NSObjects are cleaned up after each
        # call to prevent memory leaks.
        # https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html
        # https://pyobjc.readthedocs.io/en/latest/api/module-objc.html#memory-management
        with objc.autorelease_pool():
            # accept input_path or output_path as pathlib.Path
            if not isinstance(input_path, str):
                input_path = str(input_path)

            if not isinstance(output_path, str):
                output_path = str(output_path)

            if not pathlib.Path(input_path).is_file():
                raise FileNotFoundError(f"could not find {input_path}")

            if not (0.0 <= compression_quality <= 1.0):
                raise ValueError(
                    "illegal value for compression_quality: {compression_quality}"
                )

            input_url = NSURL.fileURLWithPath_(input_path)
            output_url = NSURL.fileURLWithPath_(output_path)

            with pipes() as (out, err):
                # capture stdout and stderr from system calls
                # otherwise, Quartz.CIImage.imageWithContentsOfURL_
                # prints to stderr something like:
                # 2020-09-20 20:55:25.538 python[73042:5650492] Creating client/daemon connection: B8FE995E-3F27-47F4-9FA8-559C615FD774
                # 2020-09-20 20:55:25.652 python[73042:5650492] Got the query meta data reply for: com.apple.MobileAsset.RawCamera.Camera, response: 0
                input_image = Quartz.CIImage.imageWithContentsOfURL_(input_url)

            if input_image is None:
                raise ImageConversionError(f"Could not create CIImage for {input_path}")

            output_colorspace = (
                input_image.colorSpace()
                or Quartz.CGColorSpaceCreateWithName(
                    Quartz.CoreGraphics.kCGColorSpaceSRGB
                )
            )

            output_options = NSDictionary.dictionaryWithDictionary_(
                {"kCGImageDestinationLossyCompressionQuality": compression_quality}
            )
            (
                _,
                error,
            ) = self.context.writeJPEGRepresentationOfImage_toURL_colorSpace_options_error_(
                input_image, output_url, output_colorspace, output_options, None
            )
            if not error:
                return True
            else:
                raise ImageConversionError(
                    f"Error converting file {input_path} to jpeg at {output_path}: {error}"
                )
