# -*- coding: utf-8 -*-
from __future__ import division, absolute_import, print_function, unicode_literals

#################################################################################################

import json
import sys
import os

from six import iteritems
from six.moves.urllib.parse import parse_qsl, urlencode
from kodi_six import xbmc, xbmcvfs, xbmcgui, xbmcplugin, xbmcaddon

from .. import client
from ..database import reset, get_sync, Database, jellyfin_db, get_credentials
from ..objects import Objects, Actions
from ..helper import translate, event, settings, window, dialog, api, JSONRPC, LazyLogger
from ..helper.utils import JsonDebugPrinter, translate_path, kodi_version
from ..jellyfin import Jellyfin

#################################################################################################

LOG = LazyLogger(__name__)

ADDON_BASE_URL = sys.argv[0]
try:
    PROCESS_HANDLE = int(sys.argv[1])
    QUERY_STRING = sys.argv[2]
except IndexError:
    pass

#################################################################################################


class Events(object):

    def __init__(self):

        ''' Parse the parameters. Reroute to our service.py
            where user is fully identified already.
        '''
        base_url = ADDON_BASE_URL
        path = QUERY_STRING

        try:
            params = dict(parse_qsl(path[1:]))
        except Exception:
            params = {}

        mode = params.get('mode')
        server = params.get('server')

        if server == 'None':
            server = None

        jellyfin_client = Jellyfin(server).get_client()
        api_client = jellyfin_client.jellyfin

        addon_data = translate_path("special://profile/addon_data/plugin.video.jellyfin/data.json")
        try:
            with open(addon_data, 'rb') as infile:
                data = json.load(infile)

            server_data = data['Servers'][0]
            api_client.config.data['auth.server'] = server_data.get('address')
            api_client.config.data['auth.server-name'] = server_data.get('Name')
            api_client.config.data['auth.user_id'] = server_data.get('UserId')
            api_client.config.data['auth.token'] = server_data.get('AccessToken')
        except Exception as e:
            LOG.warning('Addon appears to not be configured yet: {}'.format(e))

        LOG.info("path: %s params: %s", path, JsonDebugPrinter(params))

        if '/extrafanart' in base_url:

            jellyfin_path = path[1:]
            jellyfin_id = params.get('id')
            get_fanart(jellyfin_id, jellyfin_path, server, api_client)

        elif '/Extras' in base_url or '/VideoFiles' in base_url:

            jellyfin_path = path[1:]
            jellyfin_id = params.get('id')
            get_video_extras(jellyfin_id, jellyfin_path, server, api_client)

        elif mode == 'play':

            item = api_client.get_item(params['id'])
            item["resumePlayback"] = sys.argv[3].split(":")[1] == "true"
            Actions(server, api_client).play(item, params.get('dbid'), params.get('transcode') == 'true', playlist=params.get('playlist') == 'true')

        elif mode == 'playlist':
            api_client.post_session(api_client.config.data['app.session'], "Playing", {
                'PlayCommand': "PlayNow",
                'ItemIds': params['id'],
                'StartPositionTicks': 0
            })
        elif mode == 'deviceid':
            client.reset_device_id()
        elif mode == 'reset':
            reset()
        elif mode == 'delete':
            delete_item()
        elif mode == 'refreshboxsets':
            event('SyncLibrary', {'Id': "Boxsets:Refresh"})
        elif mode == 'nextepisodes':
            get_next_episodes(params['id'], params['limit'])
        elif mode == 'browse':
            browse(params.get('type'), params.get('id'), params.get('folder'), server, api_client)
        elif mode == 'synclib':
            event('SyncLibrary', {'Id': params.get('id')})
        elif mode == 'updatelib':
            event('SyncLibrary', {'Id': params.get('id'), 'Update': True})
        elif mode == 'repairlib':
            event('RepairLibrary', {'Id': params.get('id')})
        elif mode == 'removelib':
            event('RemoveLibrary', {'Id': params.get('id')})
        elif mode == 'repairlibs':
            event('RepairLibrarySelection')
        elif mode == 'updatelibs':
            event('SyncLibrarySelection')
        elif mode == 'removelibs':
            event('RemoveLibrarySelection')
        elif mode == 'addlibs':
            event('AddLibrarySelection')
        elif mode == 'addserver':
            event('AddServer')
        elif mode == 'login':
            event('ServerConnect', {'Id': server})
        elif mode == 'removeserver':
            event('RemoveServer', {'Id': server})
        elif mode == 'settings':
            xbmc.executebuiltin('Addon.OpenSettings(plugin.video.jellyfin)')
        elif mode == 'adduser':
            add_user(api_client)
        elif mode == 'updatepassword':
            event('UpdatePassword')
        elif mode == 'thememedia':
            get_themes(api_client)
        elif mode == 'managelibs':
            manage_libraries()
        elif mode == 'backup':
            backup()
        elif mode == 'restartservice':
            window('jellyfin.restart.bool', True)
        elif mode is None and not params:
            # Used when selecting "Browse" from a context menu, see #548
            item_id = base_url.strip('/').split('/')[-1]
            browse('', item_id, None, server, api_client)
        else:
            listing()


