# The MIT License (MIT)

# Copyright(c) 2022 Ben Hunt

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from subprocess import CompletedProcess, run
from typing import List

from libqtile.group import _Group
from libqtile.config import Screen

from libqtile.widget import base
from libqtile.log_utils import logger

SPOTIFY = "Spotify"


class Spotify(base.ThreadPoolText):
    """
    A widget to interact with spotify via dbus.
    """

    defaults = [
        ("play_icon", "", "icon to display when playing music"),
        ("pause_icon", "", "icon to display when music paused"),
        ("update_interval", 0.5, "polling rate in seconds"),
        ("format", "{icon} {artist}: {album} - {track}", "Spotify display format"),
    ]

    def __init__(self, **config) -> None:
        # init base class
        super().__init__(text="", **config)
        self.add_defaults(Spotify.defaults)
        self.add_callbacks(
            {
                "Button1": self.toggle_music,
            }
        )

    def _is_proc_running(self, proc_name: str) -> bool:
        # create regex pattern to search for to avoid similar named processes
        pattern = f"{proc_name}$"
        # pgrep will return a string of pids for matching processes
        cmd = ["pgrep", "-fli", pattern]
        proc_out = run(cmd, capture_output=True).stdout.decode(
            "utf-8"
        )

        return proc_out != ""

    def toggle_between_groups(self) -> None:
        """
        remember which group you were on before you switched to spotify
        so you can toggle between the 2 groups
        """
        current_screen: Screen = self.qtile.current_screen
        current_group_info = self.qtile.current_group.info()
        logger.warning(f"current group info: {current_group_info}")
        windows = current_group_info["windows"]
        if SPOTIFY in windows:
            # go to previous group
            logger.warning("going to previous group")
            current_screen.group.get_previous_group().toscreen()
            logger.warning("went to previous group")
        else:
            self.go_to_spotify()

    def go_to_spotify(self) -> None:
        """
        Switch to whichever group has the current spotify instance
        if none exists then we will spawn an instance on the current group
        """
        # spawn spotify if not already running
        if not self._is_proc_running("spotify"):
            self.qtile.spawn("spotify", shell=True)
            return

        all_groups: List[_Group] = self.qtile.groups
        # we need to find the group that has spotify in it
        for group in all_groups:
            info = group.info()
            # get a list of windows for the group.  We look for 'Spotify here'
            windows = info["windows"]
            if SPOTIFY in windows:
                name = group.name
                # switch to 'name' group
                spotify_group = self.qtile.groups_map[name]
                spotify_group.toscreen()
                break

    def poll(self) -> str: # type: ignore
        """Poll content for the text box"""
        vars = {
            "icon": self.play_icon if self.playing else self.pause_icon,
            "artist": self.artist,
            "track": self.song_title,
            "album": self.album,
        }

        return self.format.format(**vars) # type: ignore

    def toggle_music(self) -> None:
        cmd = """
        dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify \
        /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.PlayPause
        """
        run(cmd, shell=True)


    def get_proc_output(self, proc: CompletedProcess) -> str:
        stdout = proc.stdout.decode("utf-8")
        no_spotify = "Error" in stdout
        return (
            ""
            if no_spotify
            else stdout.rstrip()
        )


    @property
    def _meta(self) -> str:
        cmd = """dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify \
            /org/mpris/MediaPlayer2 \
            org.freedesktop.DBus.Properties.Get \
            string:'org.mpris.MediaPlayer2.Player' \
            string:'Metadata'
        """
        proc = run( cmd, shell=True, capture_output=True)

        output: str = proc.stdout.decode("utf-8").replace("'", "ʼ").rstrip()
        return "" if ("org.mpris.MediaPlayer2.spotify" in output) else output

    @property
    def artist(self) -> str:
        cmd ="""
        dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify \
        /org/mpris/MediaPlayer2 \
        org.freedesktop.DBus.Properties.Get string:'org.mpris.MediaPlayer2.Player' \
        string:'Metadata' | grep -m1 'xesam:artist' -b2 | tail -n 1 | grep -o '\".*\"' | \
        sed 's/\"//g' | sed -e 's/&/and/g'
        """
        proc: CompletedProcess = run(cmd,
            shell=True,
            capture_output=True,
        )

        return self.get_proc_output(proc)

    @property
    def song_title(self) -> str:
        cmd = f"""
        echo '{self._meta}' | grep -m1 'xesam:title' -b1 | tail -n1 | grep -o '\".*\"' | \
        sed 's/\"//g' | sed -e 's/&/and/g'
        """
        proc: CompletedProcess = run(cmd,
            shell=True,
            capture_output=True
        )

        return self.get_proc_output(proc)

    @property
    def album(self) -> str:
        cmd =  f"""
        echo '{self._meta}' | grep -m1 'xesam:album' -b1 | tail -n1 | grep -o '\".*\"' | \
        sed 's/\"//g' | sed -e 's/&/and/g'
        """
        proc = run(cmd,
            shell=True,
            capture_output=True,
        )

        return self.get_proc_output(proc)

    @property
    def playing(self) -> bool:
        cmd = """
        dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify \
        /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get \
        string:'org.mpris.MediaPlayer2.Player' string:'PlaybackStatus' | grep -o Playing
        """
        play = run(cmd,
            shell=True,
            capture_output=True,
        ).stdout.decode("utf-8")

        return play != ""
