"""
homeassistant.components.media_player.itunes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides an interface to iTunes-API (https://github.com/maddox/itunes-api)

The iTunes media player will allow you to control your iTunes instance. You
can play/pause/next/previous/mute, adjust volume, etc.

In addition to controlling iTunes, your available AirPlay endpoints will be
added as media players as well. You can then individually address them append
turn them on, turn them off, or adjust their volume.

Configuration:

To use iTunes you will need to add something like the following to
your configuration.yaml file.

media_player:
  platform: itunes
  name: iTunes
  host: http://192.168.1.16
  port: 8181

Variables:

name
*Optional
The name of the device.

url
*Required
URL of your running version of iTunes-API. Example: http://192.168.1.50:8181

"""
import logging

from homeassistant.components.media_player import (
    MediaPlayerDevice, MEDIA_TYPE_MUSIC, SUPPORT_PAUSE, SUPPORT_SEEK,
    SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE, SUPPORT_PREVIOUS_TRACK,
    SUPPORT_NEXT_TRACK, SUPPORT_TURN_ON, SUPPORT_TURN_OFF,
    ATTR_ENTITY_PICTURE, ATTR_SUPPORTED_MEDIA_COMMANDS)
from homeassistant.const import (
    STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_OFF, STATE_ON)

import requests

_LOGGER = logging.getLogger(__name__)

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

SUPPORT_AIRPLAY = SUPPORT_VOLUME_SET | SUPPORT_TURN_ON | SUPPORT_TURN_OFF

DOMAIN = 'itunes'


class Itunes(object):
    """ itunes-api client. """

    def __init__(self, host, port):
        self.host = host
        self.port = port

    @property
    def _base_url(self):
        """ Returns the base url for endpoints. """
        return self.host + ":" + str(self.port)

    def _request(self, method, path, params=None):
        """ Makes the actual request and returns the parsed response. """
        url = self._base_url + path

        try:
            if method == 'GET':
                response = requests.get(url)
            elif method == "POST":
                response = requests.put(url, params)
            elif method == "PUT":
                response = requests.put(url, params)
            elif method == "DELETE":
                response = requests.delete(url)

            return response.json()
        except requests.exceptions.HTTPError:
            return {'player_state': 'error'}
        except requests.exceptions.RequestException:
            return {'player_state': 'offline'}

    def _command(self, named_command):
        """ Makes a request for a controlling command. """
        return self._request('PUT', '/' + named_command)

    def now_playing(self):
        """ Returns the current state. """
        return self._request('GET', '/now_playing')

    def set_volume(self, level):
        """ Sets the volume and returns the current state, level 0-100. """
        return self._request('PUT', '/volume', {'level': level})

    def set_muted(self, muted):
        """ Mutes and returns the current state, muted True or False. """
        return self._request('PUT', '/mute', {'muted': muted})

    def play(self):
        """ Sets playback to play and returns the current state. """
        return self._command('play')

    def pause(self):
        """ Sets playback to paused and returns the current state. """
        return self._command('pause')

    def next(self):
        """ Skips to the next track and returns the current state. """
        return self._command('next')

    def previous(self):
        """ Skips back and returns the current state. """
        return self._command('previous')

    def artwork_url(self):
        """ Returns a URL of the current track's album art. """
        return self._base_url + '/artwork'

    def airplay_devices(self):
        """ Returns a list of AirPlay devices. """
        return self._request('GET', '/airplay_devices')

    def airplay_device(self, device_id):
        """ Returns an AirPlay device. """
        return self._request('GET', '/airplay_devices/' + device_id)

    def toggle_airplay_device(self, device_id, toggle):
        """ Toggles airplay device on or off, id, toggle True or False. """
        command = 'on' if toggle else 'off'
        path = '/airplay_devices/' + device_id + '/' + command
        return self._request('PUT', path)

    def set_volume_airplay_device(self, device_id, level):
        """ Sets volume, returns current state of device, id,level 0-100. """
        path = '/airplay_devices/' + device_id + '/volume'
        return self._request('PUT', path, {'level': level})

# pylint: disable=unused-argument
# pylint: disable=abstract-method
# pylint: disable=too-many-instance-attributes


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

    add_devices([
        ItunesDevice(
            config.get('name', 'iTunes'),
            config.get('host'),
            config.get('port'),
            add_devices
        )
    ])