def listing():

    ''' Display all jellyfin nodes and dynamic entries when appropriate.
    '''
    total = int(window('Jellyfin.nodes.total') or 0)
    sync = get_sync()
    whitelist = [x.replace('Mixed:', "") for x in sync['Whitelist']]
    servers = get_credentials()['Servers'][1:]

    for i in range(total):

        window_prop = "Jellyfin.nodes.%s" % i
        path = window('%s.index' % window_prop)

        if not path:
            path = window('%s.content' % window_prop) or window('%s.path' % window_prop)

        label = window('%s.title' % window_prop)
        node = window('%s.type' % window_prop)
        artwork = window('%s.artwork' % window_prop)
        view_id = window('%s.id' % window_prop)
        context = []

        if view_id and node in ('movies', 'tvshows', 'musicvideos', 'music', 'mixed') and view_id not in whitelist:
            label = "%s %s" % (label, translate(33166))
            context.append((translate(33123), "RunPlugin(plugin://plugin.video.jellyfin/?mode=synclib&id=%s)" % view_id))

        if view_id and node in ('movies', 'tvshows', 'musicvideos', 'music') and view_id in whitelist:

            context.append((translate(33136), "RunPlugin(plugin://plugin.video.jellyfin/?mode=updatelib&id=%s)" % view_id))
            context.append((translate(33132), "RunPlugin(plugin://plugin.video.jellyfin/?mode=repairlib&id=%s)" % view_id))
            context.append((translate(33133), "RunPlugin(plugin://plugin.video.jellyfin/?mode=removelib&id=%s)" % view_id))

        LOG.debug("--[ listing/%s/%s ] %s", node, label, path)

        if path:
            directory(label, path, artwork=artwork, context=context)

    for server in servers:
        context = []

        if server.get('ManualAddress'):
            context.append((translate(33141), "RunPlugin(plugin://plugin.video.jellyfin/?mode=removeserver&server=%s)" % server['Id']))

        if 'AccessToken' not in server:
            directory("%s (%s)" % (server['Name'], translate(30539)), "plugin://plugin.video.jellyfin/?mode=login&server=%s" % server['Id'], False, context=context)
        else:
            directory(server['Name'], "plugin://plugin.video.jellyfin/?mode=browse&server=%s" % server['Id'], context=context)

    directory(translate(33194), "plugin://plugin.video.jellyfin/?mode=managelibs", True)
    directory(translate(33134), "plugin://plugin.video.jellyfin/?mode=addserver", False)
    directory(translate(33054), "plugin://plugin.video.jellyfin/?mode=adduser", False)
    directory(translate(5), "plugin://plugin.video.jellyfin/?mode=settings", False)
    directory(translate(33161), "plugin://plugin.video.jellyfin/?mode=updatepassword", False)
    directory(translate(33058), "plugin://plugin.video.jellyfin/?mode=reset", False)
    directory(translate(33180), "plugin://plugin.video.jellyfin/?mode=restartservice", False)

    if settings('backupPath'):
        directory(translate(33092), "plugin://plugin.video.jellyfin/?mode=backup", False)

    xbmcplugin.setContent(PROCESS_HANDLE, 'files')
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)


def directory(label, path, folder=True, artwork=None, fanart=None, context=None):

    ''' Add directory listitem. context should be a list of tuples [(label, action)*]
    '''
    li = dir_listitem(label, path, artwork, fanart)

    if context:
        li.addContextMenuItems(context)

    xbmcplugin.addDirectoryItem(PROCESS_HANDLE, path, li, folder)

    return li


