import collections
import queue
import signal
import subprocess
import threading

import mucus.deezer.stream
import mucus.exception

events = ('play', 'stop', 'loop')


class Events(collections.namedtuple('Events', events)):
    def __new__(cls):
        return super().__new__(cls, *(threading.Event() for _ in events))


class Player:
    def __init__(self, client):
        self.client = client
        self.queue = queue.Queue()
        self.events = Events()
        self.thread = None
        self.song = None

    def loop(self):
        self.events.stop.clear()
        while not self.events.stop.is_set():
            try:
                song = self.queue.get(timeout=1)
            except queue.Empty:
                continue
            if song is None:
                self.queue.task_done()
                break
            else:
                self.play(song)
                self.queue.task_done()

    def play(self, song):
        while True:
            data = mucus.deezer.stream.stream(song, self.client.data.license_token)

            media = next(data)
            if media is None:
                raise mucus.exception.NoMedia

            source = next(data)
            if source is None:
                raise mucus.exception.NoSource

            self.song = song
            self.events.play.set()

            opts = ['--buffer', '2048', '-q', '-V0']
            if media['format'].startswith('MP3_'):
                opts.insert(0, '-t')
                opts.insert(1, 'mp3')

            with subprocess.Popen(['sox', *opts, '-', '-d'], bufsize=2048, stdin=subprocess.PIPE) as p:
                for chunk in data:
                    if self.events.stop.is_set():
                        break

                    if not self.events.play.is_set():
                        p.send_signal(signal.SIGSTOP)
                        self.events.play.wait()
                        p.send_signal(signal.SIGCONT)

                    if self.events.stop.is_set():
                        break

                    p.stdin.write(chunk)

                p.stdin.close()

                if self.events.stop.is_set():
                    p.terminate()

            if self.events.stop.is_set():
                break
            elif self.events.loop.is_set():
                continue
            else:
                break
        self.song = None
        self.events.play.clear()

    def start(self):
        try:
            subprocess.run(['sox', '--version'], stdout=subprocess.DEVNULL)
        except FileNotFoundError:
            raise mucus.exception.SoxNotInstalled

        self.thread = threading.Thread(target=self.loop, daemon=True)
        self.thread.start()
