# validated: 2017-10-01 DS e4a8bff70e77 cpp/EntryNotifier.cpp cpp/EntryNotifier.h cpp/IEntryNotifier.h
"""----------------------------------------------------------------------------"""
""" Copyright (c) FIRST 2017. All Rights Reserved.                             """
""" Open Source Software - may be modified and shared by FRC teams. The code   """
""" must be accompanied by the FIRST BSD license file in the root directory of """
""" the project.                                                               """
"""----------------------------------------------------------------------------"""

from collections import namedtuple

from .callback_manager import CallbackManager, CallbackThread

from .constants import (
    NT_NOTIFY_IMMEDIATE,
    NT_NOTIFY_LOCAL,
    NT_NOTIFY_UPDATE,
    NT_NOTIFY_FLAGS,
)


_EntryListenerData = namedtuple(
    "EntryListenerData",
    [
        "prefix",
        "local_id",  # we don't have entry handles like ntcore has
        "flags",
        "callback",
        "poller_uid",
    ],
)

#
_EntryNotification = namedtuple(
    "EntryNotification", ["name", "value", "flags", "local_id"]
)

_assign_both = NT_NOTIFY_UPDATE | NT_NOTIFY_FLAGS
_immediate_local = NT_NOTIFY_IMMEDIATE | NT_NOTIFY_LOCAL


class EntryNotifierThread(CallbackThread):
    def __init__(self):
        CallbackThread.__init__(self, "entry-notifier")

    def matches(self, listener, data):
        if not data.value:
            return False

        # Flags must be within requested flag set for this listener.
        # Because assign messages can result in both a value and flags update,
        # we handle that case specially.
        listen_flags = listener.flags & ~_immediate_local
        flags = data.flags & ~_immediate_local

        if (flags & _assign_both) == _assign_both:
            if (listen_flags & _assign_both) == 0:
                return False
            listen_flags &= ~_assign_both
            flags &= ~_assign_both

        if (flags & ~listen_flags) != 0:
            return False

        # must match local id or prefix
        if listener.local_id is not None:
            if listener.local_id != data.local_id:
                return False
        else:
            if not data.name.startswith(listener.prefix):
                return False

        return True

    def setListener(self, data, listener_uid):
        pass

    def doCallback(self, callback, data):
        callback(data)


class EntryNotifier(CallbackManager):

    THREAD_CLASS = EntryNotifierThread

    def __init__(self, verbose):
        CallbackManager.__init__(self, verbose)

        self.m_local_notifiers = False

    def add(self, callback, prefix, flags):
        if (flags & NT_NOTIFY_LOCAL) != 0:
            self.m_local_notifiers = True
        return self.doAdd(_EntryListenerData(prefix, None, flags, callback, None))

    def addById(self, callback, local_id, flags):
        if (flags & NT_NOTIFY_LOCAL) != 0:
            self.m_local_notifiers = True
        return self.doAdd(_EntryListenerData(None, local_id, flags, callback, None))

    def addPolled(self, poller_uid, prefix, flags):
        if (flags & NT_NOTIFY_LOCAL) != 0:
            self.m_local_notifiers = True
        return self.doAdd(_EntryListenerData(prefix, None, flags, None, poller_uid))

    def addPolledById(self, poller_uid, local_id, flags):
        if (flags & NT_NOTIFY_LOCAL) != 0:
            self.m_local_notifiers = True
        return self.doAdd(_EntryListenerData(None, local_id, flags, None, poller_uid))

    def notifyEntry(self, local_id, name, value, flags, only_listener=None):

        # optimization: don't generate needless local queue entries if we have
        # no local listeners (as this is a common case on the server side)
        if not self.m_local_notifiers and (flags & NT_NOTIFY_LOCAL) != 0:
            return

        self.send(only_listener, _EntryNotification(name, value, flags, local_id))

    def start(self):
        CallbackManager.start(self)

# validated: 2017-10-01 DS e4a8bff70e77 cpp/EntryNotifier.cpp cpp/EntryNotifier.h cpp/IEntryNotifier.h
"""----------------------------------------------------------------------------"""
""" Copyright (c) FIRST 2017. All Rights Reserved.                             """
""" Open Source Software - may be modified and shared by FRC teams. The code   """
""" must be accompanied by the FIRST BSD license file in the root directory of """
""" the project.                                                               """
"""----------------------------------------------------------------------------"""

