# X-Plane Plugin for Cessna 172 Control
# File: PI_C172TouchPortal.py
# To be placed in: X-Plane/Resources/plugins/PythonPlugins/

import xp
import socket
import json
import threading
import time
import re


class PythonInterface:
    """Classe principale requise par XPPython3"""

    def __init__(self):
        self.Name = "Xplane Cessna 172 Handler"
        self.Sig = "xplane.cessna172.handler"
        self.Desc = "Handler for Xplane Cessna 172 via Touch Portal"

        # Plugin ID for custom dataref
        self.plugin_id = "xplane_cessna172"

        # Network configuration
        self.host = '127.0.0.1'
        self.port = 5050
        self.tp_port = 5051  # Port pour envoyer les états à Touch Portal
        self.socket = None
        self.running = False

        # DataRefs for Cessna 172
        self.datarefs = {}

        # CommandRefs for Cessna 172
        self.commandrefs = {}

        # État précédent pour détecter les changements
        self.previous_states = {}

        # Flight loop callback ID
        self.flight_loop_id = None

        #Touch Portal FlightDeck License Status
        self.tp_license_status_state = 'Unactivated'
        self.tp_running_status_state = "Inactive"
        self.tp_version_status_state = "0.0.1"

        #Splash screen
        self.windowId = None
        self.splash_window = None
        self.splash_start_time = 0.0
        self.tp_plugin_name = "XP-FlightDeck Plugin for Touch Portal"

    def XPluginStart(self):
        """X-Plane plugin entry point"""
        xp.log("Cessna 172 Handler plugin started")
        self.register_custom_datarefs()
        self.setup_datarefs()
        self.setup_commandrefs()

        # Start network server
        if self.setup_network():
            # Start listening thread
            listen_thread = threading.Thread(target=self.listen_for_commands)
            listen_thread.daemon = True
            listen_thread.start()

            # Créer le flight loop pour surveiller les changements d'état
            self.flight_loop_id = xp.createFlightLoop(self.flight_loop_callback, phase=1)
            xp.scheduleFlightLoop(self.flight_loop_id, interval=-1)  # Chaque frame

            xp.log("Cessna 172 Handler plugin started successfully")
            return self.Name, self.Sig, self.Desc
        else:
            xp.log("Error starting plugin")
            return None

    def XPluginStop(self):
        """Plugin shutdown"""
        self.running = False
        if self.flight_loop_id:
            xp.destroyFlightLoop(self.flight_loop_id)

        # Déregistrer les DataRefs personnalisés
        for dataref_name, dataref_ref in self.custom_datarefs.items():
            try:
                xp.unregisterDataAccessor(dataref_ref)
                xp.log(f"Unregistered custom DataRef: {dataref_name}")
            except Exception as e:
                xp.log(f"Error unregistering DataRef {dataref_name}: {e}")

        if self.splash_window:
            xp.destroyWindow(self.splash_window)
            self.splash_window = None

        if self.socket:
            try:
                self.socket.close()
            except:
                pass
        xp.log("Cessna 172 Handler plugin stopped")



    def XPluginEnable(self):
        """Plugin activation"""
        self.create_splash()
        xp.log("[XPluginEnable] Cessna 172 Handler plugin enabled")
        return 1

    def XPluginDisable(self):
        """Plugin deactivation"""
        xp.log("Cessna 172 Handler plugin disabled")

    def XPluginReceiveMessage(self, inFromWho, inMessage, inParam):
        """X-Plane message reception"""
        pass

    @staticmethod
    def register_command_callback(commandRef, phase, refCon):

        self = refCon["self"]
        cmd_key = refCon["cmd_key"]
        cmd_custom_dataref_attached = refCon["cmd_custom_dataref_attached"]


        xp.log(f"[Register] Start Register Callback on Click in Xplane Menu: {dir(self)}")

        try:

            component = cmd_key

            if cmd_custom_dataref_attached:
                component_dataref = cmd_custom_dataref_attached
            else:
                component_dataref = None

            xp.log(f"[Register] Component to find: {component} with dataref or custom dataref: {component_dataref}")

            if component in self.commandrefs:
                cmd = self.commandrefs[component]
                if cmd:

                    xp.log(f"[Register] Success find cmd: {component} -> {cmd}")

                    if phase == xp.CommandBegin:
                        xp.log(f"[Register] Success OK CLICKED on cmd: {component} -> {cmd}")
                        current_state = self.get_dataref_value(component_dataref)
                        xp.log(f"[Register] Success OK Current State after CLICKED on cmd: {component_dataref} -> {current_state}")
                        new_state = 1 if current_state == 0 else 0
                        xp.log(f"[Register] Success OK Current State after CLICKED on cmd: {component_dataref} -> {current_state} -> {new_state}")
                        if new_state != current_state:
                            self.set_dataref_value(component_dataref, new_state)
                            xp.log(f"[Register] {component} state changed by X-Plane command: {current_state} -> {new_state}")

                else:
                    xp.log(f"[Register] Error CommandRef is None for: {component}")
            else:
                xp.log(f"[Register] Error Unknown component: {component}")
        except Exception as e:
            xp.log(f"[Register] Exception: {e}")
        return 0.0

    def loop_command_callback(self, elapsedMe, elapsedSim, counter, refcon):
        try:
            component = refcon  # str comme "cmd_sim_pause_on"
            if component in self.commandrefs:
                cmd = self.commandrefs[component]
                if cmd:
                    xp.log(f"[FlightLoop] Success Executing: {component} -> {cmd}")
                    xp.commandOnce(cmd)
                else:
                    xp.log(f"[FlightLoop] Error CommandRef is None for: {component}")
            else:
                xp.log(f"[FlightLoop] Error Unknown component: {component}")
        except Exception as e:
            xp.log(f"[FlightLoop] Exception: {e}")
        return 0.0

    def setup_commandrefs(self):
        """Initialize required CommandRefs for Cessna 172"""
        try:
            # Configuration des Commands
            commandref_config = {
                'cmd_sim_pause_off': {'path': 'sim/operation/pause_off','custom_dataref_attached': ''},
                'cmd_sim_pause_on': {'path': 'sim/operation/pause_on','custom_dataref_attached': ''},
                'cmd_sim_pause_toggle': {'path': 'sim/operation/pause_toggle','custom_dataref_attached': ''},
                'cmd_sim_quit': {'path': 'sim/operation/quit','custom_dataref_attached': ''},
                'cmd_sim_view_cockpit': {'path': 'sim/view/track_weapon','custom_dataref_attached': ''},
                'cmd_sim_view_wingman': {'path': 'sim/view/wingman','custom_dataref_attached': ''},
                'cmd_sim_map': {'path': 'sim/map/show_current','custom_dataref_attached': 'sim_map_visible'},
                'cmd_sim_atc': {'path': 'sim/operation/contact_atc','custom_dataref_attached': 'sim_atc_visible'},
                'cmd_sim_autostart': {'path': 'sim/operation/auto_start', 'custom_dataref_attached': 'sim_autostart_visible'},
            }

            # Stocker la configuration pour usage ultérieur
            self.commandref_config = commandref_config

            for key, config in commandref_config.items():
                try:
                    ref = xp.findCommand(config['path'])
                    if not ref:
                        xp.log(f"ERROR: CommandRef not found: {config['path']}")
                    else:

                        self.commandrefs[key] = ref

                        context = {"self": self,
                                   "cmd_key": key,
                                   "cmd_custom_dataref_attached":config['custom_dataref_attached']}

                        # Register of the command, in order to get the state of the custom dataref linked to that command
                        # when a user click in Xplane, not in Touch Portal, but in order to synch with TP
                        xp.registerCommandHandler(
                            ref,
                            PythonInterface.register_command_callback,
                            1,  # before = True => ton callback est appelé *avant* le comportement par défaut
                            context  # refCon (contexte) optionnel
                        )

                        xp.log(f"CommandRef initialized: {key} -> {config['path']}")

                except Exception as e:
                    xp.log(f"Error initializing CommandRef {key}: {e}")

        except Exception as e:
            xp.log(f"General error initializing Commands: {e}")

    def register_custom_datarefs(self):

        self.custom_datarefs = {}

        self.dataref_values = {
            "sim_map_visible": {
                "path": f"{self.plugin_id}/custom/map_visible",
                "value": 0
            },
            "sim_atc_visible": {
                "path": f"{self.plugin_id}/custom/atc_visible",
                "value": 0
            },
            "sim_autostart_visible": {
                "path": f"{self.plugin_id}/custom/autostart_visible",
                "value": 0
            }
        }

        for key, info in self.dataref_values.items():
            def make_getter(k=key):
                return lambda refCon: self.dataref_values[k]["value"]

            #def make_setter(k=key):
            #    return lambda value, refCon: self.set_dataref_value(k, value)
            def make_setter(k=key):
                def setter_func(value, refCon):
                    self.dataref_values[k]["value"] = int(value)
                    xp.log(f"Custom DataRef {k} set to {int(value)} via callback")
                    return None  # Les setters ne doivent rien retourner

                return setter_func

            self.custom_datarefs[info["path"]] = xp.registerDataAccessor(
                info["path"],
                xp.Type_Int,
                1,  # Writable
                make_getter(),  # Read callback
                make_setter(),  # Write callback
                None, None, None, None,  # Float, Double
                None, None, None, None,  # Arrays
                None, None, None  # Data
            )

            xp.log(f"[INFO] Registered DataRef: {info["path"]}")

    def unregister_dataref(self):
        if self.dataref:
            xp.unregisterDataAccessor(self.dataref)

    def setup_datarefs(self):
        """Initialize required DataRefs for Cessna 172"""
        try:
            # Configuration des DataRefs avec leurs indices pour les arrays
            dataref_config = {
                'beacon_lights': {'path': 'sim/cockpit/electrical/beacon_lights_on', 'is_array': False},
                'landing_lights': {'path': 'sim/cockpit/electrical/landing_lights_on', 'is_array': False},
                'taxi_lights': {'path': 'sim/cockpit/electrical/taxi_light_on', 'is_array': False},
                'nav_lights': {'path': 'sim/cockpit/electrical/nav_lights_on', 'is_array': False},
                'strobe_lights': {'path': 'sim/cockpit/electrical/strobe_lights_on', 'is_array': False},
                'fuel_pump': {'path': 'sim/cockpit/engine/fuel_pump_on', 'is_array': True, 'index': 0},
                'pitot_heat': {'path': 'sim/cockpit/switches/pitot_heat_on', 'is_array': False},
                'alternator': {'path': 'sim/cockpit/electrical/generator_on', 'is_array': True, 'index': 0},
                'battery_master': {'path': 'sim/cockpit2/electrical/battery_on', 'is_array': True, 'index': 0},
                'battery_master_standby': {'path': 'sim/cockpit2/electrical/battery_on', 'is_array': True, 'index': 1},
                'avionics': {'path': 'sim/cockpit/electrical/avionics_on', 'is_array': False},
                'mixture': {'path': 'sim/cockpit2/engine/actuators/mixture_ratio', 'is_array': True, 'index': 0, 'is_float': True},
                'propeller': {'path': 'sim/cockpit2/engine/actuators/prop_ratio', 'is_array': True, 'index': 0, 'is_float': True},
                'magnetos': {'path': 'sim/cockpit2/engine/actuators/ignition_key', 'is_array': True, 'index': 0},
                'parking_brake': {'path': 'sim/flightmodel/controls/parkbrake','is_array': False, 'index': 0, 'is_float': True},
                'flap': {'path': 'sim/cockpit2/controls/flap_handle_request_ratio', 'is_array': False, 'index': 0, 'is_float': True},
                'sim_pause': {'path': 'sim/time/paused', 'is_array': False, 'index': 0,'is_float': False},
                'sim_views': {'path': 'sim/graphics/view/view_type', 'is_array': False, 'index': 0,'is_float': False},
                'sim_map_visible': {'path': f"{self.plugin_id}/custom/map_visible", 'is_array': False, 'index': 0, 'is_float': False},
                'sim_atc_visible': {'path': f"{self.plugin_id}/custom/atc_visible", 'is_array': False, 'index': 0, 'is_float': False},
                'sim_autostart_visible': {'path': f"{self.plugin_id}/custom/autostart_visible", 'is_array': False, 'index': 0, 'is_float': False},
                'sim_autostop_visible': {'path': 'sim/cockpit2/engine/actuators/mixture_ratio_all', 'is_array': False,'index': 0, 'is_float': True},
                'engine_running': {'path': 'sim/flightmodel/engine/ENGN_running', 'is_array': True, 'index': 0, 'is_float': False},
                'landing_gear': {'path': "sim/cockpit/switches/gear_handle_status", 'is_array': False, 'index': 0,'is_float': False}

            }

            # Stocker la configuration pour usage ultérieur
            self.dataref_config = dataref_config

            for key, config in dataref_config.items():
                try:
                    self.datarefs[key] = xp.findDataRef(config['path'])
                    if self.datarefs[key] is None:
                        xp.log(f"ERROR: DataRef not found: {config['path']}")
                    else:
                        array_info = f" (array[{config['index']}])" if config['is_array'] else ""
                        float_info = " (float)" if config.get('is_float', False) else ""
                        xp.log(f"DataRef initialized: {key} -> {config['path']}{array_info}{float_info}")
                        # Initialiser l'état précédent
                        self.previous_states[key] = None
                except Exception as e:
                    xp.log(f"Error initializing DataRef {key}: {e}")

        except Exception as e:
            xp.log(f"General error initializing DataRefs: {e}")

    def get_dataref_value(self, component):
        """Get value from DataRef (handles both single values and arrays)"""
        if not self.datarefs.get(component) or component not in self.dataref_config:
            xp.log(f"Error reading DataRef from get_dataref_value {component}")
            return 0

        config = self.dataref_config[component]
        is_float = config.get('is_float', False)
        #xp.log(f"Success reading DataRef from get_dataref_value {component}")

        # Vérifier si c'est un DataRef personnalisé
        dataref_path = config['path']
        is_custom_dataref = dataref_path.startswith(self.plugin_id)

        try:
            if is_custom_dataref:
                # Pour les DataRefs personnalisés, lire directement depuis notre dictionnaire
                if component in self.dataref_values:
                    return self.dataref_values[component]["value"]
                else:
                    xp.log(f"Custom DataRef {component} not found in dataref_values")
                    return 0

            elif config['is_array']:
                # Array DataRef
                values = []
                index = config['index']

                if is_float:
                    count = xp.getDatavf(self.datarefs[component], values, 0, 16)
                else:
                    count = xp.getDatavi(self.datarefs[component], values, 0, 16)


                if count > index and len(values) > index:
                    return values[index]
                return 0.0 if is_float else 0
            else:
                if is_float:
                    return xp.getDataf(self.datarefs[component])
                else:
                    return xp.getDatai(self.datarefs[component])

        except Exception as e:
            xp.log(f"Error reading DataRef {component}: {e}")
            return 0.0 if config.get('is_float', False) else 0

    def set_dataref_value(self, component, value):
        """Set value to DataRef (handles both single values and arrays, int or float)"""
        if not self.datarefs.get(component) or component not in self.dataref_config:
            xp.log(f"Error reading DataRef from set_dataref_value {component}")
            return False

        config = self.dataref_config[component]
        is_array = config.get('is_array', False)
        is_float = config.get('is_float', False)

        # Vérifier si c'est un DataRef personnalisé
        dataref_path = config['path']
        xp.log(f"Custom DataRef {component} found for path {dataref_path}")
        is_custom_dataref = dataref_path.startswith(self.plugin_id)

        try:
            if is_custom_dataref:
                # Pour les DataRefs personnalisés, utiliser directement la fonction de callback
                if component in self.dataref_values:
                    self.dataref_values[component]["value"] = int(value) if not is_float else float(value)
                    xp.log(f"Custom DataRef {component} set to {value}")
                    return True
                else:
                    xp.log(f"Custom DataRef {component} not found in dataref_values")
                    return False

            elif is_array:
                values = []
                index = config['index']

                if is_float:
                    count = xp.getDatavf(self.datarefs[component], values, 0, 16)
                else:
                    count = xp.getDatavi(self.datarefs[component], values, 0, 16)

                # Étendre si nécessaire
                if count <= index or len(values) <= index:
                    default_value = 0.0 if is_float else 0
                    values.extend([default_value] * (index + 1 - len(values)))

                values[index] = float(value) if is_float else int(value)

                if is_float:
                    xp.setDatavf(self.datarefs[component], values, 0, len(values))
                else:
                    xp.setDatavi(self.datarefs[component], values, 0, len(values))

            else:
                # Cas simple : valeur unique
                if is_float:
                    xp.setDataf(self.datarefs[component], float(value))
                else:
                    xp.setDatai(self.datarefs[component], int(value))

            return True

        except Exception as e:
            xp.log(f"Error setting DataRef {component} for value {int(value)} where dataref {self.datarefs[component]}: {e}")
            return False

    def setup_network(self):
        """Initialize UDP server to receive commands from Touch Portal"""
        try:
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.socket.bind((self.host, self.port))
            self.socket.settimeout(1.0)
            self.running = True
            xp.log(f"UDP server started on {self.host}:{self.port}")
            return True
        except Exception as e:
            xp.log(f"Error initializing UDP server: {e}")
            return False

    def flight_loop_callback(self, elapsedMe, elapsedSim, counter, refcon):
        """Flight loop callback pour surveiller les changements d'état"""
        try:
            # Liste des composants à surveiller
            components_to_monitor = ['beacon_lights', 'landing_lights', 'taxi_lights', 'nav_lights', 'strobe_lights',
                                     'fuel_pump', 'pitot_heat', 'magnetos', 'battery_master','battery_master_standby',
                                     'alternator','avionics','parking_brake','flap','sim_pause','sim_views',
                                     'sim_map_visible','sim_atc_visible','landing_gear','sim_autostart_visible',
                                     'sim_autostop_visible','engine_running']

            for component in components_to_monitor:

                if self.datarefs.get(component):
                    current_state = self.get_dataref_value(component)
                    #xp.log(f"Loop back component getDatafref from component {component} current state: {current_state}")
                    previous_state = self.previous_states.get(component)
                    #xp.log(f"Loop back component getDatafref from component {component} previous state: {previous_state}")

                    if previous_state is None or current_state != previous_state:
                        self.previous_states[component] = current_state

                        if isinstance(current_state, float):
                            # Cas 1 : interrupteur en float (0.0 ou 1.0)
                            if current_state in (0.0, 1.0):
                                state_value = int(current_state)
                                xp.log(f"{component} cas 1: {state_value}")
                            # Cas 2 : valeur float continue (ex : mixture, parking_brake)
                            else:
                                state_value = round(current_state, 2)  # on garde le float avec 2 décimales
                                xp.log(f"{component} cas 2: {state_value}")
                        elif isinstance(current_state, int):
                            # Cas 3: déjà un int, on le garde tel quel
                            state_value = current_state
                            xp.log(f"{component} cas 3: {state_value}")
                        else:
                            # Cas 4 : autre type (ex : string ou None), on laisse tel quel
                            state_value = current_state
                            xp.log(f"{component} cas 4: {state_value}")

                        self.send_state_to_touchportal(component, state_value)
                        xp.log(f"{component} state changed current: {current_state}")
                        xp.log(f"{component} state changed sent to Touch Portal: {state_value}")
        except Exception as e:
            xp.log(f"Error in flight loop callback: {e}")

        return -1  # Continue le flight loop

    def send_state_to_touchportal(self, component, state):
        """Envoyer l'état d'un composant vers Touch Portal"""
        try:
            message = {
                "type": "state_update",
                "component": component,
                "state": state
            }

            # Créer un socket UDP temporaire pour envoyer vers Touch Portal
            temp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            temp_socket.sendto(json.dumps(message).encode('utf-8'), (self.host, self.tp_port))
            temp_socket.close()

            xp.log(f"State sent to Touch Portal: {component} = {state}")
        except Exception as e:
            xp.log(f"Error sending state to Touch Portal: {e}")

    def listen_for_commands(self):
        """Listen for UDP commands in background"""
        while self.running:
            try:
                if self.socket:
                    data, addr = self.socket.recvfrom(1024)
                    xp.log(f"Data from Touch Portal: {data}")
                    xp.log(f"Data from Touch Portal: {data.decode('utf-8')}")
                    try:
                        message = json.loads(data.decode('utf-8'))
                        command = message.get('command', '')
                        license_status = message.get('license_status', '')
                        running_status = message.get('running_status', '')
                        version_status = message.get('version_status', '')
                        if command:
                            self.execute_command(command)
                        command = message.get('command', '')
                        if license_status:
                            self.tp_license_status_state = license_status
                        if running_status:
                            self.tp_running_status_state = running_status
                        if version_status:
                            self.tp_version_status_state = version_status
                    except json.JSONDecodeError:
                        xp.log("Error: Invalid JSON message received")
            except socket.timeout:
                continue
            except Exception as e:
                if self.running:  # Only log if not shutting down
                    xp.log(f"Error listening UDP: {e}")

    def execute_command(self, command):
        """Execute a Touch Portal command in Xplane"""
        xp.log(f"Executing a Touch Portal command in Xplane: {command}")

        try:

            ####################################
            ##LIGHTS        ####################
            if command == "beacon_lights_on":
                self.set_beacon_lights(True)

            elif command == "beacon_lights_off":
                self.set_beacon_lights(False)

            elif command == "beacon_lights_toggle":
                self.toggle_beacon_lights()

            elif command == "landing_lights_on":
                self.set_landing_lights(True)

            elif command == "landing_lights_off":
                self.set_landing_lights(False)

            elif command == "landing_lights_toggle":
                self.toggle_landing_lights()

            elif command == "taxi_lights_on":
                self.set_taxi_lights(True)

            elif command == "taxi_lights_off":
                self.set_taxi_lights(False)

            elif command == "taxi_lights_toggle":
                self.toggle_taxi_lights()

            elif command == "nav_lights_on":
                self.set_nav_lights(True)

            elif command == "nav_lights_off":
                self.set_nav_lights(False)

            elif command == "nav_lights_toggle":
                self.toggle_nav_lights()

            elif command == "strobe_lights_on":
                self.set_strobe_lights(True)

            elif command == "strobe_lights_off":
                self.set_strobe_lights(False)

            elif command == "strobe_lights_toggle":
                self.toggle_strobe_lights()

            ####################################
            ##FUEL AND HEAT ####################
            elif command == "fuel_pump_on":
                self.set_fuel_pump(True)

            elif command == "fuel_pump_off":
                self.set_fuel_pump(False)

            elif command == "fuel_pump_toggle":
                self.toggle_fuel_pump()

            elif command == "pitot_heat_on":
                self.set_pitot_heat(True)

            elif command == "pitot_heat_off":
                self.set_pitot_heat(False)

            elif command == "pitot_heat_toggle":
                self.toggle_pitot_heat()

            ####################################
            ##BAT/GEN       ####################

            elif command == "battery_master_on":
                self.set_battery_master(True)

            elif command == "battery_master_off":
                self.set_battery_master(False)

            elif command == "battery_master_standby_on":
                self.set_battery_master_standby(True)

            elif command == "battery_master_standby_off":
                self.set_battery_master_standby(False)

            elif command == "alternator_on":
                self.set_alternator(True)

            elif command == "alternator_off":
                self.set_alternator(False)

            elif command == "avionics_on":
                self.set_avionics(True)

            elif command == "avionics_off":
                self.set_avionics(False)

            ####################################
            ##MAGNETOS/PROP/MIXT  ##############

            elif command == "mixture_rich":
                self.set_mixture_rich()

            elif command == "propeller_high_rpm":
                self.set_propeller_high()

            elif command == "magnetos_off":
                self.set_magnetos_off()

            elif command == "magnetos_left":
                self.set_magnetos_left()

            elif command == "magnetos_right":
                self.set_magnetos_right()

            elif command == "magnetos_both":
                self.set_magnetos_both()

            elif command == "magnetos_start":
                self.set_magnetos_start()

            ####################################
            ##BRAKES / LANDING GEAR  ############
            elif command == "parking_brake_on":
                self.set_parking_brake(True)

            elif command == "parking_brake_off":
                self.set_parking_brake(False)

            elif command == "landing_gear_on":
                self.set_landing_gear(True)

            elif command == "landing_gear_off":
                self.set_landing_gear(False)

            ####################################
            ##DIRECTION           ##############
            elif command.startswith("flap__"):
                # Extraire la valeur float depuis la chaîne, exemple "flap__0_33" → 0.33
                match = re.match(r"flap__([0-9]+)_([0-9]+)", command)
                if match:
                    integer_part = match.group(1)
                    decimal_part = match.group(2)
                    flap_value = float(f"{integer_part}.{decimal_part}")
                    self.set_flap(flap_value)

            ####################################
            ##SIMUL:pause         ##############
            elif command == "sim_pause_on":
                self.set_sim_pause(True)

            elif command == "sim_pause_off":
                self.set_sim_pause(False)

            elif command == "cmd_sim_pause_on":
                self.set_cmd_sim_pause('cmd_sim_pause_on')

            elif command == "cmd_sim_pause_off":
                self.set_cmd_sim_pause('cmd_sim_pause_off')

            ####################################
            ##SIMUL:views, map, atc   ##########
            elif command == "cmd_sim_views_on":
                self.set_cmd_sim_views('cmd_sim_view_cockpit')

            elif command == "cmd_sim_views_off":
                self.set_cmd_sim_views('cmd_sim_view_wingman')

            elif command == "cmd_sim_map_on":
                self.set_cmd_sim_map('cmd_sim_map',True)

            elif command == "cmd_sim_map_off":
                self.set_cmd_sim_map('cmd_sim_map',False)

            elif command == "cmd_sim_atc_on":
                self.set_cmd_sim_atc('cmd_sim_atc',True)

            elif command == "cmd_sim_atc_off":
                self.set_cmd_sim_atc('cmd_sim_atc', False)

            elif command == "cmd_sim_autostart_on":
                self.set_cmd_sim_autostart('cmd_sim_autostart',True)

            elif command == "cmd_sim_autostart_off":
                self.set_cmd_sim_autostart('cmd_sim_autostart', False)

            elif command == "cmd_sim_autostop_on":
                self.set_cmd_sim_autostop(True)

            elif command == "cmd_sim_autostop_off":
                self.set_cmd_sim_autostop(False)

            else:
                xp.log(f"Unknown command: {command}")

        except Exception as e:
            xp.log(f"Error executing command {command}: {e}")

    #############################################
    ### CESSNA XPLANE LIGHTS ####################
    def set_beacon_lights(self, state):
        """Enable/disable beacon lights"""
        if self.set_dataref_value('beacon_lights', 1 if state else 0):
            xp.log(f"Beacon Lights: {'ON' if state else 'OFF'}")

    def toggle_beacon_lights(self):
        """Toggle beacon lights state"""
        current_state = self.get_dataref_value('beacon_lights')
        new_state = 0 if current_state else 1
        if self.set_dataref_value('beacon_lights', new_state):
            xp.log(f"Beacon Lights toggled: {'ON' if new_state else 'OFF'}")

    def set_landing_lights(self, state):
        """Enable/disable landing lights"""
        if self.set_dataref_value('landing_lights', 1 if state else 0):
            xp.log(f"Landing Lights: {'ON' if state else 'OFF'}")

    def toggle_landing_lights(self):
        """Toggle landing lights state"""
        current_state = self.get_dataref_value('landing_lights')
        new_state = 0 if current_state else 1
        if self.set_dataref_value('landing_lights', new_state):
            xp.log(f"Landing Lights toggled: {'ON' if new_state else 'OFF'}")

    def set_taxi_lights(self, state):
        """Enable/disable taxi lights"""
        if self.set_dataref_value('taxi_lights', 1 if state else 0):
            xp.log(f"Taxi Lights: {'ON' if state else 'OFF'}")

    def toggle_taxi_lights(self):
        """Toggle taxi lights state"""
        current_state = self.get_dataref_value('taxi_lights')
        new_state = 0 if current_state else 1
        if self.set_dataref_value('taxi_lights', new_state):
            xp.log(f"Taxi Lights toggled: {'ON' if new_state else 'OFF'}")

    def set_nav_lights(self, state):
        """Enable/disable navigation lights"""
        if self.set_dataref_value('nav_lights', 1 if state else 0):
            xp.log(f"Navigation Lights: {'ON' if state else 'OFF'}")

    def toggle_nav_lights(self):
        """Toggle navigation lights state"""
        current_state = self.get_dataref_value('nav_lights')
        new_state = 0 if current_state else 1
        if self.set_dataref_value('nav_lights', new_state):
            xp.log(f"Nav Lights toggled: {'ON' if new_state else 'OFF'}")

    def set_strobe_lights(self, state):
        """Enable/disable strobe lights"""
        if self.set_dataref_value('strobe_lights', 1 if state else 0):
            xp.log(f"Strobe Lights: {'ON' if state else 'OFF'}")

    def toggle_strobe_lights(self):
        """Toggle strobe lights state"""
        current_state = self.get_dataref_value('strobe_lights')
        new_state = 0 if current_state else 1
        if self.set_dataref_value('strobe_lights', new_state):
            xp.log(f"Strobe Lights toggled: {'ON' if new_state else 'OFF'}")

    #############################################
    ### CESSNA XPLANE BAT/GEN/AVIONICS ##########

    def set_battery_master(self, state):
        """Enable/disable master battery"""
        if self.set_dataref_value('battery_master', 1 if state else 0):
            xp.log(f"Battery Master: {'ON' if state else 'OFF'}")

    def set_battery_master_standby(self, state):
        """Enable/disable master standby battery"""
        if self.set_dataref_value('battery_master_standby', 1 if state else 0):
            xp.log(f"Battery Master StandBy: {'ON' if state else 'OFF'}")

    def set_alternator(self, state):
        """Enable/disable alternator"""
        if self.set_dataref_value('alternator', 1 if state else 0):
            xp.log(f"Alternator: {'ON' if state else 'OFF'}")

    def set_avionics(self, state):
        """Enable/disable avionics"""
        if self.set_dataref_value('avionics', 1 if state else 0):
            xp.log(f"Avionics: {'ON' if state else 'OFF'}")

    #############################################
    ### CESSNA XPLANE HEAT/FUEL      ############

    def set_fuel_pump(self, state):
        """Enable/disable fuel pump"""
        if self.set_dataref_value('fuel_pump', 1 if state else 0):
            xp.log(f"Fuel Pump: {'ON' if state else 'OFF'}")

    def toggle_fuel_pump(self):
        """Toggle fuel pump state"""
        current_state = self.get_dataref_value('fuel_pump')
        new_state = 0 if current_state else 1
        if self.set_dataref_value('fuel_pump', new_state):
            xp.log(f"Fuel Pump toggled: {'ON' if new_state else 'OFF'}")

    def set_pitot_heat(self, state):
        """Enable/disable pitot heat"""
        if self.set_dataref_value('pitot_heat', 1 if state else 0):
            xp.log(f"Pitot Heat: {'ON' if state else 'OFF'}")

    def toggle_pitot_heat(self):
        """Toggle pitot heat state"""
        current_state = self.get_dataref_value('pitot_heat')
        new_state = 0 if current_state else 1
        if self.set_dataref_value('pitot_heat', new_state):
            xp.log(f"Pitot Heat toggled: {'ON' if new_state else 'OFF'}")

    #############################################
    ### CESSNA XPLANE MAGN/MIXT/PROP ############

    def set_magnetos_off(self):
        """Desactivate both magnetos (position 0 = OFF)"""
        if self.set_dataref_value('magnetos', 0):
            xp.log("Magnetos: OFF")

    def set_magnetos_right(self):
        """Desactivate both magnetos (position 1 = RIGHT)"""
        if self.set_dataref_value('magnetos', 1):
            xp.log("Magnetos: RIGHT")

    def set_magnetos_left(self):
        """Desactivate both magnetos (position 2 = LEFT)"""
        if self.set_dataref_value('magnetos', 2):
            xp.log("Magnetos: LEFT")

    def set_magnetos_both(self):
        """Activate both magnetos (position 3 = BOTH)"""
        if self.set_dataref_value('magnetos', 3):
            xp.log("Magnetos: BOTH")

    def set_magnetos_start(self):
        """Activate both magnetos (position 4 = START)"""
        if self.set_dataref_value('magnetos', 4):
            xp.log("Magnetos: START")

    def set_mixture_rich(self):
        """Set mixture to rich position (1.0)"""
        if self.set_dataref_value('mixture', 1.0):
            xp.log("Mixture: RICH")

    def set_propeller_high(self):
        """Set propeller to high speed"""
        if self.set_dataref_value('propeller', 1.0):
            xp.log("Propeller: HIGH RPM")

    #############################################
    ### CESSNA BRAKES / LANDING GEAR   ##########
    def set_parking_brake(self, state):
        """Enable/disable parking brake"""
        if self.set_dataref_value('parking_brake', 1.0 if state else 0.0):
            xp.log(f"Parking Brake: {'ON' if state else 'OFF'}")
        else:
            xp.log(f"Parking Brake: failed")

    def set_landing_gear(self, state):
        """Enable/disable Landing Gear"""
        if self.set_dataref_value('landing_gear', 1 if state else 0):
            xp.log(f"Landing Gear: {'ON' if state else 'OFF'}")

    #############################################
    ### CESSNA DIRECTION ##########

    def set_flap(self, state):
        """Set flap to a specific ratio between 0.0 and 1.0"""
        ratio = max(0.0, min(1.0, float(state)))  # clamp entre 0.0 et 1.0
        round_ratio = round(ratio, 2)
        if self.set_dataref_value("flap", round_ratio):
            xp.log(f"Flap set to: {round_ratio}")
        else:
            xp.log("Flap: set failed")

    #############################################
    ### SIM: pause ##########
    def set_cmd_sim_pause(self, cmd):
        """Enable/Disable sim cmd pause"""
        xp.registerFlightLoopCallback(self.loop_command_callback, 0.01, cmd)


    #############################################
    ### SIM: views ##########
    def set_cmd_sim_views(self, cmd):
        """Change views of sim"""
        xp.registerFlightLoopCallback(self.loop_command_callback, 0.01, cmd)

    def set_cmd_sim_map(self, cmd, state):
        """Enable/Disable sim cmd map"""
        xp.registerFlightLoopCallback(self.loop_command_callback, 0.01, cmd)

    def set_cmd_sim_atc(self, cmd, state):
        """Enable/Disable sim cmd ATC"""
        xp.registerFlightLoopCallback(self.loop_command_callback, 0.01, cmd)

    def set_cmd_sim_autostart(self, cmd, state):
        """Enable/Disable sim cmd Engine Autostart"""
        xp.registerFlightLoopCallback(self.loop_command_callback, 0.01, cmd)
        if self.set_dataref_value('engine_running', 1 if state else 0):
            xp.log(f"Fuel Pump: {'ON' if state else 'OFF'}")

    def set_cmd_sim_autostop(self, state):
        """Enable/disable Engine Autostop via mixture OFF"""
        if self.set_dataref_value('sim_autostop_visible', 1.0 if state else 0.0):
            xp.log(f"Autostop Engine via Mixture: {'ON' if state else 'OFF'}")

    #############################################
    ### UI WINDOW ##########
    def draw_splash(self, inWindowID, inRefcon):

        (left, top, right, bottom) = xp.getWindowGeometry(inWindowID)
        xp.drawTranslucentDarkBox(left, top, right, bottom)
        white = (1.0, 1.0, 1.0)
        red = (1.0, 0.0, 0.0)
        green = (0.0, 1.0, 0.0)
        blue = (0.0, 0.0, 1.0)
        yellow = (1.0, 1.0, 0.0)
        cyan = (0.0, 1.0, 1.0)
        magenta = (1.0, 0.0, 1.0)
        gray = (0.5, 0.5, 0.5)
        orange = (1.0, 0.5, 0.0)

        # license status value
        if self.tp_running_status_state.lower() == "running":
            status_running_color = green
        else:
            status_running_color = yellow

        if self.tp_license_status_state.lower() == "activated":
            status_license_color = green
        else:
            status_license_color = yellow

        xp.drawString(white, left + 10,  top - 20,
                      f"FlightDeck plugin:", 0, xp.Font_Basic)

        xp.drawString(green, left + 10, top - 40,
                      "Installed", None, xp.Font_Basic)

        xp.drawString(white, left + 10,  top - 60,
                      f"Touch Portal status:", 0, xp.Font_Basic)

        xp.drawString(status_running_color, left + 10, top - 80,
                      self.tp_running_status_state, None, xp.Font_Basic)

        # always white for prefix
        prefix = "Your Touch Portal FlightDeck plugin License status:"
        xp.drawString((1, 1, 1), left + 10, top - 100,
                      prefix, None, xp.Font_Basic)

        xp.drawString(status_license_color, left + 10, top - 120,
                      self.tp_license_status_state, None, xp.Font_Basic)

        xp.drawString(white, left + 10,  top - 140,
                      f"Version: {self.tp_version_status_state}", 0, xp.Font_Basic)


    def splash_loop_cb(self, elapsedMe, elapsedSim, counter, refcon):

        #xp.log(f"SPLASH LOOP CB lanched OK - xp.getElapsedTime: {xp.getElapsedTime()}")
        #xp.log(f"SPLASH LOOP CB lanched OK - self.splash_start_time: {self.splash_start_time}")
        #xp.log(f"SPLASH LOOP CB lanched OK - diff: {xp.getElapsedTime() - self.splash_start_time}")

        if xp.getElapsedTime() - self.splash_start_time > 25.0:
            if self.splash_window is not None:
                #xp.log(f"SPLASH LOOP CB lanched OK - DESTROY WINDOW : {elapsedMe}")
                #xp.destroyWindow(self.splash_window)
                self.splash_window = None
                #xp.log(f"SPLASH LOOP CB lanched OK - DESTROY WINDOW : {self.splash_window}")
            return 0.0  # stop callback
        return -1      # continue

    def create_splash(self):
        if self.splash_window is not None:
            #xp.log("SPLASH window already created")
            return

        window_info = (200, 680, 600, 520, 2,
                      self.draw_splash,
                      None,
                      None,
                      None,
                      None,
                      0,
                      xp.WindowDecorationRoundRectangle,
                      xp.WindowLayerFloatingWindows,
                      None)
        self.splash_window = xp.createWindowEx(window_info)
        xp.setWindowTitle(self.splash_window, f"{self.tp_plugin_name}")
        self.splash_start_time = xp.getElapsedTime()
        xp.registerFlightLoopCallback(self.splash_loop_cb, -1, 0)







