#!/usr/bin/env python

import logging
import time

from datetime import datetime
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 owltail.api_handler import APIHandler

from .const import PLAYLIST_FINISHED, PLAY_FROM_INPUT

logger = logging.getLogger(__name__)
api_handler = APIHandler()


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
        self.started = False
        self.session_info = {}

    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)
        self.session_info['songs'].append(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.session_info['end_time'] = datetime.now()
        self.playlist = []
        self.source = None
        self.duration = None
        if self.timer:
            self.timer.cancel()
            self.timer = None

        self.send_stop_message()
        api_handler.send_music_session(self.session_info)
        self.session_info = {}

    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 start_thread(self):
        if not self.started:
            self.start()
        self.started = True

    def playlist_start(self, source, duration, username):
        self.session_info['songs'] = []
        self.session_info['user'] = username
        self.start_thread()
        self._create_ws_connection()
        self.player.set_property("volume", INIT_VOLUME)
        self.source = source
        self.session_info['theme'] = source
        self.duration = duration
        self.session_info['start_time'] = datetime.now()
        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):
        if self.duration:
            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)


player = Player()
