import logging

import zmq
from abc import ABCMeta, abstractmethod
from threading import Thread


class WorkerThread(object):
    __meta__ = ABCMeta

    def __init__(self, context, worker_address, idle_timeout):
        self.context = context
        self.worker_address = worker_address
        self.idle_timeout = idle_timeout
        self.logger = logging.getLogger(self.get_logger_name())

        self.is_working = False
        self.is_idle = False

        self.running = False
        self.inactive_time = 0
        self.thread = None

    def start(self):
        if self.running:
            return

        self.running = True
        self.inactive_time = 0

        self.thread = Thread(target=self.run)
        self.thread.daemon = True
        self.thread.start()

    def stop(self):
        self.running = False

    def join(self):
        self.thread.join()

    def run(self):
        socket = None

        try:
            self.logger.debug("Creating socket to address: %s", self.worker_address)
            socket = self.create_socket(self.worker_address)

            receiving = False
            while self.running:
                if receiving:
                    try:
                        data = socket.recv(flags=zmq.NOBLOCK)
                    except zmq.ZMQError:
                        receiving = False
                        continue
                else:
                    ready = socket.poll(1000, zmq.POLLIN)
                    if ready & zmq.POLLIN == 0:
                        self.is_working = False
                        self.inactive_time += 1000
                        if self.inactive_time > self.idle_timeout:
                            self.is_idle = True

                        continue
                    else:
                        self.logger.debug("Data available, start receiving: %s", type(self))
                        receiving = True
                        data = socket.recv()
                        self.logger.debug("Data received")

                self.inactive_time = 0
                self.is_idle = False
                self.is_working = True

                self.logger.debug("Doing work")
                resp = self.do_work(data)
                self.logger.debug("Work done")
                if resp is not None:
                    self.logger.debug("Sending reply")
                    socket.send(resp)
                    self.logger.debug("Reply sent")
        except:
            self.logger.exception("Error handling work")
            raise
        finally:
            self.running = False
            try:
                if socket is not None:
                    socket.close()
            except:
                pass

    @abstractmethod
    def do_work(self, data):
        # type: (str) -> None
        raise NotImplementedError()

    @abstractmethod
    def dispose(self):
        raise NotImplementedError()

    @abstractmethod
    def create_socket(self, address):
        raise NotImplementedError()

    def get_logger_name(self):
        return "ZeroMqThreadPool.WorkerThread"