from collections import namedtuple

from .callback_manager import CallbackManager, CallbackThread

from .constants import (
    NT_NOTIFY_IMMEDIATE,
    NT_NOTIFY_LOCAL,
    NT_NOTIFY_UPDATE,
    NT_NOTIFY_FLAGS,
)


_EntryListenerData = namedtuple(
    "EntryListenerData",
    [
        "prefix",
        "local_id",  # we don't have entry handles like ntcore has
        "flags",
        "callback",
        "poller_uid",
    ],
)

#
_EntryNotification = namedtuple(
    "EntryNotification", ["name", "value", "flags", "local_id"]
)

_assign_both = NT_NOTIFY_UPDATE | NT_NOTIFY_FLAGS
_immediate_local = NT_NOTIFY_IMMEDIATE | NT_NOTIFY_LOCAL


class EntryNotifierThread(CallbackThread):
    def __init__(self):
        CallbackThread.__init__(self, "entry-notifier")

    def matches(self, listener, data):
        if not data.value:
            return False

        # Flags must be within requested flag set for this listener.
        # Because assign messages can result in both a value and flags update,
        # we handle that case specially.
        listen_flags = listener.flags & ~_immediate_local
        flags = data.flags & ~_immediate_local

        if (flags & _assign_both) == _assign_both:
            if (listen_flags & _assign_both) == 0:
                return False
            listen_flags &= ~_assign_both
            flags &= ~_assign_both

        if (flags & ~listen_flags) != 0:
            return False

        # must match local id or prefix
        if listener.local_id is not None:
            if listener.local_id != data.local_id:
                return False
        else:
            if not data.name.startswith(listener.prefix):
                return False

        return True

    def setListener(self, data, listener_uid):
        pass

    def doCallback(self, callback, data):
        callback(data)


class EntryNotifier(CallbackManager):

    THREAD_CLASS = EntryNotifierThread

    def __init__(self, verbose):
        CallbackManager.__init__(self, verbose)

        self.m_local_notifiers = False

    def add(self, callback, prefix, flags):
        if (flags & NT_NOTIFY_LOCAL) != 0:
            self.m_local_notifiers = True
        return self.doAdd(_EntryListenerData(prefix, None, flags, callback, None))

    def addById(self, callback, local_id, flags):
        if (flags & NT_NOTIFY_LOCAL) != 0:
            self.m_local_notifiers = True
        return self.doAdd(_EntryListenerData(None, local_id, flags, callback, None))

    def addPolled(self, poller_uid, prefix, flags):
        if (flags & NT_NOTIFY_LOCAL) != 0:
            self.m_local_notifiers = True
        return self.doAdd(_EntryListenerData(prefix, None, flags, None, poller_uid))

    def addPolledById(self, poller_uid, local_id, flags):
        if (flags & NT_NOTIFY_LOCAL) != 0:
            self.m_local_notifiers = True
        return self.doAdd(_EntryListenerData(None, local_id, flags, None, poller_uid))

    def notifyEntry(self, local_id, name, value, flags, only_listener=None):

        # optimization: don't generate needless local queue entries if we have
        # no local listeners (as this is a common case on the server side)
        if not self.m_local_notifiers and (flags & NT_NOTIFY_LOCAL) != 0:
            return

        self.send(only_listener, _EntryNotification(name, value, flags, local_id))

    def start(self):
        CallbackManager.start(self)

# validated: 2017-10-01 DS e4a8bff70e77 cpp/EntryNotifier.cpp cpp/EntryNotifier.h cpp/IEntryNotifier.h ---------------------------------------------------------------------------- Copyright (c) FIRST 2017. All Rights Reserved. Open Source Software - may be modified and shared by FRC teams. The code must be accompanied by the FIRST BSD license file in the root directory of the project. ---------------------------------------------------------------------------- # we don't have entry handles like ntcore has # # Flags must be within requested flag set for this listener. # Because assign messages can result in both a value and flags update, # we handle that case specially. # must match local id or prefix # optimization: don't generate needless local queue entries if we have # no local listeners (as this is a common case on the server side)