'''Generic NeighborManager for any communicator and discoverer'''

# Copyright (c) 2024 Carnegie Mellon University
# This code is subject to the license terms contained in the LICENSE file.


import queue
from typing import List, Optional

from ..discoverers.impl.discoverer import Discoverer
from ..communicators.impl.communicator import Communicator
from .neighbor_manager import Event, NeighborManager
from .node_id import NodeID


class NeighborManagerImpl(NeighborManager):
    '''Generic neighbor manager with configurable discovery.'''
    name = 'neighbor_manager_impl'
    tags = {}
    _discoverer: Discoverer
    _communicator: Communicator

    def __init__(self, discoverer: Discoverer, **kwargs):
        super().__init__(discoverer=discoverer, **kwargs)
        self._discoverer = discoverer
        # Set up a socket to listen to.
        self._communicator = self._discoverer.communicator

    def synchronize(self):
        # TODO(Merritt/Piggy): write this
        return

    def _poll_for_events(self, timeout: Optional[float] = None) -> List[Event]:
        retval = []
        # We do not use self._default_timeout for polling. If you want
        # poll_for_events to block, you must specify a timeout.
        try:
            while not self._queue.empty():
                # There is a race here between callers to poll_for_events().
                # One of the racers will raise a queue.Empty, which is safely
                # caught below.
                event = self._queue.get(block=False)
                retval.append(event)
            if timeout is not None:
                # If we have a timeout, wait the full timeout for another event.
                event = self._queue.get(timeout=timeout)
                retval.append(event)
        except queue.Empty:
            pass
        return retval

    def _send_all(self, payload: bytes) -> None:
        self._communicator.send_all(payload)

    def _send(self, node: NodeID, payload: bytes) -> None:
        self._communicator.send(node, payload)
