import sys
from SwSpotify import SpotifyClosed, SpotifyPaused, SpotifyNotRunning


def get_info_windows():
    """
    Reads the window titles to get the data.

    Older Spotify versions simply use FindWindow for "SpotifyMainWindow",
    the newer ones create an EnumHandler and flood the list with
    Chrome_WidgetWin_0s
    """

    import win32gui

    windows = []

    old_window = win32gui.FindWindow("SpotifyMainWindow", None)
    old = win32gui.GetWindowText(old_window)

    def find_spotify_uwp(hwnd, windows):
        text = win32gui.GetWindowText(hwnd)
        classname = win32gui.GetClassName(hwnd)
        if classname == "Chrome_WidgetWin_0" and len(text) > 0:
            windows.append(text)

    if old:
        windows.append(old)
    else:
        win32gui.EnumWindows(find_spotify_uwp, windows)

    # If Spotify isn't running the list will be empty
    if len(windows) == 0:
        raise SpotifyClosed

    # Local songs may only have a title field
    try:
        artist, track = windows[0].split(" - ", 1)
    except ValueError:
        artist = ''
        track = windows[0]

    # The window title is the default one when paused
    if windows[0].startswith('Spotify'):
        raise SpotifyPaused

    return track, artist


def get_info_linux():
    """
    Uses the dbus API to get the data.
    """

    import dbus
    if not hasattr(get_info_linux, 'session_bus'):
        get_info_linux.session_bus = dbus.SessionBus()
    try:
        spotify_bus = get_info_linux.session_bus.get_object("org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2")
        spotify_properties = dbus.Interface(spotify_bus, "org.freedesktop.DBus.Properties")
        metadata = spotify_properties.Get("org.mpris.MediaPlayer2.Player", "Metadata")
    except dbus.exceptions.DBusException:
        raise SpotifyClosed

    track = str(metadata['xesam:title'])
    # When this function is called and Spotify hasn't finished setting up the dbus properties, the artist field
    # is empty. This counts as if it weren't open because no data is loaded.
    try:
        artist = str(metadata['xesam:artist'][0])
    except IndexError:
        raise SpotifyClosed from None
    status = str(spotify_properties.Get("org.mpris.MediaPlayer2.Player", "PlaybackStatus"))
    if status.lower() != 'playing':
        raise SpotifyPaused

    return track, artist


def get_info_mac():
    """
    Runs an AppleScript script to get the data.

    Exceptions aren't thrown inside get_info_mac because it automatically
    opens Spotify if it's closed.
    """

    from Foundation import NSAppleScript

    apple_script_code = """
    getCurrentlyPlayingTrack()

    on getCurrentlyPlayingTrack()
        tell application "Spotify"
            set isPlaying to player state as string
            set currentArtist to artist of current track as string
            set currentTrack to name of current track as string
            return {currentArtist, currentTrack, isPlaying}
        end tell
    end getCurrentlyPlayingTrack
    """

    try:
        s = NSAppleScript.alloc().initWithSource_(apple_script_code)
        x = s.executeAndReturnError_(None)
        a = str(x[0]).split('"')
        if a[5].lower() != 'playing':
            raise SpotifyPaused
    except IndexError:
        raise SpotifyClosed from None

    return a[3], a[1]


def get_info_web():
    import SwSpotify.web_server
    import os
    import contextlib
    from SwSpotify import WebData
    with open(os.devnull, 'w') as f, contextlib.redirect_stdout(f):
        SwSpotify.web_server.run()
    if not WebData.track:
        raise SpotifyClosed
    elif WebData.playState == "Play":
        raise SpotifyPaused
    else:
        return WebData.track, WebData.artist


def current():
    try:
        if sys.platform.startswith("win"):
            return get_info_windows()
        elif sys.platform.startswith("darwin"):
            return get_info_mac()
        else:
            return get_info_linux()
    except SpotifyNotRunning as e:
        try:
            return get_info_web()
        except SpotifyClosed:
            raise e


def artist():
    return current()[1]


def song():
    return current()[0]