#! /usr/bin/env python
__author__ = "Ben Raanan"
__copyright__ = "Copyright 2021, MBARI"
__credits__ = ["MBARI"]
__license__ = "GPL-3.0"
__maintainer__ = "Ben Raanan"
__email__ = "byraanan at mbari.org"
__doc__ = '''

Defines an observer class for monitoring LCM data requests as part of the LRAUV backseat helper service.

@author: __author__
@license: __license__
'''
import time

from LCM.HandlerBase import LcmHandlerBase


class LcmDataReq(LcmHandlerBase):
    """
    Monitors Tethys LRAUV data requests for specified variable name.

    NOTE: Tethys LRAUV data URIs use the following convention:

        * regular data URI:
            - name: <component>.<variable>
            - LCM channel: <component>

        * universal data URI:
            - name: <variable>
            - LCM channel: "Universal"
    """
    def __init__(self, lcm_instance, name: str, channel=""):
        self.lc = lcm_instance
        self.channel = channel
        self.name = name
        self.rx_time = -1

        if not self.channel:
            # infer the channel name from name
            self.channel = name.split('.', 1)[0] if '.' in name else "Universal"

        self.subscription = self.subscribe()


    def __del__(self):
        """
        Remove callback subscription upon object deletion.

        :return: void
        """
        self.unsubscribe()


    def __eq__(self, other):
        return self.name == other.name


    def __hash__(self):
        return hash(self.name)


    def data_req_callback(self, channel, data):
        """
        Reset the request timer.

        :param channel: LCM channel name string (required input by LCM)
        :param data: coded LCM packet (required input by LCM)
        :return: void
        """
        self.rx_time = time.time()


    def universal_data_req_callback(self, channel, data):
        """
        Specialized callback for universal data variables. Resets the request timer.

        :param channel: LCM channel name string (required input by LCM)
        :param data: coded LCM packet (required input by LCM)
        :return: void
        """
        msg = self.decode(data)
        # only reset the timer if the universal var name is found
        # in the incoming LCM msg
        if self.name in self.get_item_names(msg):
            self.rx_time = time.time()


    def subscribe(self):
        """
        Register the callback to trigger when messages are received on the variable's
        channel.

        :return: LCMSubscription class
        """
        if self.channel == "Universal":
            return self.lc.subscribe(self.channel, self.universal_data_req_callback)

        return self.lc.subscribe(self.channel, self.data_req_callback)


    def unsubscribe(self):
        """
        Remove callback trigger and suspends the timer.
        :return: void
        """
        self.lc.unsubscribe(self.subscription)
        self.rx_time = -1


    def needs_request(self, rx_data_timeout=60.0):
        """
        Indicates whether data request should be issued.

        Data requests shall be made when the timespan since last rx
        exceeds rx_data_timeout.

        :param rx_data_timeout: max time since last message was received.
        :return: True when a data request should be issued.
        """
        if(time.time() - self.rx_time) > rx_data_timeout:
            # reset the clock
            self.rx_time = time.time()
            return True

        return False
