#!/usr/bin/python3
# -*- coding: utf-8 -*-

# Copyright (c) 2014-2024 Arturo Martín Romero <amartinromero@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

__author__ = [
    "Arturo Martín Romero <amartinromero@gmail.com>",
    "Nacho Sancho Morte <sannacho@gmail.com>",
]
__license__ = "GPLv3"
__copyright__ = "(C) 2014-2024 Vitalinux team"

import os
import sys
import subprocess
import gettext
import logging

_ = gettext.gettext
gettext.bindtextdomain(
    "vx-pantallas-conmutador", "/usr/share/locale"
)  # Indicamos el dominio o nombre del archivo .mo
gettext.textdomain("vx-pantallas-conmutador")

from gi import require_version

require_version("Gtk", "3.0")
require_version("AppIndicator3", "0.1")

from gi.repository import (
    Gio,
    GLib,
    GObject,
    Gtk as gtk,
    AppIndicator3 as AppIndicator,
)


def obtener_version_paquete_apt(nombre_paquete):
    try:
        resultado = subprocess.run(
            ["dpkg", "-s", nombre_paquete],
            stdout=subprocess.PIPE,
            check=True,
            universal_newlines=True,
        )
        lineas = resultado.stdout.split("\n")
        for linea in lineas:
            if linea.startswith("Version:"):
                _version = linea.split(" ")[1]
                print(f"#> Versión de \"{nombre_paquete}\": '{_version }'")
                return _version
    except subprocess.CalledProcessError:
        return None


__paquete__ = "vx-dga-l-pantallas-conmutador"
__version__ = obtener_version_paquete_apt(__paquete__)