def dir_listitem(label, path, artwork=None, fanart=None):

    ''' Gets the icon paths for default node listings
    '''
    li = xbmcgui.ListItem(label, path=path)
    li.setArt({
        "thumb": artwork or "special://home/addons/plugin.video.jellyfin/resources/icon.png",
        "fanart": fanart or "special://home/addons/plugin.video.jellyfin/resources/fanart.png",
        "landscape": artwork or fanart or "special://home/addons/plugin.video.jellyfin/resources/fanart.png",
    })

    return li


def manage_libraries():

    directory(translate(33098), "plugin://plugin.video.jellyfin/?mode=refreshboxsets", False)
    directory(translate(33154), "plugin://plugin.video.jellyfin/?mode=addlibs", False)
    directory(translate(33139), "plugin://plugin.video.jellyfin/?mode=updatelibs", False)
    directory(translate(33140), "plugin://plugin.video.jellyfin/?mode=repairlibs", False)
    directory(translate(33184), "plugin://plugin.video.jellyfin/?mode=removelibs", False)
    directory(translate(33060), "plugin://plugin.video.jellyfin/?mode=thememedia", False)

    xbmcplugin.setContent(PROCESS_HANDLE, 'files')
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)


def browse(media, view_id=None, folder=None, server_id=None, api_client=None):

    ''' Browse content dynamically.
    '''
    LOG.info("--[ v:%s/%s ] %s", view_id, media, folder)

    if not window('jellyfin_online.bool') and server_id is None:

        monitor = xbmc.Monitor()

        for _i in range(300):
            if window('jellyfin_online.bool'):
                break
            elif monitor.waitForAbort(0.1):
                return
        else:
            LOG.error("Default server is not online.")

            return

    folder = folder.lower() if folder else None

    if folder is None and media in ('homevideos', 'movies', 'books', 'audiobooks'):
        return browse_subfolders(media, view_id, server_id)

    if folder and folder == 'firstletter':
        return browse_letters(media, view_id, server_id)

    if view_id:

        view = api_client.get_item(view_id)
        xbmcplugin.setPluginCategory(PROCESS_HANDLE, view['Name'])

    content_type = "files"

    if media in ('tvshows', 'seasons', 'episodes', 'movies', 'musicvideos', 'songs', 'albums'):
        content_type = media
    elif media in ('homevideos', 'photos'):
        content_type = "images"
    elif media in ('books', 'audiobooks'):
        content_type = "videos"
    elif media == 'music':
        content_type = "artists"

    if folder == 'recentlyadded':
        listing = api_client.get_recently_added(None, view_id, None)
    elif folder == 'genres':
        listing = api_client.get_genres(view_id)
    elif media == 'livetv':
        listing = api_client.get_channels()
    elif folder == 'unwatched':
        listing = get_filtered_section(view_id, None, None, None, None, None, ['IsUnplayed'], None, server_id, api_client)
    elif folder == 'favorite':
        listing = get_filtered_section(view_id, None, None, None, None, None, ['IsFavorite'], None, server_id, api_client)
    elif folder == 'inprogress':
        listing = get_filtered_section(view_id, None, None, None, None, None, ['IsResumable'], None, server_id, api_client)
    elif folder == 'boxsets':
        listing = get_filtered_section(view_id, get_media_type('boxsets'), None, True, None, None, None, None, server_id, api_client)
    elif folder == 'random':
        listing = get_filtered_section(view_id, get_media_type(content_type), 25, True, "Random", None, None, None, server_id, api_client)
    elif (folder or "").startswith('firstletter-'):
        listing = get_filtered_section(view_id, get_media_type(content_type), None, None, None, None, None, {'NameStartsWith': folder.split('-')[1]}, server_id, api_client)
    elif (folder or "").startswith('genres-'):
        listing = get_filtered_section(view_id, get_media_type(content_type), None, None, None, None, None, {'GenreIds': folder.split('-')[1]}, server_id, api_client)
    elif folder == 'favepisodes':
        listing = get_filtered_section(None, get_media_type(content_type), 25, None, None, None, ['IsFavorite'], None, server_id, api_client)
    elif folder and media == 'playlists':
        listing = get_filtered_section(folder, get_media_type(content_type), None, False, 'None', None, None, None, server_id, api_client)
    elif media == 'homevideos':
        listing = get_filtered_section(folder or view_id, get_media_type(content_type), None, False, None, None, None, None, server_id, api_client)
    elif media in ['movies', 'episodes']:
        listing = get_filtered_section(folder or view_id, get_media_type(content_type), None, True, None, None, None, None, server_id, api_client)
    elif media in ('boxset', 'library'):
        listing = get_filtered_section(folder or view_id, None, None, True, None, None, None, None, server_id, api_client)
    elif media == 'boxsets':
        listing = get_filtered_section(folder or view_id, None, None, False, None, None, ['Boxsets'], None, server_id, api_client)
    elif media == 'tvshows':
        listing = get_filtered_section(folder or view_id, get_media_type(content_type), None, True, None, None, None, None, server_id, api_client)
    elif media == 'seasons':
        listing = api_client.get_seasons(folder)
    elif media != 'files':
        listing = get_filtered_section(folder or view_id, get_media_type(content_type), None, False, None, None, None, None, server_id, api_client)
    else:
        listing = get_filtered_section(folder or view_id, None, None, False, None, None, None, None, server_id, api_client)

    if listing:

        actions = Actions(server_id, api_client)
        list_li = []
        listing = listing if type(listing) == list else listing.get('Items', [])

        for item in listing:

            li = xbmcgui.ListItem()
            li.setProperty('jellyfinid', item['Id'])
            li.setProperty('jellyfinserver', server_id)
            actions.set_listitem(item, li)

            if item.get('IsFolder'):

                params = {
                    'id': view_id or item['Id'],
                    'mode': "browse",
                    'type': get_folder_type(item, media) or media,
                    'folder': item['Id'],
                    'server': server_id
                }
                path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
                context = []

                if item['Type'] in ('Series', 'Season', 'Playlist'):
                    context.append(("Play", "RunPlugin(plugin://plugin.video.jellyfin/?mode=playlist&id=%s&server=%s)" % (item['Id'], server_id)))

                if item['UserData']['Played']:
                    context.append((translate(16104), "RunPlugin(plugin://plugin.video.jellyfin/?mode=unwatched&id=%s&server=%s)" % (item['Id'], server_id)))
                else:
                    context.append((translate(16103), "RunPlugin(plugin://plugin.video.jellyfin/?mode=watched&id=%s&server=%s)" % (item['Id'], server_id)))

                li.addContextMenuItems(context)
                list_li.append((path, li, True))

            elif item['Type'] == 'Genre':

                params = {
                    'id': view_id or item['Id'],
                    'mode': "browse",
                    'type': get_folder_type(item, media) or media,
                    'folder': 'genres-%s' % item['Id'],
                    'server': server_id
                }
                path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
                list_li.append((path, li, True))

            else:
                if item['Type'] not in ('Photo', 'PhotoAlbum'):
                    params = {
                        'id': item['Id'],
                        'mode': "play",
                        'server': server_id
                    }
                    path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
                    li.setProperty('path', path)
                    context = [(translate(13412), "RunPlugin(plugin://plugin.video.jellyfin/?mode=playlist&id=%s&server=%s)" % (item['Id'], server_id))]

                    if item['UserData']['Played']:
                        context.append((translate(16104), "RunPlugin(plugin://plugin.video.jellyfin/?mode=unwatched&id=%s&server=%s)" % (item['Id'], server_id)))
                    else:
                        context.append((translate(16103), "RunPlugin(plugin://plugin.video.jellyfin/?mode=watched&id=%s&server=%s)" % (item['Id'], server_id)))

                    li.addContextMenuItems(context)

                list_li.append((li.getProperty('path'), li, False))

        xbmcplugin.addDirectoryItems(PROCESS_HANDLE, list_li, len(list_li))

    if content_type == 'images':
        xbmcplugin.addSortMethod(PROCESS_HANDLE, xbmcplugin.SORT_METHOD_VIDEO_TITLE)
        xbmcplugin.addSortMethod(PROCESS_HANDLE, xbmcplugin.SORT_METHOD_DATE)
        xbmcplugin.addSortMethod(PROCESS_HANDLE, xbmcplugin.SORT_METHOD_VIDEO_RATING)
        xbmcplugin.addSortMethod(PROCESS_HANDLE, xbmcplugin.SORT_METHOD_VIDEO_RUNTIME)

    xbmcplugin.setContent(PROCESS_HANDLE, content_type)
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)


