import os
import subprocess
from typing import Optional, Tuple

from platypush.plugins import action
from platypush.plugins.camera import CameraPlugin, Camera


class CameraIrMlx90640Plugin(CameraPlugin):
    """
    Plugin to interact with a `ML90640 <https://shop.pimoroni.com/products/mlx90640-thermal-camera-breakout>`_
    infrared thermal camera.

    In order to use this plugin you'll need to download and compile the
    `mlx90640 <https://github.com/pimoroni/mlx90640-library>`_ C++ bindings and examples for the device.
    Instructions on Raspbian::

        # Install the dependencies
        $ [sudo] apt-get install libi2c-dev
        $ cd $PLATYPUSH_SRC_DIR
        $ git submodule init
        $ git submodule update
        $ cd platypush/plugins/camera/ir/mlx90640/lib
        $ make clean
        $ make bcm2835
        $ make examples/rawrgb I2C_MODE=LINUX

    """

    def __init__(
        self,
        rawrgb_path: Optional[str] = None,
        resolution: Tuple[int, int] = (32, 24),
        warmup_frames: Optional[int] = 5,
        **kwargs,
    ):
        """
        :param rawrgb_path: Specify it if the rawrgb executable compiled from
            https://github.com/pimoroni/mlx90640-library is in another folder than
            `<directory of this file>/lib/examples`.
        :param resolution: Device resolution (default: 32x24).
        :param warmup_frames: Number of frames to be skipped on sensor initialization/warmup (default: 2).
        :param kwargs: Extra parameters to be passed to :class:`platypush.plugins.camera.CameraPlugin`.
        """
        super().__init__(
            device='mlx90640',
            resolution=resolution,
            warmup_frames=warmup_frames,
            **kwargs,
        )

        if not rawrgb_path:
            rawrgb_path = os.path.join(
                os.path.dirname(os.path.abspath(__file__)), 'lib', 'examples', 'rawrgb'
            )
        rawrgb_path = os.path.abspath(os.path.expanduser(rawrgb_path))

        if not (os.path.isfile(rawrgb_path)):
            raise AssertionError(
                'rawrgb executable not found. Please follow the documentation of this plugin to build it'
            )

        self.rawrgb_path = rawrgb_path
        self._capture_proc = None

    def _is_capture_running(self):
        return self._capture_proc is not None and self._capture_proc.poll() is None

    def prepare_device(self, device: Camera, **_):
        if not self._is_capture_running():
            self._capture_proc = subprocess.Popen(
                [self.rawrgb_path, '{}'.format(device.info.fps)],
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
            )

        return self._capture_proc

    def release_device(self, device: Camera):
        if not self._is_capture_running():
            return

        self._capture_proc.terminate()
        self._capture_proc.kill()
        self._capture_proc.wait()
        self._capture_proc = None

    def capture_frame(self, device: Camera, *args, **kwargs):
        from PIL import Image

        camera = self.prepare_device(device)
        frame = camera.stdout.read(
            device.info.resolution[0] * device.info.resolution[1] * 3
        )
        return Image.frombytes('RGB', device.info.resolution, frame)

    def to_grayscale(self, image):
        from PIL import Image

        new_image = Image.new('L', image.size)

        for i in range(0, image.size[0]):
            for j in range(0, image.size[1]):
                r, g, b = image.getpixel((i, j))
                value = int(2.0 * r - 1.125 * g - 1.75 * b)

                if value > 255:
                    value = 255
                if value < 0:
                    value = 0

                new_image.putpixel((i, j), value)

        return new_image

    @action
    def capture(self, output_file=None, *args, **kwargs):
        """
        Back-compatibility alias for :meth:`.capture_image`.
        """
        return self.capture_image(*args, image_file=output_file, **kwargs)


# vim:sw=4:ts=4:et:
