from threading import Thread
from queue import Queue
from enum import Enum, unique


@unique
class PCmd(Enum):
    GET_MORE = 1
    REFRESH = 2


class FakeSource:
    def __init__(self, stream_name, gen_func):
        setattr(self, stream_name, gen_func)


class PrawStreamWrapper:
    def __init__(self, source, stream_name, gen_func=None):
        if source is None:
            if gen_func is None:
                raise ValueError(
                    'PrawStreamWrapper Error: '
                    'source and gen_func are both None'
                )
            source = FakeSource(stream_name, gen_func)
        self.source = source
        self.stream_name = stream_name
        self.generator = None
        self.start()

    def start(self):
        self.__stop = False
        self.cmd_queue = Queue()
        self.cmd_queue.put(PCmd.REFRESH)
        self.out_queue = Queue()
        self.__thread = Thread(target=self.async_worker, daemon=True)
        self.__thread.start()

    def load_more(self):
        self.cmd_queue.put(PCmd.GET_MORE)

    def queue_get(self):
        res = self.out_queue.get()
        if res == -1:
            raise StopIteration()
        return res

    def change_stream_name(self, n_name):
        self.stream_name = n_name
        self.refresh()

    def refresh(self):
        self.stop()
        self.start()

    def async_worker(self):
        while not self.__stop:
            cmd = self.cmd_queue.get()  # wait for commands
            if cmd == PCmd.GET_MORE:
                if self.generator is None:
                    self.cmd_queue.put(PCmd.REFRESH)
                    self.cmd_queue.put(PCmd.GET_MORE)
                    continue
                try:
                    self.out_queue.put(
                        next(self.generator)
                    )
                except StopIteration:
                    self.out_queue.put(-1)
            elif cmd == PCmd.REFRESH:
                self.generator = getattr(
                    self.source, self.stream_name
                )(limit=None)
                self.cmd_queue.put(PCmd.GET_MORE)
            else:
                # error?
                print(f'PrawStreamWrapper Error: unknown command: `{cmd}`')

    def stop(self):
        self.__stop = True
        self.__thread.join()

    def __del__(self):
        self.stop()
