#!/usr/bin/env python3
from gi.repository import Gtk, GLib, Gio
import threading
from pydbus import SystemBus
import subprocess


class BluetoothMenu(Gtk.Application):
    def __init__(self):
        super().__init__(application_id="org.example.BluetoothMenu")
        self.bus = SystemBus()
        self.adapter = self.bus.get("org.bluez", "/org/bluez/hci0")
        self.object_manager = self.bus.get("org.bluez", "/")
        self.window = None
        self.device_window = None
        self.scanning = False
        self.discovered_devices = {}

    def do_activate(self):
        if not self.window:
            self.window = Gtk.ApplicationWindow(application=self)
            self.window.set_default_size(200, 150)
            self.window.set_title("Bluetooth Menu")
            self.vbox = Gtk.Box(
                orientation=Gtk.Orientation.VERTICAL, spacing=6)

            if hasattr(self.window, "set_child"):
                self.window.set_child(self.vbox)
            else:
                self.window.add(self.vbox)

            self.build_menu()
            self.update_bluetooth_buttons()

        self.window.present()

    def build_menu(self):
        self.item_scan = Gtk.Button(label='Escanear Dispositivos')
        self.item_scan.connect('clicked', self.scan_and_show_devices)
        self.add_to_box(self.vbox, self.item_scan)

        self.item_on = Gtk.Button(label='Encender Bluetooth')
        self.item_on.connect('clicked', self.turn_on_bluetooth)
        self.add_to_box(self.vbox, self.item_on)

        self.item_off = Gtk.Button(label='Apagar Bluetooth')
        self.item_off.connect('clicked', self.turn_off_bluetooth)
        self.add_to_box(self.vbox, self.item_off)

        self.item_send_file = Gtk.Button(label='Enviar Archivo')
        self.item_send_file.connect('clicked', self.send_file_dialog)
        self.add_to_box(self.vbox, self.item_send_file)

        item_quit = Gtk.Button(label='Cerrar')
        item_quit.connect('clicked', self.exit_app)
        self.add_to_box(self.vbox, item_quit)

    def add_to_box(self, box, widget):
        if hasattr(box, "append"):
            box.append(widget)
        else:
            box.pack_start(widget, True, True, 0)

    def send_file_dialog(self, _):
        dialog = Gtk.FileChooserDialog(
            title="Selecciona un archivo para enviar",
            transient_for=self.window,
            action=Gtk.FileChooserAction.OPEN,
            modal=True
        )
        dialog.add_buttons(
            "Cancelar", Gtk.ResponseType.CANCEL,
            "Abrir", Gtk.ResponseType.OK
        )

        dialog.connect("response", self.on_file_chosen)
        dialog.show()

    def on_file_chosen(self, dialog, response):
        if response == Gtk.ResponseType.OK:
            file = dialog.get_file()  # Obtenemos el archivo directamente
            if file:
                file_path = file.get_path()  # Obtenemos la ruta del archivo

                # Oculta la ventana principal antes de abrir la ventana de selección de dispositivos
                self.window.hide()

                # Llama a la función para seleccionar el dispositivo y enviar el archivo
                self.select_device_for_file_transfer(file_path)

        dialog.destroy()

    def select_device_for_file_transfer(self, file_path):
        if self.device_window:
            self.device_window.destroy()

        self.device_window = Gtk.Window(title="Seleccionar dispositivo")
        self.device_window.set_default_size(300, 400)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        self.device_window.set_child(vbox)

        devices = self.get_discovered_devices()
        for name, address in devices:
            button = Gtk.Button(label=f"{name} ({address})")
            button.connect("clicked", self.send_file_to_device,
                           file_path, address)
            vbox.append(button)

        # Añade un botón "Cerrar" al final de la ventana de selección de dispositivos
        close_button = Gtk.Button(label="Cerrar")
        close_button.connect("clicked", lambda _: self.device_window.destroy())
        vbox.append(close_button)

        # Muestra la ventana de selección de dispositivos
        self.device_window.show()

        # Restaura la ventana principal al cerrar la ventana de selección de dispositivos
        self.device_window.connect("destroy", lambda w: self.window.show())

    def send_file_to_device(self, button, file_path, address):
        try:
            # Ejecuta bluetooth-sendto con la dirección del dispositivo y el archivo
            result = subprocess.run(
                ['bluetooth-sendto', '--device=' + address, file_path],
                capture_output=True,
                text=True
            )

            # Verifica si se produjo un error en la ejecución
            if result.returncode == 0:
                self.show_notification(f"Archivo enviado a {address}")
            else:
                self.show_notification(
                    f"Error al enviar archivo: {result.stderr}")

        except Exception as e:
            self.show_notification(f"Error al ejecutar bluetooth-sendto: {e}")

    def update_bluetooth_buttons(self):
        powered = self.adapter.Powered
        self.item_on.set_sensitive(not powered)
        self.item_off.set_sensitive(powered)

    def scan_and_show_devices(self, _):
        try:
            # Inicia el escaneo solo si no está en proceso
            if not self.scanning:
                self.adapter.StartDiscovery()
                self.scanning = True
                self.show_notification(
                    "Escaneo de dispositivos Bluetooth iniciado")

            # Muestra la ventana de dispositivos en tiempo real
            self.show_device_list_window()
        except Exception as e:
            self.show_notification(f"Error iniciando el escaneo: {e}")

    def show_device_list_window(self):
        if self.device_window:
            self.device_window.destroy()

        self.device_window = Gtk.Window(title="Dispositivos Disponibles")
        self.device_window.set_default_size(400, 500)
        self.device_window.set_resizable(True)
        self.device_window.set_transient_for(self.window)

        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.set_policy(
            Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        scrolled_window.set_min_content_height(400)

        self.device_list_vbox = Gtk.Box(
            orientation=Gtk.Orientation.VERTICAL, spacing=10)
        if hasattr(scrolled_window, "set_child"):
            scrolled_window.set_child(self.device_list_vbox)
        else:
            scrolled_window.add(self.device_list_vbox)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        self.add_to_box(vbox, scrolled_window)

        refresh_button = Gtk.Button(label="Refrescar")
        refresh_button.connect("clicked", self.refresh_device_list)
        self.add_to_box(vbox, refresh_button)

        close_button = Gtk.Button(label="Cerrar")
        close_button.connect("clicked", self.stop_scan_and_close)
        self.add_to_box(vbox, close_button)

        if hasattr(self.device_window, "set_child"):
            self.device_window.set_child(vbox)
        else:
            self.device_window.add(vbox)

        self.device_window.present()

        # Asegura que el temporizador se ejecute para actualizar la lista en tiempo real
        if not hasattr(self, 'update_devices_timer'):
            self.update_devices_timer = GLib.timeout_add_seconds(
                2, self.update_device_list)

    def get_discovered_devices(self):
        devices = []
        managed_objects = self.object_manager.GetManagedObjects()
        for path, interfaces in managed_objects.items():
            if "org.bluez.Device1" in interfaces:
                device_properties = interfaces["org.bluez.Device1"]
                if device_properties.get("Connected") or device_properties.get("Paired"):
                    # Omitir dispositivos emparejados o ya conectados
                    continue
                name = device_properties.get("Name", "Desconocido")
                address = device_properties.get("Address")
                devices.append((name, address))
        return devices

    def refresh_device_list(self, _):
        self.discovered_devices.clear()
        if self.device_list_vbox and self.device_window:
            self.clear_box_children(self.device_list_vbox)
        self.show_notification("Lista de dispositivos actualizada")

    def clear_box_children(self, box):
        if hasattr(box, 'get_children'):
            for child in box.get_children():
                box.remove(child)
        else:
            child = box.get_first_child()
            while child:
                next_child = child.get_next_sibling()
                box.remove(child)
                child = next_child

    def update_device_list(self):
        devices = self.get_discovered_devices()
        for name, address in devices:
            if address not in self.discovered_devices:
                self.discovered_devices[address] = name
                if self.device_window and self.device_list_vbox:
                    button = Gtk.Button(label=f"{name} ({address})")
                    button.connect("clicked", self.pair_trust_connect, address)
                    self.add_to_box(self.device_list_vbox, button)
        return True

    def stop_scan_and_close(self, _):
        if self.device_window:
            self.device_window.destroy()
            self.device_window = None
        self.discovered_devices.clear()

    def get_discovered_devices(self):
        devices = []
        managed_objects = self.object_manager.GetManagedObjects()
        for path, interfaces in managed_objects.items():
            if "org.bluez.Device1" in interfaces:
                device_properties = interfaces["org.bluez.Device1"]
                name = device_properties.get("Name", "Desconocido")
                address = device_properties.get("Address")
                devices.append((name, address))
        return devices

    def pair_trust_connect(self, button, address):
        threading.Thread(target=self.pair_trust_connect_thread,
                         args=(address,)).start()

    def pair_trust_connect_thread(self, address):
        device_path = f"/org/bluez/hci0/dev_{address.replace(':', '_')}"
        device = self.bus.get("org.bluez", device_path)
        try:
            device.Pair()
            device.Trusted = True
            device.Connect()
            self.show_notification(f"Conectado a {address}")
        except Exception as e:
            self.show_notification(f"Error al conectar con {address}: {e}")

    def turn_on_bluetooth(self, _):
        self.adapter.Powered = True
        self.show_notification("Bluetooth Encendido")
        self.update_bluetooth_buttons()

    def turn_off_bluetooth(self, _):
        self.adapter.Powered = False
        self.show_notification("Bluetooth Apagado")
        self.update_bluetooth_buttons()

    def exit_app(self, _):
        if self.scanning:
            self.adapter.StopDiscovery()
            self.scanning = False
        if hasattr(self, 'update_devices_timer'):
            GLib.source_remove(self.update_devices_timer)
        self.quit()

    def show_notification(self, message):
        subprocess.run(['notify-send', message])


def main():
    app = BluetoothMenu()
    app.run()


if __name__ == "__main__":
    main()
