"""
homeassistant.components.media_player.kodi
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides an interface to the XBMC/Kodi JSON-RPC API

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.kodi/
"""
import logging
import urllib

from homeassistant.components.media_player import (
    SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK,
    SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, MediaPlayerDevice)
from homeassistant.const import (
    STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING)

_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['jsonrpc-requests==0.1']

SUPPORT_KODI = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
    SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK


def setup_platform(hass, config, add_devices, discovery_info=None):
    """ Sets up the kodi platform. """

    url = '{}:{}'.format(config.get('host'), config.get('port', '8080'))

    jsonrpc_url = config.get('url')  # deprecated
    if jsonrpc_url:
        url = jsonrpc_url.rstrip('/jsonrpc')

    add_devices([
        KodiDevice(
            config.get('name', 'Kodi'),
            url,
            auth=(
                config.get('user', ''),
                config.get('password', ''))),
    ])


class KodiDevice(MediaPlayerDevice):
    """ Represents a XBMC/Kodi device. """

    # pylint: disable=too-many-public-methods, abstract-method

    def __init__(self, name, url, auth=None):
        import jsonrpc_requests
        self._name = name
        self._url = url
        self._server = jsonrpc_requests.Server(
            '{}/jsonrpc'.format(self._url),
            auth=auth)
        self._players = None
        self._properties = None
        self._item = None
        self._app_properties = None
        self.update()

    @property
    def name(self):
        """ Returns the name of the device. """
        return self._name

    def _get_players(self):
        """ Returns the active player objects or None """
        import jsonrpc_requests
        try:
            return self._server.Player.GetActivePlayers()
        except jsonrpc_requests.jsonrpc.TransportError:
            _LOGGER.warning('Unable to fetch kodi data')
            _LOGGER.debug('Unable to fetch kodi data', exc_info=True)
            return None

    @property
    def state(self):
        """ Returns the state of the device. """
        if self._players is None:
            return STATE_OFF

        if len(self._players) == 0:
            return STATE_IDLE

        if self._properties['speed'] == 0:
            return STATE_PAUSED
        else:
            return STATE_PLAYING

    def update(self):
        """ Retrieve latest state. """
        self._players = self._get_players()

        if self._players is not None and len(self._players) > 0:
            player_id = self._players[0]['playerid']

            assert isinstance(player_id, int)

            self._properties = self._server.Player.GetProperties(
                player_id,
                ['time', 'totaltime', 'speed']
            )

            self._item = self._server.Player.GetItem(
                player_id,
                ['title', 'file', 'uniqueid', 'thumbnail', 'artist']
            )['item']

            self._app_properties = self._server.Application.GetProperties(
                ['volume', 'muted']
            )
        else:
            self._properties = None
            self._item = None
            self._app_properties = None

    @property
    def volume_level(self):
        """ Volume level of the media player (0..1). """
        if self._app_properties is not None:
            return self._app_properties['volume'] / 100.0

    @property
    def is_volume_muted(self):
        """ Boolean if volume is currently muted. """
        if self._app_properties is not None:
            return self._app_properties['muted']

    @property
    def media_content_id(self):
        """ Content ID of current playing media. """
        if self._item is not None:
            return self._item.get('uniqueid', None)

    @property
    def media_content_type(self):
        """ Content type of current playing media. """
        if self._players is not None and len(self._players) > 0:
            return self._players[0]['type']

    @property
    def media_duration(self):
        """ Duration of current playing media in seconds. """
        if self._properties is not None:
            total_time = self._properties['totaltime']

            return (
                total_time['hours'] * 3600 +
                total_time['minutes'] * 60 +
                total_time['seconds'])

    @property
    def media_image_url(self):
        """ Image url of current playing media. """
        if self._item is not None:
            return self._get_image_url()

    def _get_image_url(self):
        """ Helper function that parses the thumbnail URLs used by Kodi. """
        url_components = urllib.parse.urlparse(self._item['thumbnail'])

        if url_components.scheme == 'image':
            return '{}/image/{}'.format(
                self._url,
                urllib.parse.quote_plus(self._item['thumbnail']))

    @property
    def media_title(self):
        """ Title of current playing media. """
        # find a string we can use as a title
        if self._item is not None:
            return self._item.get(
                'title',
                self._item.get(
                    'label',
                    self._item.get(
                        'file',
                        'unknown')))

    @property
    def supported_media_commands(self):
        """ Flags of media commands that are supported. """
        return SUPPORT_KODI

    def turn_off(self):
        """ turn_off media player. """
        self._server.System.Shutdown()
        self.update_ha_state()

    def volume_up(self):
        """ volume_up media player. """
        assert self._server.Input.ExecuteAction('volumeup') == 'OK'
        self.update_ha_state()

    def volume_down(self):
        """ volume_down media player. """
        assert self._server.Input.ExecuteAction('volumedown') == 'OK'
        self.update_ha_state()

    def set_volume_level(self, volume):
        """ set volume level, range 0..1. """
        self._server.Application.SetVolume(int(volume * 100))
        self.update_ha_state()

    def mute_volume(self, mute):
        """ mute (true) or unmute (false) media player. """
        self._server.Application.SetMute(mute)
        self.update_ha_state()

    def _set_play_state(self, state):
        """ Helper method for play/pause/toggle. """
        players = self._get_players()

        if len(players) != 0:
            self._server.Player.PlayPause(players[0]['playerid'], state)

        self.update_ha_state()

    def media_play_pause(self):
        """ media_play_pause media player. """
        self._set_play_state('toggle')

    def media_play(self):
        """ media_play media player. """
        self._set_play_state(True)

    def media_pause(self):
        """ media_pause media player. """
        self._set_play_state(False)

    def _goto(self, direction):
        """ Helper method used for previous/next track. """
        players = self._get_players()

        if len(players) != 0:
            self._server.Player.GoTo(players[0]['playerid'], direction)

        self.update_ha_state()

    def media_next_track(self):
        """ Send next track command. """
        self._goto('next')

    def media_previous_track(self):
        """ Send next track command. """
        # first seek to position 0, Kodi seems to go to the beginning
        # of the current track current track is not at the beginning
        self.media_seek(0)
        self._goto('previous')

    def media_seek(self, position):
        """ Send seek command. """
        players = self._get_players()

        time = {}

        time['milliseconds'] = int((position % 1) * 1000)
        position = int(position)

        time['seconds'] = int(position % 60)
        position /= 60

        time['minutes'] = int(position % 60)
        position /= 60

        time['hours'] = int(position)

        if len(players) != 0:
            self._server.Player.Seek(players[0]['playerid'], time)

        self.update_ha_state()