def browse_subfolders(media, view_id, server_id=None):

    ''' Display submenus for jellyfin views.
    '''
    from ..views import DYNNODES

    view = Jellyfin(server_id).get_client().jellyfin.get_item(view_id)
    xbmcplugin.setPluginCategory(PROCESS_HANDLE, view['Name'])
    nodes = DYNNODES[media]

    for node in nodes:

        params = {
            'id': view_id,
            'mode': "browse",
            'type': media,
            'folder': view_id if node[0] == 'all' else node[0],
            'server': server_id
        }
        path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
        directory(node[1] or view['Name'], path)

    xbmcplugin.setContent(PROCESS_HANDLE, 'files')
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)


def browse_letters(media, view_id, server_id=None):

    ''' Display letters as options.
    '''
    letters = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ"

    view = Jellyfin(server_id).get_client().jellyfin.get_item(view_id)
    xbmcplugin.setPluginCategory(PROCESS_HANDLE, view['Name'])

    for node in letters:

        params = {
            'id': view_id,
            'mode': "browse",
            'type': media,
            'folder': 'firstletter-%s' % node,
            'server': server_id
        }
        path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
        directory(node, path)

    xbmcplugin.setContent(PROCESS_HANDLE, 'files')
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)


def get_folder_type(item, content_type=None):

    media = item['Type']

    if media == 'Series':
        return "seasons"
    elif media == 'Season':
        return "episodes"
    elif media == 'BoxSet':
        return "boxset"
    elif media == 'MusicArtist':
        return "albums"
    elif media == 'MusicAlbum':
        return "songs"
    elif media == 'CollectionFolder':
        return item.get('CollectionType', 'library')
    elif media == 'Folder' and content_type == 'music':
        return "albums"