class SystrayIconApp(object):
    # Definimos las constantes:
    APP_ID = "vx-dga-l-pantallas-conmutador"
    APP_NAME = _("Screen Resolution Switch")
    APP_LICENCE = """This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License version 3, as published
by the Free Software Foundation.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranties of
MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program. If not, see http://www.gnu.org/licenses/"""
    # Determinamos la app version:
    # APP_VERSION = pkg_resources.require(APP_ID)[0].version
    APP_VERSION = __version__
    APP_TOOLTIP = _("Change Screen Resolution and Projection")
    APP_ICON = "vx-pantallas-conmutador"
    APP_ICON_ORANGE = "vx-pantallas-conmutador-orange"
    APP_DESCRIPTION = _(
        "Allows you to switch between different screen resolutions 4x3 and 16x9, with or without a projector"
    )
    CMD_DCONF_SCHEMA = "dconf-editor"
    CMD_LXRAND_SET = "vx-lxrand-set"
    VX_LXRAND_OPERATION_TIPO_DEFAULT = "auto"
    CMD_DISPLAY_SETTINGS = "xfce4-display-settings"
    CMD_CONFIGURE_OPTIONS = "vx-pantallas-conmutador-indicator-update"
    CMD_NATIVE_RESOLUTION = "vx-lxrand-set -m auto -x native"
    SCHEMA_BASE = "org.vitalinux.pantallas-conmutador"
    URL_SOPORTE = "https://soporte.vitalinux.educa.aragon.es"
    URL_GITLAB = "https://gitlab.vitalinux.educa.aragon.es/vitalinux-devops/vx-dga-l-pantallas-conmutador"

    def gsettings_set(self, schema, rama, param, value):
        if rama != None:
            _ruta = schema + "." + rama
        else:
            _ruta = schema
        _settings = Gio.Settings.new(_ruta)
        _settings.set_boolean(param, bool(int(value)))

    def gsettings_get(self, schema, rama, param):
        if rama != None:
            _ruta = schema + "." + rama
        else:
            _ruta = schema
        _settings = Gio.Settings.new(_ruta)
        print(
            f"El valor de {_ruta} y parametro {param} es: {_settings.get_value(param)}"
        )
        return _settings.get_value(param)

    def __init__(self):
        self.tray = AppIndicator.Indicator.new(
            self.APP_ID,
            self.APP_ICON,
            AppIndicator.IndicatorCategory.APPLICATION_STATUS,
        )
        self.tray.set_status(AppIndicator.IndicatorStatus.ACTIVE)
        GLib.idle_add(self.tray.set_icon, self.APP_ICON)
        # Personalizamos el título o mensaje emergente que se muestra al pasar el ratón por el indicador:
        self.tray.set_title(self.APP_TOOLTIP)

        # Mostramos la información del dimensiones de la pantalla por defecto:
        self.run_obtener_size()
        # Creamos el menú del indicator:
        self.make_menu()

        # Conectamos la señal "changed" del objeto GSettings para actualizar el menú
        self.settings = Gio.Settings.new(self.SCHEMA_BASE)
        self.settings.connect("changed", self.on_settings_changed)

    def run_obtener_size(self):
        comando = ["obtener-resolucion-pantalla"]
        _resultado = self.ejecutar_comando(comando)
        self.resolucion = _resultado.replace(":", "x")
        print(f"#> Las dimensiones aproximadas del monitor son: {self.resolucion}")

    def ejecutar_comando(self, comando):
        try:
            # Ejecutar el comando y capturar la salida estándar
            print(f"#> Ejecutamos: {comando}")
            resultado = subprocess.check_output(
                comando, shell=True, universal_newlines=True
            )
            return resultado.strip()
        except subprocess.CalledProcessError as e:
            # Manejar errores si el comando devuelve un código de salida no exitoso
            print(f"Error al ejecutar el comando: {e}")
            return None

    def run_pantallas_conmutador(self):
        comando = [
            self.CMD_LXRAND_SET,
            "-m",
            self.VX_LXRAND_OPERATION_TIPO_DEFAULT,
            "-x",
            self.resolucion,
        ]
        print(f"Se va a ejecutar el comando: {comando}")
        resultado = subprocess.run(
            comando, capture_output=True, universal_newlines=True
        )
        # Imprimimos el resultado por la salida estándar:
        print(resultado.stdout)

    def run_soporte(self, widget):
        subprocess.call(["xdg-open", self.URL_SOPORTE])

    def run_native_resolution(self, widget):
        comando = [self.CMD_NATIVE_RESOLUTION]
        self.ejecutar_comando(comando)

    def run_display_settings(self, widget):
        comando = [self.CMD_DISPLAY_SETTINGS]
        self.ejecutar_comando(comando)

    def run_dconf(self, widget):
        # subprocess.call([self.CMD_DCONF_SCHEMA, self.SCHEMA_BASE])
        # comando = [self.CMD_DCONF_SCHEMA, self.SCHEMA_BASE]
        comando = [self.CMD_CONFIGURE_OPTIONS]
        # Usamos subprocess para ejecutar el comando en segundo plano:
        subprocess.run(
            comando,
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            start_new_session=True,
        )

    def exit_menu(self, widget):
        sys.exit(0)

    def make_item_menu(
        self,
        etiqueta,
        imagen: str = None,
        sensitive: bool = True,
        signal: str = None,
        funcion: str = None,
    ):
        _el = gtk.MenuItem()
        _box = gtk.Box(orientation=gtk.Orientation.HORIZONTAL, spacing=6)
        if imagen != None:
            _img = gtk.Image()
            _img.set_from_icon_name(imagen, 20)
            _box.pack_start(_img, False, False, 0)
        _label = gtk.Label()
        _label.set_text(etiqueta)
        _box.pack_start(_label, False, False, 0)
        _el.add(_box)
        # Añadimos la señal al elemento del menú y su función asociada:
        if signal != None and funcion != None:
            _el.connect(signal, funcion)
        _el.set_sensitive(sensitive)
        # _el.set_always_show_image(True)
        _el.show()
        self.menu.append(_el)

    def make_item_menu_radiolist(self, etiqueta):
        if self.radio_group == None:
            # Al primer elemento del grupo se le asigna un grupo None, y una vez creado se captura el grupo de pertenencia para asignarlo a los demás:
            radio_item = gtk.RadioMenuItem.new_with_mnemonic(None, f"{etiqueta}")
            radio_item.set_active(False)
            radio_item.set_no_show_all(True)
            self.radio_group = radio_item.get_group()
        else:
            radio_item = gtk.RadioMenuItem.new_with_mnemonic(
                self.radio_group, f"{etiqueta}"
            )
        radio_item.connect("activate", self.on_radio_toggled, f"{etiqueta}")
        self.menu.append(radio_item)
        # radio_item.set_active(False)

    def make_menu(self):
        self.menu = gtk.Menu()

        # Creamos el Grupo para el Radiolist:
        self.radio_group = None
        # Añadimos los elementos del Radiolist a partir de los valores del Esquema:
        # El primer elemento del menú (Defecto) no se visualiza, y lo creamos al no tener la posibilidad de no dejar todos los radiolist sin marcar
        el0 = _("Default")
        el1 = _("Resolution")
        # el2 = _("Screen")
        # el3 = _("Projection")
        # Generamos la lista de elemntos a mostrar a partir de los valores guardados en dconf/gsettings:
        lista_elementos = [f"{el0} {self.resolucion}"]
        ## Añadimos las opciones para la resolución 16x9. Primero la cabecera con su resolución y luego las opciones:
        lista_elementos.append(f"{el1} 16x9:vx-monitor-16_9")
        _lista_16_9 = self.gsettings_get(self.SCHEMA_BASE, None, "r16-9")
        for el in _lista_16_9:
            partes = el.split(":")
            # Traducimos la primera palabra correspondiente al modo indicado: Screen, Projection, Projection TV, ... (Screen y Projection estan definidos en vx-pantallas-conmutado.mo)
            _modo = partes[0].split(" ")
            _modo[0] = _(_modo[0])
            lista_elementos.append(" ".join(_modo) + " '" + partes[2] + "'")
        ## Añadimos las opciones para la resolución 4x3. Primero la cabecera con su resolución y luego las opciones:
        lista_elementos.append(f"{el1} 4x3:vx-monitor-4_3")
        _lista_4_3 = self.gsettings_get(self.SCHEMA_BASE, None, "r4-3")
        for el in _lista_4_3:
            partes = el.split(":")
            # Traducimos la primera palabra correspondiente al modo indicado: Screen, Projection, Projection TV, ... (Screen y Projection estan definidos en vx-pantallas-conmutado.mo)
            _modo = partes[0].split(" ")
            _modo[0] = _(_modo[0])
            lista_elementos.append(" ".join(_modo) + " '" + partes[2] + "'")

        # Inicializamos size con el valor de la resolución detecatada al comienzo:
        size = self.resolucion
        for el in lista_elementos:
            partes = el.split(":")
            if (
                len(partes) > 1
            ):  # Comprobamos si es un dato una cabecerá de los menús, con su label y su imagen
                etiqueta = partes[0]
                size = partes[0].split(" ")[1]
                print(f"#> El size ahora vale: {size}")
                imagen = partes[1]
                self.make_item_menu(etiqueta, imagen, False)
            else:
                etiqueta = partes[0]
                self.make_item_menu_radiolist(f"{etiqueta} [{size}]")

        self.menu.append(gtk.SeparatorMenuItem())

        # Añadimos un elemento para imponer la resolución nativa:
        self.make_item_menu(
            _("Native Resolution"),
            "vx-pantallas-resolucion-nativa",
            True,
            "activate",
            self.run_native_resolution,
        )

        # Añadimos un elemento para configurar las pantallas con xfce4-display-settings:
        self.make_item_menu(
            _("Configure Screens"),
            "vx-pantallas-configuracion",
            True,
            "activate",
            self.run_display_settings,
        )

        # Añadimos un elemento para configurar dconf:
        self.make_item_menu(
            _("Configure options"),
            "vx-configuracion-pantallas-conmutador",
            True,
            "activate",
            self.run_dconf,
        )

        # Añadimos un elemento para acceder a la Web de soporte:
        self.make_item_menu(
            _("Support and Incidents"), "migasfree", True, "activate", self.run_soporte
        )

        # Añadimos un elemento Checkbox para inhabilitar el resto de opciones:
        self.check_item = gtk.CheckMenuItem(label=_("Disable"))
        self.check_item.connect("toggled", self.checkbox_toggled, "Deshabilitar")
        self.menu.append(self.check_item)

        # Añadimos un elemento Acerca de:
        self.make_item_menu(
            _("About"),
            "vx-info-about-pantallas-conmutador",
            True,
            "activate",
            self.on_about,
        )
        # Añadimos un elemento para Salir:
        self.make_item_menu(_("Exit"), "vx-salir", True, "activate", self.exit_menu)

        # Comprobamos si el usuario ha indicado por defecto que estarán habilitadas o deshabilitadas los radiolist del appindicator al iniciarse:
        self.check_disable()

        # Mostramos información de los elementos del menú:
        self.imprimir_info_menu()

        # self.menu.show()
        self.menu.show_all()
        # indicator.set_menu(menu)
        self.tray.set_menu(self.menu)

    def check_disable(self):
        _estado = self.gsettings_get(self.SCHEMA_BASE, None, "disable")
        print(
            f'#> El usuario tiene configurado por defecto el comportamiento "disable": {_estado}'
        )
        if _estado:
            self.disable_radioitems()

    def disable_radioitems(self):
        for elemento in self.menu.get_children():
            if isinstance(elemento, gtk.RadioMenuItem):
                etiqueta = elemento.get_label()
                elemento.set_sensitive(False)
                print(f'El elemento "{etiqueta}": Deshabilitado - por defecto')
            # Marcamos el CheckMenuItem para indicar que se ha deshabilitado:
            elif isinstance(elemento, gtk.CheckMenuItem):
                elemento.set_active(True)

    def checkbox_toggled(self, switch, label):
        if switch.get_active():
            self.gsettings_set(self.SCHEMA_BASE, None, "disable", 1)
        else:
            self.gsettings_set(self.SCHEMA_BASE, None, "disable", 0)
        for elemento in self.menu.get_children():
            # Deshabilitamos todos los Radiolist si se ha chequeado el Checkbox:
            if isinstance(elemento, gtk.RadioMenuItem) and switch.get_active():
                etiqueta = elemento.get_label()
                elemento.set_sensitive(False)
                print(f'El elemento "{etiqueta}": Deshabilitado')
            if isinstance(elemento, gtk.RadioMenuItem) and not switch.get_active():
                etiqueta = elemento.get_label()
                elemento.set_sensitive(True)
                print(f'El elemento "{etiqueta}": Habilitado')
        estado = (
            "#> Funciones Deshabilitadas"
            if switch.get_active()
            else "#> Funciones Habilitadas"
        )
        print(f"CheckMenuItem: \"'{label}'\" cambiado a: {estado}")

    def imprimir_info_menu(self):
        menu = self.menu
        for elemento in menu.get_children():
            if isinstance(elemento, gtk.MenuItem):
                etiqueta = elemento.get_label()
                print(f"Etiqueta del elemento: {etiqueta}")
                # Si el elemento es un Gtk.RadioMenuItem, también puedes imprimir información adicional
                if isinstance(elemento, gtk.RadioMenuItem):
                    grupo = elemento.get_group()
                    print(f"Grupo del elemento (ID): {id(grupo)}")
                    # Puedes acceder a otros métodos y propiedades específicas de Gtk.RadioMenuItem aquí
                    # Por ejemplo, elemento.get_active() para verificar si está seleccionado

    def on_radio_toggled(self, widget, name):
        if widget.get_active():
            self.resolucion = name.split("'")[1]
            print(
                f"Selected: {name} - {widget.get_label()} - resolución: {self.resolucion}"
            )
            self.run_pantallas_conmutador()

    def ejecutar_comando_1(self, widget):
        self.run_command(self.CMD_COMANDO_1)

    def on_about(self, widget):
        """Signal handler: About item clicked."""
        logging.debug(".on_about()")
        dialog = gtk.AboutDialog()
        dialog.set_destroy_with_parent(True)
        dialog.set_program_name(self.APP_NAME)
        _written_by = _("Written by")
        _autores = "\n".join(__author__)
        _copyright = f"{_written_by}:\n{_autores}\n{__copyright__}"
        dialog.set_copyright(_copyright)
        dialog.set_license(self.APP_LICENCE)
        dialog.set_version(self.APP_VERSION)
        dialog.set_website(self.URL_GITLAB)
        dialog.set_website_label("Gitlab Vitalinux")
        dialog.set_icon_name(self.APP_ICON_ORANGE)
        dialog.set_logo_icon_name(self.APP_ICON_ORANGE)
        dialog.connect("response", lambda *largs: dialog.destroy())
        dialog.run()
        dialog.destroy()

    def on_settings_changed(self, settings, key):
        """Signal handler: GSettings changed."""
        print(f"GSettings {key} ha cambiado. Actualizando el menú...")
        self.update_menu()

    def update_menu(self):
        """Actualiza el menú del indicador."""
        # Elimina todos los elementos del menú actual
        self.menu.forall(lambda w: w.destroy())

        # Vuelve a crear el menú
        self.make_menu()


if __name__ == "__main__":
    # main()
    indicator = SystrayIconApp()
    gtk.main()
