"""
Support to interface with the Emby API.

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

from datetime import timedelta

import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.components.media_player import (
    MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE,
    SUPPORT_SEEK, SUPPORT_STOP, SUPPORT_PREVIOUS_TRACK, MediaPlayerDevice,
    SUPPORT_PLAY, PLATFORM_SCHEMA)
from homeassistant.const import (
    CONF_HOST, CONF_API_KEY, CONF_PORT, CONF_SSL, DEVICE_DEFAULT_NAME,
    STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING, STATE_UNKNOWN)
from homeassistant.helpers.event import (track_utc_time_change)
from homeassistant.util import Throttle
import homeassistant.util.dt as dt_util

REQUIREMENTS = ['pyemby==0.2']

MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1)

MEDIA_TYPE_TRAILER = 'trailer'

DEFAULT_PORT = 8096

_LOGGER = logging.getLogger(__name__)

SUPPORT_EMBY = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | \
    SUPPORT_STOP | SUPPORT_SEEK | SUPPORT_PLAY

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Optional(CONF_HOST, default='localhost'): cv.string,
    vol.Optional(CONF_SSL, default=False): cv.boolean,
    vol.Required(CONF_API_KEY): cv.string,
    vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
})


def setup_platform(hass, config, add_devices_callback, discovery_info=None):
    """Setup the Emby platform."""
    from pyemby.emby import EmbyRemote

    _host = config.get(CONF_HOST)
    _key = config.get(CONF_API_KEY)
    _port = config.get(CONF_PORT)

    if config.get(CONF_SSL):
        _protocol = "https"
    else:
        _protocol = "http"

    _url = '{}://{}:{}'.format(_protocol, _host, _port)

    _LOGGER.debug('Setting up Emby server at: %s', _url)

    embyserver = EmbyRemote(_key, _url)

    emby_clients = {}
    emby_sessions = {}
    track_utc_time_change(hass, lambda now: update_devices(), second=30)

    @Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
    def update_devices():
        """Update the devices objects."""
        devices = embyserver.get_sessions()
        if devices is None:
            _LOGGER.error('Error listing Emby devices.')
            return

        new_emby_clients = []
        for device in devices:
            if device['DeviceId'] == embyserver.unique_id:
                break

            if device['DeviceId'] not in emby_clients:
                _LOGGER.debug('New Emby DeviceID: %s. Adding to Clients.',
                              device['DeviceId'])
                new_client = EmbyClient(embyserver, device, emby_sessions,
                                        update_devices, update_sessions)
                emby_clients[device['DeviceId']] = new_client
                new_emby_clients.append(new_client)
            else:
                emby_clients[device['DeviceId']].set_device(device)

        if new_emby_clients:
            add_devices_callback(new_emby_clients)

    @Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
    def update_sessions():
        """Update the sessions objects."""
        sessions = embyserver.get_sessions()
        if sessions is None:
            _LOGGER.error('Error listing Emby sessions')
            return

        emby_sessions.clear()
        for session in sessions:
            emby_sessions[session['DeviceId']] = session

    update_devices()
    update_sessions()


class EmbyClient(MediaPlayerDevice):
    """Representation of a Emby device."""

    # pylint: disable=too-many-arguments, too-many-public-methods,

    def __init__(self, client, device, emby_sessions, update_devices,
                 update_sessions):
        """Initialize the Emby device."""
        self.emby_sessions = emby_sessions
        self.update_devices = update_devices
        self.update_sessions = update_sessions
        self.client = client
        self.set_device(device)
        self.media_status_last_position = None
        self.media_status_received = None

    def set_device(self, device):
        """Set the device property."""
        self.device = device

    @property
    def unique_id(self):
        """Return the id of this emby client."""
        return '{}.{}'.format(
            self.__class__, self.device['DeviceId'])

    @property
    def supports_remote_control(self):
        """Return control ability."""
        return self.device['SupportsRemoteControl']

    @property
    def name(self):
        """Return the name of the device."""
        return 'emby_{}'.format(self.device['DeviceName']) or \
            DEVICE_DEFAULT_NAME

    @property
    def session(self):
        """Return the session, if any."""
        if self.device['DeviceId'] not in self.emby_sessions:
            return None

        return self.emby_sessions[self.device['DeviceId']]

    @property
    def now_playing_item(self):
        """Return the currently playing item, if any."""
        session = self.session
        if session is not None and 'NowPlayingItem' in session:
            return session['NowPlayingItem']

    @property
    def state(self):
        """Return the state of the device."""
        session = self.session
        if session:
            if 'NowPlayingItem' in session:
                if session['PlayState']['IsPaused']:
                    return STATE_PAUSED
                else:
                    return STATE_PLAYING
            else:
                return STATE_IDLE
        # This is nasty. Need to find a way to determine alive
        else:
            return STATE_OFF

        return STATE_UNKNOWN

    def update(self):
        """Get the latest details."""
        self.update_devices(no_throttle=True)
        self.update_sessions(no_throttle=True)
        # Check if we should update progress
        try:
            position = self.session['PlayState']['PositionTicks']
        except (KeyError, TypeError):
            self.media_status_last_position = None
            self.media_status_received = None
        else:
            position = int(position) / 10000000
            if position != self.media_status_last_position:
                self.media_status_last_position = position
                self.media_status_received = dt_util.utcnow()

    def play_percent(self):
        """Return current media percent complete."""
        if self.now_playing_item['RunTimeTicks'] and \
                self.session['PlayState']['PositionTicks']:
            try:
                return int(self.session['PlayState']['PositionTicks']) / \
                    int(self.now_playing_item['RunTimeTicks']) * 100
            except KeyError:
                return 0
        else:
            return 0

    @property
    def app_name(self):
        """Return current user as app_name."""
        # Ideally the media_player object would have a user property.
        try:
            return self.device['UserName']
        except KeyError:
            return None

    @property
    def media_content_id(self):
        """Content ID of current playing media."""
        if self.now_playing_item is not None:
            try:
                return self.now_playing_item['Id']
            except KeyError:
                return None

    @property
    def media_content_type(self):
        """Content type of current playing media."""
        if self.now_playing_item is None:
            return None
        try:
            media_type = self.now_playing_item['Type']
            if media_type == 'Episode':
                return MEDIA_TYPE_TVSHOW
            elif media_type == 'Movie':
                return MEDIA_TYPE_VIDEO
            elif media_type == 'Trailer':
                return MEDIA_TYPE_TRAILER
            return None
        except KeyError:
            return None

    @property
    def media_duration(self):
        """Duration of current playing media in seconds."""
        if self.now_playing_item and self.media_content_type:
            try:
                return int(self.now_playing_item['RunTimeTicks']) / 10000000
            except KeyError:
                return None

    @property
    def media_position(self):
        """Position of current playing media in seconds."""
        return self.media_status_last_position

    @property
    def media_position_updated_at(self):
        """
        When was the position of the current playing media valid.

        Returns value from homeassistant.util.dt.utcnow().
        """
        return self.media_status_received

    @property
    def media_image_url(self):
        """Image url of current playing media."""
        if self.now_playing_item is not None:
            try:
                return self.client.get_image(
                    self.now_playing_item['ThumbItemId'], 'Thumb', 0)
            except KeyError:
                try:
                    return self.client.get_image(
                        self.now_playing_item[
                            'PrimaryImageItemId'], 'Primary', 0)
                except KeyError:
                    return None

    @property
    def media_title(self):
        """Title of current playing media."""
        # find a string we can use as a title
        if self.now_playing_item is not None:
            return self.now_playing_item['Name']

    @property
    def media_season(self):
        """Season of curent playing media (TV Show only)."""
        if self.now_playing_item is not None and \
           'ParentIndexNumber' in self.now_playing_item:
            return self.now_playing_item['ParentIndexNumber']

    @property
    def media_series_title(self):
        """The title of the series of current playing media (TV Show only)."""
        if self.now_playing_item is not None and \
           'SeriesName' in self.now_playing_item:
            return self.now_playing_item['SeriesName']

    @property
    def media_episode(self):
        """Episode of current playing media (TV Show only)."""
        if self.now_playing_item is not None and \
           'IndexNumber' in self.now_playing_item:
            return self.now_playing_item['IndexNumber']

    @property
    def supported_media_commands(self):
        """Flag of media commands that are supported."""
        if self.supports_remote_control:
            return SUPPORT_EMBY
        else:
            return None

    def media_play(self):
        """Send play command."""
        if self.supports_remote_control:
            self.client.play(self.session)

    def media_pause(self):
        """Send pause command."""
        if self.supports_remote_control:
            self.client.pause(self.session)

    def media_next_track(self):
        """Send next track command."""
        self.client.next_track(self.session)

    def media_previous_track(self):
        """Send previous track command."""
        self.client.previous_track(self.session)
