#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
The Plex Companion master python file
"""
from logging import getLogger

import xbmc

from ..plex_api import API
from .. import utils
from ..utils import cast
from .. import plex_functions as PF
from .. import playlist_func as PL
from .. import playback
from .. import json_rpc as js
from .. import variables as v
from .. import app
from .. import exceptions


log = getLogger('PLEX.companion.processing')


def update_playqueue_from_PMS(playqueue,
                              playqueue_id=None,
                              repeat=None,
                              offset=None,
                              transient_token=None,
                              start_plex_id=None):
    """
    Completely updates the Kodi playqueue with the new Plex playqueue. Pass
    in playqueue_id if we need to fetch a new playqueue

    repeat = 0, 1, 2
    offset = time offset in Plextime (milliseconds)
    """
    log.info('New playqueue %s received from Plex companion with offset '
             '%s, repeat %s, start_plex_id %s',
             playqueue_id, offset, repeat, start_plex_id)
    # Safe transient token from being deleted
    if transient_token is None:
        transient_token = playqueue.plex_transient_token
    with app.APP.lock_playqueues:
        try:
            xml = PL.get_PMS_playlist(playqueue, playqueue_id)
        except exceptions.PlaylistError:
            log.error('Could now download playqueue %s', playqueue_id)
            return
        if playqueue.id == playqueue_id:
            # This seems to be happening ONLY if a Plex Companion device
            # reconnects and Kodi is already playing something - silly, really
            # For all other cases, a new playqueue is generated by Plex
            log.debug('Update for existing playqueue detected')
            return
        playqueue.clear()
        # Get new metadata for the playqueue first
        try:
            PL.get_playlist_details_from_xml(playqueue, xml)
        except exceptions.PlaylistError:
            log.error('Could not get playqueue ID %s', playqueue_id)
            return
        playqueue.repeat = 0 if not repeat else int(repeat)
        playqueue.plex_transient_token = transient_token
        playback.play_xml(playqueue,
                          xml,
                          offset=offset,
                          start_plex_id=start_plex_id)


def process_node(key, transient_token, offset):
    """
    E.g. watch later initiated by Companion. Basically navigating Plex
    """
    app.CONN.plex_transient_token = transient_token
    params = {
        'mode': 'plex_node',
        'key': f'{{server}}{key}',
        'offset': offset
    }
    handle = f'RunPlugin(plugin://{utils.extend_url(v.ADDON_ID, params)})'
    xbmc.executebuiltin(handle)


def process_playlist(containerKey, typus, key, offset, token):
    # Get the playqueue ID
    _, container_key, query = PF.ParseContainerKey(containerKey)
    try:
        playqueue = app.PLAYQUEUES.from_plex_type(typus)
    except ValueError:
        # E.g. Plex web does not supply the media type
        # Still need to figure out the type (video vs. music vs. pix)
        xml = PF.GetPlexMetadata(key)
        try:
            xml[0].attrib
        except (AttributeError, IndexError, TypeError):
            log.error('Could not download Plex metadata')
            return
        api = API(xml[0])
        playqueue = app.PLAYQUEUES.from_plex_type(api.plex_type)
    if key:
        _, key, _ = PF.ParseContainerKey(key)
    update_playqueue_from_PMS(playqueue,
                              playqueue_id=container_key,
                              repeat=query.get('repeat'),
                              offset=utils.cast(int, offset),
                              transient_token=token,
                              start_plex_id=key)


def process_streams(plex_type, video_stream_id, audio_stream_id,
                    subtitle_stream_id):
    """
    Plex Companion client adjusted audio or subtitle stream
    """
    playqueue = app.PLAYQUEUES.from_plex_type(plex_type)
    pos = js.get_position(playqueue.playlistid)
    playqueue.items[pos].on_plex_stream_change(video_stream_id,
                                               audio_stream_id,
                                               subtitle_stream_id)


def process_refresh(playqueue_id):
    """
    example data: {'playQueueID': '8475', 'commandID': '11'}
    """
    xml = PL.get_pms_playqueue(playqueue_id)
    if xml is None:
        return
    if len(xml) == 0:
        log.debug('Empty playqueue received - clearing playqueue')
        plex_type = PL.get_plextype_from_xml(xml)
        if plex_type is None:
            return
        playqueue = app.PLAYQUEUES.from_plex_type(plex_type)
        playqueue.clear()
        return
    playqueue = app.PLAYQUEUES.from_plex_type(xml[0].attrib['type'])
    update_playqueue_from_PMS(playqueue, playqueue_id)


def skip_to(playqueue_item_id, key):
    """
    Skip to a specific playlist position.

    Does not seem to be implemented yet by Plex!
    """
    _, plex_id = PF.GetPlexKeyNumber(key)
    log.debug('Skipping to playQueueItemID %s, plex_id %s',
              playqueue_item_id, plex_id)
    found = True
    for player in list(js.get_players().values()):
        playqueue = app.PLAYQUEUES[player['playerid']]
        for i, item in enumerate(playqueue.items):
            if item.id == playqueue_item_id:
                found = True
                break
        else:
            for i, item in enumerate(playqueue.items):
                if item.plex_id == plex_id:
                    found = True
                    break
        if found is True:
            app.APP.player.play(playqueue.kodi_pl, None, False, i)
        else:
            log.error('Item not found to skip to')


def convert_xml_to_params(xml):
    new_params = dict(xml.attrib)
    for key in xml.attrib:
        if key.startswith('query'):
            new_params[key[5].lower() + key[6:]] = xml.get(key)
            del new_params[key]
    return new_params


def process_command(cmd=None, path=None, params=None):
    """cmd: a "Command" etree xml"""
    path = cmd.get('path') if cmd is not None else path
    if not path.startswith('/'):
        path = '/' + path
    if params is None:
        params = convert_xml_to_params(cmd)
    if path == '/player/playback/playMedia' and \
            params.get('address') == 'node.plexapp.com':
        process_node(cmd.get('queryKey'),
                     cmd.get('queryToken'),
                     cmd.get('queryOffset') or 0)
    elif path == '/player/playback/playMedia':
        with app.APP.lock_playqueues:
            process_playlist(params.get('containerKey'),
                             params.get('type'),
                             params.get('key'),
                             params.get('offset'),
                             params.get('token'))
    elif path == '/player/playback/refreshPlayQueue':
        with app.APP.lock_playqueues:
            process_refresh(params.get('playQueueID'))
    elif path == '/player/playback/setParameters':
        js.set_volume(int(params.get('volume')))
    elif path == '/player/playback/play':
        js.play()
    elif path == '/player/playback/pause':
        js.pause()
    elif path == '/player/playback/stop':
        js.stop()
    elif path == '/player/playback/seekTo':
        js.seek_to(float(params.get('offset', 0.0)) / 1000.0)
    elif path == '/player/playback/stepForward':
        js.smallforward()
    elif path == '/player/playback/stepBack':
        js.smallbackward()
    elif path == '/player/playback/skipNext':
        js.skipnext()
    elif path == '/player/playback/skipPrevious':
        js.skipprevious()
    elif path == '/player/playback/skipTo':
        skip_to(params.get('playQueueItemID'), params.get('key'))
    elif path == '/player/navigation/moveUp':
        js.input_up()
    elif path == '/player/navigation/moveDown':
        js.input_down()
    elif path == '/player/navigation/moveLeft':
        js.input_left()
    elif path == '/player/navigation/moveRight':
        js.input_right()
    elif path == '/player/navigation/select':
        js.input_select()
    elif path == '/player/navigation/home':
        js.input_home()
    elif path == '/player/navigation/back':
        js.input_back()
    elif path == '/player/playback/setStreams':
        process_streams(params.get('type'),
                        cast(int, params.get('videoStreamID')),
                        cast(int, params.get('audioStreamID')),
                        cast(int, params.get('subtitleStreamID')))
    elif path == '/player/timeline/subscribe':
        pass
    elif path == '/player/timeline/unsubscribe':
        pass
    else:
        if cmd is None:
            log.error('Unknown request_path: %s with params %s', path, params)
        else:
            log.error('Unknown Plex companion path/command: %s: %s',
                      cmd.tag, cmd.attrib)
        return False
    return True
