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


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

    """

    def __init__(
        self,
        printer,
        app,
        socketio,
        button_gpio_port_number,
        indicator_gpio_port_number,
        flash_gpio_port_number,
        is_flash_present,
    ):
        self.printer = printer
        self.socketio = socketio
        self.app = app

        self.flash_gpio = flash_gpio_port_number
        self.is_flash_present = is_flash_present
        self.button_gpio = button_gpio_port_number
        self.led_gpio = indicator_gpio_port_number
        self.image_path = self.app.config["UPLOAD_FOLDER"] + "/image.jpg"

    def is_raspberry_pi(self, raise_on_errors=False):
        # Check if we are running on a raspberry pi
        try:
            with io.open("/proc/cpuinfo", "r") 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.error(
                        "Couldn't get sufficient hardware information from /proc/cpuinfo, 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):
        self.app.logger.debug("Initializing GPIO")

        self.led = LED(self.led_gpio)
        self.app.logger.debug("Activated indicator LED")
        self.indicator_countdown(iters=3)
        self.button = Button(self.button_gpio, 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.flash_gpio)
        self.flash_toggle()
        self.app.logger.debug("Activated flash")

    def indicator_countdown(self, iters=10, multi=10):
        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):
        for i 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):
        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):
        # 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 Exception 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,
    ):
        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 Exception 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):
        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 Exception as e:
            self.app.logger.error(f"Error cropping image to square: {e}")
            return False

        return True

    def on_button_pressed(self):
        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 Exception 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.printer.print_img("src/static/images/extase-club.png", process=True)
        self.printer.print_img(self.image_path, process=True)
        self.printer.print_sms("")
        self.printer.print_sms("With Love From Société.Vide", signature="", bold=True)
        self.printer.print_sms("Printed by LittlePrynter", signature="")
        self.printer.print_sms("n07070.xyz", signature="")
        self.printer.print_sms(strftime("%Y-%m-%d %H:%M", gmtime()), signature="")
        self.printer.qr("https://n07070.xyz/articles/littleprynter")
        self.printer.cut()
        self.led.off()
        self.app.logger.debug("Done printing picture")
        return True
