"""
This class executes when we are on a raspberry Pi.

It handles the press of a button via GPIO,
activates a flash and prints out the picture
that was taken via a USB Webcam.
"""

import io  # To check if we are on a Raspberry Pi
import subprocess
import os
from time import sleep, gmtime, strftime
from gpiozero import Button, LED, DigitalOutputDevice
from PIL import Image
from task import TextTask, ImageTask, CutTask


class Raspberry:
    """
    This class will manage three things :
    - Connecting to a USB webcam
    - Managing a push button
    - Activating a flash ( or light )
    - Flash an indicator light

    # pylint: disable=too-many-instance-attributes
    # dede
    """

    def __init__(
        self,
        print_queue,
        app,
        configuration_file
    ):
        self.print_queue = print_queue
        self.app = app
        self.configuration_file = configuration_file
        self.image_path = self.app.config["UPLOAD_FOLDER"] + "/image.jpg"

    def is_raspberry_pi(self):
        """
        Checking if we are on a Raspberry Pi by checking
        information on the /proc/cpuinfo file
        """
        # Check if we are running on a raspberry pi
        try:
            with io.open("/proc/cpuinfo", "r", encoding="utf-8") as cpuinfo:
                found = False
                for line in cpuinfo:
                    if line.startswith("Hardware"):
                        found = True
                        label, value = line.strip().split(":", 1)
                        value = value.strip()
                        if value not in (
                            "BCM2708",
                            "BCM2709",
                            "BCM2711",
                            "BCM2835",
                            "BCM2836",
                        ):
                            self.app.logger.debug(
                                "This system does not appear to be a Raspberry Pi."
                            )
                            return False

                if not found:
                    self.app.logger.warning(
                        "Couldn't get sufficient hardware information from /proc/cpuinfo"
                    )
                    self.app.logger.error("Unable to determine if we are on a Raspberry Pi.")
                    return False
        except IOError:
            self.app.logger.error("Unable to open `/proc/cpuinfo`.")
            return False

        self.app.logger.debug("It seems we are on a Raspberry Pi")

        try:
            self._initialise_gpio()
        except Exception as e:
            self.app.logger.debug("Could not init GPIO : " + str(e))
            raise e
        return True

    def _initialise_gpio(self):
        """
        Set GPIO ports from configuration and activate them
        to show the user it's working.
        """
        self.app.logger.debug("Initializing GPIO")

        self.led = LED(self.configuration_file["rpi"]["indicator_gpio_port_number"])
        self.app.logger.debug("Activated indicator LED")
        self.indicator_countdown(iters=3)
        self.button = Button(
            self.configuration_file["rpi"]["button_gpio_port_number"],
            pull_up=True, bounce_time=0.1
        )
        self.button.when_pressed = self.on_button_pressed
        self.app.logger.debug("Activated button")

        # The "flash" is a relay-controlled device ( light bulb for example )
        self.flash = DigitalOutputDevice(self.configuration_file["rpi"]["flash_gpio_port_number"])
        self.flash_toggle()
        self.app.logger.debug("Activated flash")

    def indicator_countdown(self, iters=10, multi=10):
        """
        Activates the LED faster and faster to show a countdown
        """
        for i in range(iters, 0, -1):
            self.led.on()
            sleep(i / multi)
            self.led.off()
            sleep(i / multi)

    def indicator_led(self, timing=0.2, l=5):
        """
        Turns on the indicator LED for a certain period of time
        """
        for _ in range(l):
            self.app.logger.debug("LED turned on")
            self.led.on()
            sleep(timing)
            self.led.off()
            self.app.logger.debug("LED turned off")
            sleep(timing)

    def flash_toggle(self):
        """
        Flashes the flash
        """
        self.app.logger.debug("Flash turned on")
        self.flash.on()
        sleep(0.3)
        self.flash.off()
        self.app.logger.debug("Flash turned off")

    def take_picture(self):
        """
        Takes a picture via the USB webcam
        """
        # Validate if the image path is valid
        if not os.path.isdir(os.path.dirname(self.image_path)):
            self.app.logger.error(
                f"Invalid directory for image path: {self.image_path}"
            )
            return False

        try:
            result = subprocess.run(
                ["fswebcam", "--no-banner", "-r", "1920x1080", self.image_path],
                check=True,  # Will raise CalledProcessError if the command fails
                stdout=subprocess.PIPE,  # Capture standard output
                stderr=subprocess.PIPE,  # Capture error output
            )

            # Optionally log the command output
            self.app.logger.debug(
                f"Image captured successfully: {result.stdout.decode()}"
            )
        except subprocess.CalledProcessError as e:
            # Log error output if available
            self.app.logger.error(
                f"Unable to take a picture. Error: {e.stderr.decode()}"
            )
            return False
        except RuntimeError as e:
            # Catch any unexpected errors
            self.app.logger.error(f"Unexpected error while taking picture: {str(e)}")
            return False

        #   # Overlay logo
        # logo_path = 'src/static/images/requin.png'  # Update path as needed
        # if not self.overlay_logo(self.image_path, logo_path):
        #     self.app.logger.warning("Picture taken but failed to overlay logo.")

        return True

    def overlay_logo(
        self,
        image_path,
        logo_path,
        output_path=None,
        position="bottom_right",
        margin=10,
    ):
        """
        Takes an image and overlays it with a another picture.
        """
        try:
            image = Image.open(image_path).convert("RGBA")
            logo = Image.open(logo_path).convert("RGBA")

            # Resize logo if it's too big (logo will be 30% the width of the image)
            logo_ratio = (
                0.30  # You can change the ratio if you want the logo bigger or smaller
            )
            logo_width = int(image.width * logo_ratio)
            logo_height = int(logo.height * (logo_width / logo.width))
            logo = logo.resize((logo_width, logo_height), Image.Resampling.LANCZOS)

            # Calculate position based on the chosen location
            if position == "bottom_right":
                x = image.width - logo.width - margin
                y = image.height - logo.height - margin
            elif position == "top_left":
                x, y = margin, margin
            elif position == "top_right":
                x = image.width - logo.width - margin
                y = margin
            elif position == "bottom_left":
                x = margin
                y = image.height - logo.height - margin
            else:
                raise ValueError(
                    "Invalid position." +
                    "Choose from 'bottom_right', 'top_left', " +
                    " 'top_right', or 'bottom_left'."
                )

            # Composite the logo onto the image
            image.paste(logo, (x, y), logo)  # Use logo as its own alpha mask

            # Save the result
            if not output_path:
                output_path = image_path  # Overwrite the original image if no output path is given
            image.save(output_path)

        except RuntimeError as e:
            self.app.logger.error(f"Error overlaying logo: {e}")
            return False

        return True

    def crop_to_square(self, image_path, output_path=None):
        """
        Crop an image so that it becomes a square
        """
        try:
            image = Image.open(image_path)
            width, height = image.size

            # Determine shorter side
            new_edge = min(width, height)

            # Calculate cropping box (centered)
            left = (width - new_edge) // 2
            top = (height - new_edge) // 2
            right = left + new_edge
            bottom = top + new_edge

            image = image.crop((left, top, right, bottom))

            if not output_path:
                output_path = image_path  # Overwrite original

            image.save(output_path)

        except RuntimeError as e:
            self.app.logger.error(f"Error cropping image to square: {e}")
            return False

        return True

    def on_button_pressed(self):
        """
        When a button press is detected, a picture is taken from the webcam
        and added to the print queue.
        """
        self.app.logger.debug("Button has been pressed")
        self.led.on()
        self.app.logger.debug("Counting down")
        self.indicator_countdown(
            iters=4, multi=20
        )  # The indicator will flash a countdown LED
        self.app.logger.debug("Taking picture")
        try:
            self.flash.on()
            self.take_picture()
        except RuntimeError as e:
            self.app.logger.error(
                "Could not take a picture after the button press : " + str(e)
            )
        finally:
            self.flash.off()
        self.app.logger.debug("Printing picture")
        self.led.on()
        self.crop_to_square(self.image_path)
        self.print_queue.enqueue(ImageTask(self.image_path, signature="", process=True))
        self.print_queue.enqueue(
            TextTask(content="Imprimé par LittlePrynter", signature="")
        )
        time = strftime("%Y-%m-%d %H:%M", gmtime())
        self.print_queue.enqueue(TextTask(content=time, signature=""))
        self.print_queue.enqueue(CutTask())
        self.led.off()
        self.app.logger.debug("Added a photomaton picture to the print queue")
        return True