def get_media_type(media):

    if media == 'movies':
        return "Movie,BoxSet"
    elif media == 'homevideos':
        return "Video,Folder,PhotoAlbum,Photo"
    elif media == 'episodes':
        return "Episode"
    elif media == 'boxsets':
        return "BoxSet"
    elif media == 'tvshows':
        return "Series"
    elif media == 'music':
        return "MusicArtist,MusicAlbum,Audio"


def get_fanart(item_id, path, server_id=None, api_client=None):

    ''' Get extra fanart for listitems. This is called by skinhelper.
        Images are stored locally, due to the Kodi caching system.
    '''
    if not item_id and 'plugin.video.jellyfin' in path:
        item_id = path.split('/')[-2]

    if not item_id:
        return

    LOG.info("[ extra fanart ] %s", item_id)
    objects = Objects()
    list_li = []
    directory = translate_path("special://thumbnails/jellyfin/%s/" % item_id)

    if not xbmcvfs.exists(directory):

        xbmcvfs.mkdirs(directory)
        item = api_client.get_item(item_id)
        obj = objects.map(item, 'Artwork')
        backdrops = api.API(item).get_all_artwork(obj)
        tags = obj['BackdropTags']

        for index, backdrop in enumerate(backdrops):

            tag = tags[index]
            fanart = os.path.join(directory, "fanart%s.jpg" % tag)
            li = xbmcgui.ListItem(tag, path=fanart)
            xbmcvfs.copy(backdrop, fanart)
            list_li.append((fanart, li, False))
    else:
        LOG.debug("cached backdrop found")
        dirs, files = xbmcvfs.listdir(directory)

        for file in files:
            fanart = os.path.join(directory, file)
            li = xbmcgui.ListItem(file, path=fanart)
            list_li.append((fanart, li, False))

    xbmcplugin.addDirectoryItems(PROCESS_HANDLE, list_li, len(list_li))
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)


