# -*- coding: utf-8 -*-
"""
Display song currently playing in Spotify.

Configuration parameters:
    cache_timeout: how often to update the bar (default 5)
    format: see placeholders below (default '{artist} : {title}')
    format_down: define output if spotify is not running
        (default 'Spotify not running')
    format_stopped: define output if spotify is not playing
        (default 'Spotify stopped')
    sanitize_titles: whether to remove meta data from album/track title
        (default True)
    sanitize_words: which meta data to remove
        (default ['bonus', 'demo', 'edit', 'explicit',
                  'extended', 'feat', 'mono', 'remaster',
                  'stereo', 'version'])

Format placeholders:
    {album} album name
    {artist} artiste name (first one)
    {time} time duration of the song
    {title} name of the song

Color options:
    color_offline: Spotify is not running, defaults to color_bad
    color_paused: Song is stopped or paused, defaults to color_degraded
    color_playing: Song is playing, defaults to color_good

Click Events:
    You can add some config to your i3status.conf to  create click events
    For example

    - play_paused = 1 ==> left click : Pause/Play
    - next_song = 4 ==> scroll up : Next song
    - previous_song = 5 ==> scroll down : Previous song

i3status.conf example:

```
spotify {
    format = "{title} by {artist} -> {time}"
    format_down = "no Spotify"
    next_song = 4
    previous_song = 5
    play_paused = 1
}
```

Requires:
    spotify (>=1.0.27.71.g0a26e3b2)

@author Pierre Guilbert, Jimmy Garpehäll, sondrele, Andrwe

SAMPLE OUTPUT
{'color': '#00FF00', 'full_text': 'Rick Astley : Never Gonna Give You Up'}

paused
{'color': '#FFFF00', 'full_text': 'Rick Astley : Never Gonna Give You Up'}

stopped
{'color': '#FF0000', 'full_text': 'Spotify stopped'}
"""

import dbus
import re

from datetime import timedelta
from time import sleep


SPOTIFY_CMD = 'dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.{cmd}'


class Py3status:
    """
    """
    # available configuration parameters
    cache_timeout = 5
    format = '{artist} : {title}'
    format_down = 'Spotify not running'
    format_stopped = 'Spotify stopped'
    sanitize_titles = True
    sanitize_words = [
        'bonus',
        'demo',
        'edit',
        'explicit',
        'extended',
        'feat',
        'mono',
        'remaster',
        'stereo',
        'version'
    ]

    def _spotify_cmd(self, action):
        return SPOTIFY_CMD.format(cmd=action)

    def post_config_hook(self):
        """
        """
        # Match string after hyphen, comma, semicolon or slash containing any metadata word
        # examples:
        # - Remastered 2012
        # / Radio Edit
        # ; Remastered
        self.after_delimiter = self._compile_re(r"([\-,;/])([^\-,;/])*(META_WORDS_HERE).*")

        # Match brackets with their content containing any metadata word
        # examples:
        # (Remastered 2017)
        # [Single]
        # (Bonus Track)
        self.inside_brackets = self._compile_re(r"([\(\[][^)\]]*?(META_WORDS_HERE)[^)\]]*?[\)\]])")

    def _compile_re(self, expression):
        """
        Compile given regular expression for current sanitize words
        """
        meta_words = '|'.join(self.sanitize_words)
        expression = expression.replace('META_WORDS_HERE', meta_words)
        return re.compile(expression, re.IGNORECASE)

    def _get_text(self):
        """
        Get the current song metadatas (artist - title)
        """
        bus = dbus.SessionBus()
        try:
            self.__bus = bus.get_object('org.mpris.MediaPlayer2.spotify',
                                        '/org/mpris/MediaPlayer2')
            self.player = dbus.Interface(
                self.__bus, 'org.freedesktop.DBus.Properties')

            try:
                metadata = self.player.Get('org.mpris.MediaPlayer2.Player',
                                           'Metadata')
                album = metadata.get('xesam:album')
                artist = metadata.get('xesam:artist')[0]
                microtime = metadata.get('mpris:length')
                rtime = str(timedelta(microseconds=microtime))[:-7]
                title = metadata.get('xesam:title')
                if self.sanitize_titles:
                    album = self._sanitize_title(album)
                    title = self._sanitize_title(title)

                playback_status = self.player.Get(
                    'org.mpris.MediaPlayer2.Player', 'PlaybackStatus'
                )
                if playback_status.strip() == 'Playing':
                    color = self.py3.COLOR_PLAYING or self.py3.COLOR_GOOD
                else:
                    color = self.py3.COLOR_PAUSED or self.py3.COLOR_DEGRADED
            except Exception:
                return (
                    self.format_stopped,
                    self.py3.COLOR_PAUSED or self.py3.COLOR_DEGRADED)

            return (
                self.py3.safe_format(
                    self.format,
                    dict(title=title,
                         artist=artist,
                         album=album,
                         time=rtime)
                ), color)
        except Exception:
            return (
                self.format_down,
                self.py3.COLOR_OFFLINE or self.py3.COLOR_BAD)

    def _sanitize_title(self, title):
        """
        Remove redunant meta data from title and return it
        """
        title = re.sub(self.inside_brackets, "", title)
        title = re.sub(self.after_delimiter, "", title)
        return title.strip()

    def spotify(self):
        """
        Get the current "artist - title" and return it.
        """
        (text, color) = self._get_text()
        response = {
            'cached_until': self.py3.time_in(self.cache_timeout),
            'color': color,
            'full_text': text
        }
        return response

    def on_click(self, event):
        """
        Click events
            - Pause/Play
            - Next song
            - Previous song

        Sleep to "invalidate cache" and give the time to change the song to refresh the display
        """

        button = event['button']
        if button is None:
            pass
        elif button == self.play_paused:
            self.py3.command_run(self._spotify_cmd('PlayPause'))
            sleep(0.1)

        elif button == self.next_song:
            self.py3.command_run(self._spotify_cmd('Next'))
            sleep(0.1)

        elif button == self.previous_song:
            self.py3.command_run(self._spotify_cmd('Previous'))
            sleep(0.1)


if __name__ == "__main__":
    """
    Run module in test mode.
    """
    from py3status.module_test import module_test
    module_test(Py3status)

@@ -1943,17 +1943,19 @@
 Y_CMD = 
-'
+%22%22%22
 dbus-sen
@@ -2007,16 +2007,33 @@
 .spotify
+%0A                
  /org/mp
@@ -2084,17 +2084,19 @@
 er.%7Bcmd%7D
-'
+%22%22%22
 %0A%0A%0Aclass

split SPOTIFY_CMD on 2 lines to respect flake8