#!/usr/bin/env python

import logging
import time

import gi
import stagger

from os import listdir
from os.path import join
from random import shuffle
from subprocess import call
from base64 import b64encode

from owltail import settings


PLAY_FROM_INPUT = 'WIRE'

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()


class Player:
    def __init__(self, app):
        super().__init__()

        self.app = app

        self.playlist = []
        self.index = None
        self.source = None
        self.loopback_module_loaded = False
        self.started = False
        self.volume = None

    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_previous_song(self, *args, **kwargs):
        if self.index is None:
            self.index = 0
        if not self.playlist:
            self.app.stop(refresh=True)
            return

        self.index -= 1
        if self.index < 0:
            self.index = len(self.playlist) - 1
        self.play()

    def play_next_song(self, *args, **kwargs):
        if self.index is None:
            self.index = 0
        if not self.playlist:
            self.app.stop(refresh=True)
            return

        self.index += 1
        if self.index >= len(self.playlist):
            self.index = 0
        self.play()

    def play(self):
        track_path = self.playlist[self.index]
        if getattr(self, 'player', None):
            self.player.set_state(Gst.State.NULL)

        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.player.set_property("uri", "file://" + track_path)
        self.player.set_property("volume", 1)
        self.player.set_state(Gst.State.PLAYING)

        artist, title, picture_data, picture_mime = self.current_track_infos()
        self.app.send_message({
            'operation': 'TRACK_CHANGE',
            'artist': artist,
            'title': title,
            'picture_data': picture_data,
            'picture_mime': picture_mime,
        })

    def playlist_stop(self):
        call(settings.PLAYER_NOTHING_TO_LINEOUT_CMD.split())

        if hasattr(self, 'player'):
            self.player.set_state(Gst.State.NULL)
            self.player.set_property("volume", 0)

        self.source = None
        self.index = None
        self.playlist = []
        self.volume = None

    def playlist_start(self, source):
        self.source = source
        self.volume = 100
        if self.source == PLAY_FROM_INPUT:
            self.play_from_audio_input()
        else:
            self.play_from_playlist()

    def play_from_playlist(self):
        call(settings.PLAYER_PLAYBACK_TO_LINEOUT_CMD.split())
        self.set_lineout_volume(settings.PLAYLIST_VOLUME_START)
        self.playlist = self.get_playlist()
        self.play_next_song()

    def play_from_audio_input(self):
        call(settings.PLAYER_LINEIN_TO_LINEOUT_CMD.split())
        self.set_lineout_volume(settings.LINEIN_VOLUME_START)

    def set_lineout_volume(self, value):
        call(settings.PLAYER_SET_VOLUME.split() + [str(int(value))])

    def fade_out(self):
        if self.source == PLAY_FROM_INPUT:
            volume = settings.LINEIN_VOLUME_START * self.volume / 100
        else:
            volume = settings.PLAYLIST_VOLUME_START * self.volume / 100

        for i in range(settings.FADE_OUT_STEP):
            self.set_lineout_volume(volume * (1 - i / settings.FADE_OUT_STEP))
            self.app.send_message({
                'operation': 'VOLUME',
                'volume': self.volume * (1 - i / settings.FADE_OUT_STEP)
            })
            time.sleep(settings.FADE_OUT_DURATION / settings.FADE_OUT_STEP)
        self.app.stop(refresh=True)

    def current_track_infos(self):
        if self.index is None or not self.playlist:
            return ('', '', '', '')
        track_path = self.playlist[self.index]
        try:
            tag = stagger.read_tag(track_path)

            picture_data = ''
            picture_mime = ''
            if stagger.id3.APIC in tag:
                picture_data = (
                    b64encode(tag[stagger.id3.APIC][0].data).decode()
                )
                picture_mime = tag[stagger.id3.APIC][0].mime
            elif stagger.id3.PIC in tag:
                picture_data = b64encode(tag[stagger.id3.PIC][0].data).decode()
                picture_mime = tag[stagger.id3.PIC][0].mime
            return (tag.artist, tag.title, picture_data, picture_mime)
        except:
            logger.warning('Could not read tag of file ' + track_path)
            return ('', '', '', '')

    def set_volume(self, value):
        if self.source == PLAY_FROM_INPUT:
            volume = settings.LINEIN_VOLUME_START
        else:
            volume = settings.PLAYLIST_VOLUME_START
        self.volume = value
        self.set_lineout_volume(volume * value / 100)
