#
# Copyright 2021 Ettus Research, a National Instruments Company
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
"""
X4XX GPS Manager

Handles GPS-related tasks
"""

import re

from usrp_mpm.gpsd_iface import GPSDIfaceExtension


class X4xxGPSMgr:
    """
    Manager class for GPS-related actions for the X4XX.

    This also "disables" the sensors when the GPS is not enabled.
    """

    def __init__(self, clk_aux_board, log):
        assert clk_aux_board and clk_aux_board.is_gps_supported()
        self._clocking_auxbrd = clk_aux_board
        self.log = log.getChild("GPS")
        self.log.trace("Initializing GPSd interface")
        self._gpsd = GPSDIfaceExtension()
        # To disable sensors, we simply return an empty value if GPS is disabled.
        # For TPV, SKY, and GPGGA, we can do this in the same fashion (they are
        # very similar). gps_time is different (it returns an int) so for sake
        # of simplicity it's defined separately below.
        for sensor_name in ("gps_tpv", "gps_sky", "gps_gpgga"):
            sensor_api = f"get_{sensor_name}_sensor"
            setattr(
                self,
                sensor_api,
                lambda sensor_name=sensor_name: (
                    {"name": sensor_name, "type": "STRING", "unit": "", "value": "n/a"}
                    if not self.is_gps_enabled()
                    else getattr(self._gpsd, f"get_{sensor_name}_sensor")()
                ),
            )

    def extend(self, context):
        """
        Extend 'context' with the sensor methods of this class (get_gps_*_sensor).
        If 'context' already has such a method, it is skipped.

        Returns a dictionary compatible to mboard_sensor_callback_map.
        """
        new_methods = {
            re.search(r"get_(.*)_sensor", method_name).group(1): method_name
            for method_name in dir(self)
            if not method_name.startswith("_")
            and callable(getattr(self, method_name))
            and method_name.endswith("sensor")
        }
        for method_name in new_methods.values():
            if hasattr(context, method_name):
                continue
            new_method = getattr(self, method_name)
            self.log.trace("%s: Adding %s method", context, method_name)
            setattr(context, method_name, new_method)
        return new_methods

    def is_gps_enabled(self):
        """
        Return True if the GPS is enabled/active.
        """
        return self._clocking_auxbrd.is_gps_enabled()

    def get_gps_enabled_sensor(self):
        """
        Get enabled status of GPS as a sensor dict
        """
        gps_enabled = self.is_gps_enabled()
        return {
            "name": "gps_enabled",
            "type": "BOOLEAN",
            "unit": "enabled" if gps_enabled else "disabled",
            "value": str(gps_enabled).lower(),
        }

    def get_gps_locked_sensor(self):
        """
        Get lock status of GPS as a sensor dict
        """
        gps_locked = self.is_gps_enabled() and bool(self._clocking_auxbrd.get_gps_locked())
        return {
            "name": "gps_locked",
            "type": "BOOLEAN",
            "unit": "locked" if gps_locked else "unlocked",
            "value": str(gps_locked).lower(),
        }

    def get_gps_alarm_sensor(self):
        """
        Get alarm status of GPS as a sensor dict
        """
        gps_alarm = self.is_gps_enabled() and bool(self._clocking_auxbrd.get_gps_alarm())
        return {
            "name": "gps_alarm",
            "type": "BOOLEAN",
            "unit": "active" if gps_alarm else "not active",
            "value": str(gps_alarm).lower(),
        }

    def get_gps_warmup_sensor(self):
        """
        Get warmup status of GPS as a sensor dict
        """
        gps_warmup = self.is_gps_enabled() and bool(self._clocking_auxbrd.get_gps_warmup())
        return {
            "name": "gps_warmup",
            "type": "BOOLEAN",
            "unit": "warming up" if gps_warmup else "warmup done",
            "value": str(gps_warmup).lower(),
        }

    def get_gps_survey_sensor(self):
        """
        Get survey status of GPS as a sensor dict
        """
        gps_survey = self.is_gps_enabled() and bool(self._clocking_auxbrd.get_gps_survey())
        return {
            "name": "gps_survey",
            "type": "BOOLEAN",
            "unit": "survey active" if gps_survey else "survey not active",
            "value": str(gps_survey).lower(),
        }

    def get_gps_phase_lock_sensor(self):
        """
        Get phase_lock status of GPS as a sensor dict
        """
        gps_phase_lock = self.is_gps_enabled() and bool(self._clocking_auxbrd.get_gps_phase_lock())
        return {
            "name": "gps_phase_lock",
            "type": "BOOLEAN",
            "unit": "phase locked" if gps_phase_lock else "no phase lock",
            "value": str(gps_phase_lock).lower(),
        }

    def get_gps_time_sensor(self):
        """
        Get GPS time in integer seconds as a sensor dict
        """
        if not self.is_gps_enabled():
            return {
                "name": "gps_time",
                "type": "INTEGER",
                "unit": "seconds",
                "value": str(-1),
            }
        return self._gpsd.get_gps_time_sensor()