def get_video_extras(item_id, path, server_id=None, api_client=None):

    ''' Returns the video files for the item as plugin listing, can be used
        to browse actual files or video extras, etc.
    '''
    if not item_id and 'plugin.video.jellyfin' in path:
        item_id = path.split('/')[-2]

    if not item_id:
        return

    # TODO implement????
    # Jellyfin(server_id).get_client().jellyfin.get_item(item_id)

    """
    def getVideoFiles(jellyfinId,jellyfinPath):
        #returns the video files for the item as plugin listing, can be used for browsing the actual files or videoextras etc.
        jellyfin = jellyfinserver.Read_JellyfinServer()
        if not jellyfinId:
            if "plugin.video.jellyfin" in jellyfinPath:
                jellyfinId = jellyfinPath.split("/")[-2]
        if jellyfinId:
            item = jellyfin.getItem(jellyfinId)
            putils = playutils.PlayUtils(item)
            if putils.isDirectPlay():
                #only proceed if we can access the files directly. TODO: copy local on the fly if accessed outside
                filelocation = putils.directPlay()
                if not filelocation.endswith("/"):
                    filelocation = filelocation.rpartition("/")[0]
                dirs, files = xbmcvfs.listdir(filelocation)
                for file in files:
                    file = filelocation + file
                    li = xbmcgui.ListItem(file, path=file)
                    xbmcplugin.addDirectoryItem(handle=PROCESS_HANDLE, url=file, listitem=li)
                for dir in dirs:
                    dir = filelocation + dir
                    li = xbmcgui.ListItem(dir, path=dir)
                    xbmcplugin.addDirectoryItem(handle=PROCESS_HANDLE, url=dir, listitem=li, isFolder=True)
        #xbmcplugin.endOfDirectory(PROCESS_HANDLE)
    """


def get_next_episodes(item_id, limit):

    ''' Only for synced content.
    '''
    with Database('jellyfin') as jellyfindb:

        db = jellyfin_db.JellyfinDatabase(jellyfindb.cursor)
        library = db.get_view_name(item_id)

        if not library:
            return

    result = JSONRPC('VideoLibrary.GetTVShows').execute({
        'sort': {'order': "descending", 'method': "lastplayed"},
        'filter': {
            'and': [
                {'operator': "true", 'field': "inprogress", 'value': ""},
                {'operator': "is", 'field': "tag", 'value': "%s" % library}
            ]},
        'properties': ['title', 'studio', 'mpaa', 'file', 'art']
    })

    try:
        items = result['result']['tvshows']
    except (KeyError, TypeError):
        return

    list_li = []

    for item in items:
        if settings('ignoreSpecialsNextEpisodes.bool'):
            params = {
                'tvshowid': item['tvshowid'],
                'sort': {'method': "episode"},
                'filter': {
                    'and': [
                        {'operator': "lessthan", 'field': "playcount", 'value': "1"},
                        {'operator': "greaterthan", 'field': "season", 'value': "0"}
                    ]},
                'properties': [
                    "title", "playcount", "season", "episode", "showtitle",
                    "plot", "file", "rating", "resume", "tvshowid", "art",
                    "streamdetails", "firstaired", "runtime", "writer",
                    "dateadded", "lastplayed"
                ],
                'limits': {"end": 1}
            }
        else:
            params = {
                'tvshowid': item['tvshowid'],
                'sort': {'method': "episode"},
                'filter': {'operator': "lessthan", 'field': "playcount", 'value': "1"},
                'properties': [
                    "title", "playcount", "season", "episode", "showtitle",
                    "plot", "file", "rating", "resume", "tvshowid", "art",
                    "streamdetails", "firstaired", "runtime", "writer",
                    "dateadded", "lastplayed"
                ],
                'limits': {"end": 1}
            }

        result = JSONRPC('VideoLibrary.GetEpisodes').execute(params)

        try:
            episodes = result['result']['episodes']
        except (KeyError, TypeError):
            pass
        else:
            for episode in episodes:

                li = create_listitem(episode)
                list_li.append((episode['file'], li))

        if len(list_li) == limit:
            break

    xbmcplugin.addDirectoryItems(PROCESS_HANDLE, list_li, len(list_li))
    xbmcplugin.setContent(PROCESS_HANDLE, 'episodes')
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)


