import logging
from threading import Thread

import zmq
from zmq import Context

logger = logging.getLogger("ZeroMqEventManager.StreamerDevice")


class StreamerDevice(object):
    def __init__(self, context, sub_address, push_address, min_port, max_port):
        # type: (Context, str, str, int, int) -> None
        self.context = context
        self.sub_address = sub_address
        self.push_address = push_address
        self.min_port = min_port
        self.max_port = max_port
        self.port = None
        self.sub_socket = None
        self.push_socket = None
        self.running = False
        self.thread = None

    def start(self):
        self.sub_socket = self.context.socket(zmq.SUB)
        self.sub_socket.set_hwm(100000)
        self.sub_socket.connect(self.sub_address)

        self.push_socket = self.context.socket(zmq.PUSH)
        self.push_socket.set_hwm(100000)
        self.port = self.push_socket.bind_to_random_port(self.push_address, self.min_port, self.max_port)
        logger.info("StreamerPort: {}".format(self.port))

        self.running = True
        self.thread = Thread(name="StreamerDevice", target=self.execute)
        self.thread.daemon = False
        self.thread.start()

        return self.port

    def stop(self):
        self.running = False
        self.thread.join()
        self.push_socket.close()
        self.sub_socket.close()

    def add_subjects(self, subjects):
        for subject in subjects:
            self.sub_socket.setsockopt_string(zmq.SUBSCRIBE, subject + " ")

    def remove_subjects(self, subjects):
        for subject in subjects:
            self.sub_socket.setsockopt_string(zmq.UNSUBSCRIBE, subject + " ")

    def execute(self):
        logger.info("Starting streamer device: %s %s", self.sub_address, self.push_address)

        receiving = False
        while self.running:
            try:
                if not receiving:
                    ready = self.sub_socket.poll(1000, zmq.POLLIN)
                    if ready & zmq.POLLIN == 0:
                        continue
                    receiving = True
                try:
                    data = self.sub_socket.recv(flags=zmq.NOBLOCK)
                except zmq.ZMQError:
                    receiving = False
                    continue

                try:
                    self.push_socket.send(data, flags=zmq.NOBLOCK)
                except zmq.ZMQError:
                    pass

            except:
                logger.exception("Exception when handling message")

        logger.info("StreamerDevice %s thread finished", self.port)
