import shlex
import socket
import subprocess
import threading

import external_player
import internal_player
import service


class Librespot:

    def __init__(self,
                 bitrate='320',
                 device_type='tv',
                 max_retries='5',
                 name='Librespot@{}',
                 options='',
                 **kwargs):
        name = name.format(socket.gethostname())
        self.command = [
            'librespot',
            '--bitrate', f'{bitrate}',
            '--device-type', f'{device_type}',
            '--disable-audio-cache',
            '--disable-credential-cache',
            '--name', f'{name}',
            '--onevent', 'onevent.py',
            '--quiet',
        ] + shlex.split(options)
        service.log(self.command)
        self.file = ''
        self._is_started = threading.Event()
        self._is_stopped = threading.Event()
        self._librespot = None
        self._max_retries = int(max_retries)
        self._retries = 0
        self._thread = threading.Thread()

    def get_player(self, **kwargs):
        return (internal_player if self.file else external_player).Player(**kwargs)

    def restart(self):
        if self._thread.is_alive():
            self._librespot.terminate()
        else:
            self.start()

    def start(self):
        if not self._thread.is_alive() and self._retries < self._max_retries:
            self._thread = threading.Thread(daemon=True, target=self._run)
            self._thread.start()
            self._is_started.wait(1)

    def stop(self):
        if self._thread.is_alive():
            self._is_stopped.set()
            self._librespot.terminate()
            self._thread.join()

    def start_sink(self):
        pass

    def stop_sink(self):
        pass

    def _run(self):
        service.log('librespot thread started')
        self._is_started.clear()
        self._is_stopped.clear()
        while not self._is_stopped.is_set():
            with subprocess.Popen(self.command, stderr=subprocess.PIPE, text=True) as self._librespot:
                self._is_started.set()
                for line in self._librespot.stderr:
                    service.log(line.rstrip())
            self.stop_sink()
            if self._librespot.returncode <= 0:
                self._retries = 0
            else:
                self._retries += 1
                if self._retries < self._max_retries:
                    service.notification(
                        f'librespot failed {self._retries}/{self._max_retries}')
                else:
                    service.notification('librespot failed too many times')
                    break
        service.log('librespot thread stopped')

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.stop()