def create_listitem(item):

    ''' Listitem based on jsonrpc items.
    '''
    title = item['title']
    label2 = ""
    li = xbmcgui.ListItem(title)
    li.setProperty('IsPlayable', "true")

    metadata = {
        'Title': title,
        'duration': str(item['runtime'] / 60),
        'Plot': item['plot'],
        'Playcount': item['playcount']
    }

    if "showtitle" in item:
        metadata['TVshowTitle'] = item['showtitle']
        label2 = item['showtitle']

    if "episodeid" in item:
        # Listitem of episode
        metadata['mediatype'] = "episode"
        metadata['dbid'] = item['episodeid']

    # TODO: Review once Krypton is RC - probably no longer needed if there's dbid
    if "episode" in item:
        episode = item['episode']
        metadata['Episode'] = episode

    if "season" in item:
        season = item['season']
        metadata['Season'] = season

    if season and episode:
        episodeno = "s%.2de%.2d" % (season, episode)
        li.setProperty('episodeno', episodeno)
        label2 = "%s - %s" % (label2, episodeno) if label2 else episodeno

    if "firstaired" in item:
        metadata['Premiered'] = item['firstaired']

    if "rating" in item:
        metadata['Rating'] = str(round(float(item['rating']), 1))

    if "director" in item:
        metadata['Director'] = " / ".join(item['director'])

    if "writer" in item:
        metadata['Writer'] = " / ".join(item['writer'])

    if "cast" in item:
        cast = []
        castandrole = []
        for person in item['cast']:
            name = person['name']
            cast.append(name)
            castandrole.append((name, person['role']))
        metadata['Cast'] = cast
        metadata['CastAndRole'] = castandrole

    li.setLabel2(label2)
    li.setInfo(type="Video", infoLabels=metadata)
    li.setProperty('resumetime', str(item['resume']['position']))
    li.setProperty('totaltime', str(item['resume']['total']))
    li.setArt(item['art'])
    li.setProperty('dbid', str(item['episodeid']))
    li.setProperty('fanart_image', item['art'].get('tvshow.fanart', ''))

    for key, value in iteritems(item['streamdetails']):
        for stream in value:
            li.addStreamInfo(key, stream)

    return li


def add_user(api_client):

    ''' Add or remove users from the default server session.
    '''
    if not window('jellyfin_online.bool'):
        return

    session = api_client.get_device(client.get_device_id())
    users = api_client.get_users()
    current = session[0]['AdditionalUsers']

    result = dialog("select", translate(33061), [translate(33062), translate(33063)] if current else [translate(33062)])

    if result < 0:
        return

    if not result:  # Add user
        eligible = [x for x in users if x['Id'] not in [current_user['UserId'] for current_user in current]]
        resp = dialog("select", translate(33064), [x['Name'] for x in eligible])

        if resp < 0:
            return

        user = eligible[resp]
        event('AddUser', {'Id': user['Id'], 'Add': True})
    else:  # Remove user
        resp = dialog("select", translate(33064), [x['UserName'] for x in current])

        if resp < 0:
            return

        user = current[resp]
        event('AddUser', {'Id': user['UserId'], 'Add': False})


def get_themes(api_client):

    ''' Add theme media locally, via strm. This is only for tv tunes.
        If another script is used, adjust this code.
    '''
    from ..helper.utils import normalize_string
    from ..helper.playutils import PlayUtils
    from ..helper.xmls import tvtunes_nfo

    library = translate_path("special://profile/addon_data/plugin.video.jellyfin/library")
    play = settings('useDirectPaths') == "1"

    if not xbmcvfs.exists(library + '/'):
        xbmcvfs.mkdir(library)

    if xbmc.getCondVisibility('System.HasAddon(script.tvtunes)'):

        tvtunes = xbmcaddon.Addon(id="script.tvtunes")
        tvtunes.setSetting('custom_path_enable', "true")
        tvtunes.setSetting('custom_path', library)
        LOG.info("TV Tunes custom path is enabled and set.")
    else:
        dialog("ok", "{jellyfin}", translate(33152))

        return

    with Database('jellyfin') as jellyfindb:
        all_views = jellyfin_db.JellyfinDatabase(jellyfindb.cursor).get_views()
        views = [x.view_id for x in all_views if x.media_type in ('movies', 'tvshows', 'mixed')]

    items = {}
    server = api_client.config.data['auth.server']

    for view in views:
        result = api_client.get_items_theme_video(view)

        for item in result['Items']:

            folder = normalize_string(item['Name'])
            items[item['Id']] = folder

        result = api_client.get_items_theme_song(view)

        for item in result['Items']:

            folder = normalize_string(item['Name'])
            items[item['Id']] = folder

    for item in items:

        nfo_path = os.path.join(library, items[item])
        nfo_file = os.path.join(nfo_path, "tvtunes.nfo")

        if not xbmcvfs.exists(nfo_path):
            xbmcvfs.mkdir(nfo_path)

        themes = api_client.get_themes(item)
        paths = []

        for theme in themes['ThemeVideosResult']['Items'] + themes['ThemeSongsResult']['Items']:
            putils = PlayUtils(theme, False, None, server, api_client)

            if play:
                paths.append(putils.direct_play(theme['MediaSources'][0]))
            else:
                paths.append(putils.direct_url(theme['MediaSources'][0]))

        tvtunes_nfo(nfo_file, paths)

    dialog("notification", heading="{jellyfin}", message=translate(33153), icon="{jellyfin}", time=1000, sound=False)


