#!/usr/bin/env python

import logging
import time
from os import _exit, listdir
from os.path import join
from random import shuffle
from subprocess import call
from sys import exit
from threading import Thread, Timer

import gi
from django.conf import settings

from id3parse import ID3

from owltail.ws_server import WebSocketMixin

from .const import PLAYLIST_FINISHED, PLAY_FROM_INPUT

logger = logging.getLogger(__name__)


def import_gst_gobject():
    gi.require_version('Gst', '1.0')
    from gi.repository import Gst, GObject
    Gst.init(None)
    return Gst, GObject

Gst, GObject = import_gst_gobject()

INIT_VOLUME = 1


class Player(WebSocketMixin, Thread):
    def __init__(self):
        super().__init__()
        self.mainloop = GObject.MainLoop()
        self.player = Gst.ElementFactory.make("playbin", "player")
        fakesink = Gst.ElementFactory.make("fakesink", "fakesink")
        self.player.set_property("video-sink", fakesink)
        self.player.connect("about-to-finish", self.play_next_song)

        self.playlist = []
        self.source = None
        self.duration = None
        self.timer = None
        self.loopback_module_loaded = False

    def get_playlist(self):
        source_path = join(settings.DEFAULT_SONGS_FOLDER, self.source)
        files = listdir(source_path)
        if not files:
            return None
        shuffle(files)
        return [join(source_path, filename) for filename in files]

    def play_next_song(self, *args, **kwargs):
        if not self.playlist:
            self.playlist_stop()
            return
        track_path = self.playlist.pop()
        self.player.set_property("uri", "file://" + track_path)
        self.player.set_state(Gst.State.PLAYING)
        self.send_next_song(track_path)

    def playlist_stop(self):
        if self.loopback_module_loaded:
            call(["pacmd", "set-source-mute", settings.PULSE_SOURCE, "1"])
            self.loopback_module_loaded = False

        self.player.set_state(Gst.State.NULL)
        self.playlist = []
        self.source = None
        self.duration = None
        if self.timer:
            self.timer.cancel()
            self.timer = None

        self.send_stop_message()

    def send_stop_message(self):
        self._send_ws_message_close({'operationId': PLAYLIST_FINISHED})

    def send_next_song(self, path):
        id3 = ID3.from_file(path)
        artist = ''
        title = ''
        try:
            artist = id3.find_frame_by_name('TPE1').text
            title = id3.find_frame_by_name('TIT2').text
        except ValueError:
            logger.exception(
                "Could not get ID3 informations on the track %s", path
            )
        self._send_websocket_message({
            'operationId': 'TRACK_CHANGE',
            'data': {
                'artist': artist,
                'title': title
            }
        })

    def playlist_start(self, source, duration):
        self.player.set_property("volume", INIT_VOLUME)
        self.source = source
        self.duration = duration
        if self.source == PLAY_FROM_INPUT:
            self.play_from_audio_input()
        else:
            self.playlist = self.get_playlist()
            self.play_next_song()
        self.set_stop_timeout()

    def set_stop_timeout(self):
        self.timer = Timer(
            (self.duration - settings.FADE_OUT_DURATION), self.fade_out
        )
        self.timer.start()

    def play_from_audio_input(self):
        if not self.loopback_module_loaded:
            call(["pacmd", "set-source-mute", settings.PULSE_SOURCE, "0"])
        self.loopback_module_loaded = True

    def fade_out(self):
        for i in range(settings.FADE_OUT_STEP):
            self.player.set_property(
                "volume",
                INIT_VOLUME - i / settings.FADE_OUT_STEP
            )
            time.sleep(settings.FADE_OUT_DURATION / settings.FADE_OUT_STEP)
        self.player.set_property("volume", 0)
        self.playlist_stop()

    def run(self):
        try:
            self.mainloop.run()
        except KeyboardInterrupt:
            logger.info("Ctrl-C pressed, killing player thread...")
            try:
                exit(0)
            except SystemExit:
                logger.warn("Could not exit cleanly, calling os._exit()")
                _exit(0)

if (settings.MUSIC_PLAYER_ENABLED):
    player = Player()
    player.start()
else:
    # for external imports, player must be declared
    player = None