class ItunesDevice(MediaPlayerDevice):
    """ Represents a iTunes-API instance. """

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

    def __init__(self, name, host, port, add_devices):
        self._name = name
        self._host = host
        self._port = port
        self._add_devices = add_devices

        self.client = Itunes(self._host, self._port)

        self.current_volume = None
        self.muted = None
        self.current_title = None
        self.current_album = None
        self.current_artist = None
        self.current_playlist = None
        self.content_id = None

        self.player_state = None

        self.airplay_devices = {}

        self.update()

    def update_state(self, state_hash):
        """ Update all the state properties with the passed in dictionary. """
        self.player_state = state_hash.get('player_state', None)

        self.current_volume = state_hash.get('volume', 0)
        self.muted = state_hash.get('muted', None)
        self.current_title = state_hash.get('name', None)
        self.current_album = state_hash.get('album', None)
        self.current_artist = state_hash.get('artist', None)
        self.current_playlist = state_hash.get('playlist', None)
        self.content_id = state_hash.get('id', None)

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

    @property
    def state(self):
        """ Returns the state of the device. """

        if self.player_state == 'offline' or self.player_state is None:
            return 'offline'

        if self.player_state == 'error':
            return 'error'

        if self.player_state == 'stopped':
            return STATE_IDLE

        if self.player_state == 'paused':
            return STATE_PAUSED
        else:
            return STATE_PLAYING

    def update(self):
        """ Retrieve latest state. """
        now_playing = self.client.now_playing()
        self.update_state(now_playing)

        found_devices = self.client.airplay_devices()
        found_devices = found_devices.get('airplay_devices', [])

        new_devices = []

        for device_data in found_devices:
            device_id = device_data.get('id')

            if self.airplay_devices.get(device_id):
                # update it
                airplay_device = self.airplay_devices.get(device_id)
                airplay_device.update_state(device_data)
            else:
                # add it
                airplay_device = AirPlayDevice(device_id, self.client)
                airplay_device.update_state(device_data)
                self.airplay_devices[device_id] = airplay_device
                new_devices.append(airplay_device)

        if new_devices:
            self._add_devices(new_devices)

    @property
    def is_volume_muted(self):
        """ Boolean if volume is currently muted. """
        return self.muted

    @property
    def volume_level(self):
        """ Volume level of the media player (0..1). """
        return self.current_volume/100.0

    @property
    def media_content_id(self):
        """ Content ID of current playing media. """
        return self.content_id

    @property
    def media_content_type(self):
        """ Content type of current playing media. """
        return MEDIA_TYPE_MUSIC

    @property
    def media_image_url(self):
        """ Image url of current playing media. """

        if self.player_state in (STATE_PLAYING, STATE_IDLE, STATE_PAUSED) and \
           self.current_title is not None:
            return self.client.artwork_url()
        else:
            return 'https://cloud.githubusercontent.com/assets/260/9829355' \
                '/33fab972-58cf-11e5-8ea2-2ca74bdaae40.png'

    @property
    def media_title(self):
        """ Title of current playing media. """
        return self.current_title

    @property
    def media_artist(self):
        """ Artist of current playing media. (Music track only) """
        return self.current_artist

    @property
    def media_album_name(self):
        """ Album of current playing media. (Music track only) """
        return self.current_album

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

    def set_volume_level(self, volume):
        """ set volume level, range 0..1. """
        response = self.client.set_volume(int(volume * 100))
        self.update_state(response)

    def mute_volume(self, mute):
        """ mute (true) or unmute (false) media player. """
        response = self.client.set_muted(mute)
        self.update_state(response)

    def media_play(self):
        """ media_play media player. """
        response = self.client.play()
        self.update_state(response)

    def media_pause(self):
        """ media_pause media player. """
        response = self.client.pause()
        self.update_state(response)

    def media_next_track(self):
        """ media_next media player. """
        response = self.client.next()
        self.update_state(response)

    def media_previous_track(self):
        """ media_previous media player. """
        response = self.client.previous()
        self.update_state(response)


class AirPlayDevice(MediaPlayerDevice):
    """ Represents an AirPlay device via an iTunes-API instance. """

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

    def __init__(self, device_id, client):
        self._id = device_id

        self.client = client

        self.device_name = "AirPlay"
        self.kind = None
        self.active = False
        self.selected = False
        self.volume = 0
        self.supports_audio = False
        self.supports_video = False
        self.player_state = None

    def update_state(self, state_hash):
        """ Update all the state properties with the passed in dictionary. """

        if 'player_state' in state_hash:
            self.player_state = state_hash.get('player_state', None)

        if 'name' in state_hash:
            name = state_hash.get('name', '')
            self.device_name = (name + ' AirTunes Speaker').strip()

        if 'kind' in state_hash:
            self.kind = state_hash.get('kind', None)

        if 'active' in state_hash:
            self.active = state_hash.get('active', None)

        if 'selected' in state_hash:
            self.selected = state_hash.get('selected', None)

        if 'sound_volume' in state_hash:
            self.volume = state_hash.get('sound_volume', 0)

        if 'supports_audio' in state_hash:
            self.supports_audio = state_hash.get('supports_audio', None)

        if 'supports_video' in state_hash:
            self.supports_video = state_hash.get('supports_video', None)

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

    @property
    def state(self):
        """ Returns the state of the device. """

        if self.selected is True:
            return STATE_ON
        else:
            return STATE_OFF

    def update(self):
        """ Retrieve latest state. """

    @property
    def volume_level(self):
        return float(self.volume)/100.0

    @property
    def media_content_type(self):
        return MEDIA_TYPE_MUSIC

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

    @property
    def device_state_attributes(self):
        """ Return the state attributes. """
        state_attr = {}
        state_attr[ATTR_SUPPORTED_MEDIA_COMMANDS] = SUPPORT_AIRPLAY

        if self.state == STATE_OFF:
            state_attr[ATTR_ENTITY_PICTURE] = \
                ('https://cloud.githubusercontent.com/assets/260/9833073'
                 '/6eb5c906-5958-11e5-9b4a-472cdf36be16.png')
        else:
            state_attr[ATTR_ENTITY_PICTURE] = \
                ('https://cloud.githubusercontent.com/assets/260/9833072'
                 '/6eb13cce-5958-11e5-996f-e2aaefbc9a24.png')

        return state_attr

    def set_volume_level(self, volume):
        """ set volume level, range 0..1. """
        volume = int(volume * 100)
        response = self.client.set_volume_airplay_device(self._id, volume)
        self.update_state(response)

    def turn_on(self):
        """ Select AirPlay. """
        self.update_state({"selected": True})
        self.update_ha_state()
        response = self.client.toggle_airplay_device(self._id, True)
        self.update_state(response)

    def turn_off(self):
        """ Deselect AirPlay. """
        self.update_state({"selected": False})
        self.update_ha_state()
        response = self.client.toggle_airplay_device(self._id, False)
        self.update_state(response)