def delete_item():

    ''' Delete keymap action.
    '''
    from . import context

    context.Context(delete=True)


def backup():

    ''' Jellyfin backup.
    '''
    from ..helper.utils import delete_folder, copytree

    path = settings('backupPath')
    folder_name = "Kodi%s.%s" % (kodi_version(), xbmc.getInfoLabel('System.Date(dd-mm-yy)'))
    folder_name = dialog("input", heading=translate(33089), defaultt=folder_name)

    if not folder_name:
        return

    backup = os.path.join(path, folder_name)

    if xbmcvfs.exists(backup + '/'):
        if not dialog("yesno", "{jellyfin}", translate(33090)):

            return backup()

        delete_folder(backup)

    addon_data = translate_path("special://profile/addon_data/plugin.video.jellyfin")
    destination_data = os.path.join(backup, "addon_data", "plugin.video.jellyfin")
    destination_databases = os.path.join(backup, "Database")

    if not xbmcvfs.mkdirs(path) or not xbmcvfs.mkdirs(destination_databases):

        LOG.info("Unable to create all directories")
        dialog("notification", heading="{jellyfin}", icon="{jellyfin}", message=translate(33165), sound=False)

        return

    copytree(addon_data, destination_data)

    databases = Objects().objects

    db = translate_path(databases['jellyfin'])
    xbmcvfs.copy(db, os.path.join(destination_databases, db.rsplit('\\', 1)[1]))
    LOG.info("copied jellyfin.db")

    db = translate_path(databases['video'])
    filename = db.rsplit('\\', 1)[1]
    xbmcvfs.copy(db, os.path.join(destination_databases, filename))
    LOG.info("copied %s", filename)

    if settings('enableMusic.bool'):

        db = translate_path(databases['music'])
        filename = db.rsplit('\\', 1)[1]
        xbmcvfs.copy(db, os.path.join(destination_databases, filename))
        LOG.info("copied %s", filename)

    LOG.info("backup completed")
    dialog("ok", "{jellyfin}", "%s %s" % (translate(33091), backup))


def get_filtered_section(parent_id=None, media=None, limit=None, recursive=None, sort=None, sort_order=None,
                         filters=None, extra=None, server_id=None, api_client=None):

    ''' Get dynamic listings.
    '''
    params = {
        'ParentId': parent_id,
        'IncludeItemTypes': media,
        'IsMissing': False,
        'Recursive': recursive if recursive is not None else True,
        'Limit': limit,
        'SortBy': sort or "SortName",
        'SortOrder': sort_order or "Ascending",
        'ImageTypeLimit': 1,
        'IsVirtualUnaired': False,
        'Fields': browse_info()
    }
    if filters:
        if 'Boxsets' in filters:
            filters.remove('Boxsets')
            params['CollapseBoxSetItems'] = settings('groupedSets.bool')

        params['Filters'] = ','.join(filters)

    if settings('getCast.bool'):
        params['Fields'] += ",People"

    if media and 'Photo' in media:
        params['Fields'] += ",Width,Height"

    if extra is not None:
        params.update(extra)

    return api_client._get("Users/{UserId}/Items", params)


def browse_info():
    return (
        "DateCreated,EpisodeCount,SeasonCount,Path,Genres,Studios,Taglines,MediaStreams,Overview,Etag,"
        "ProductionLocations,Width,Height,RecursiveItemCount,ChildCount"
    )
