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

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

import threading
import sys
import json
from datetime import timedelta

import xbmc
import xbmcgui
import xbmcplugin
import xbmcaddon

from ..helper import translate, playutils, api, window, settings, dialog
from ..dialogs import resume
from ..helper import LazyLogger
from ..jellyfin import Jellyfin
from ..helper.utils import translate_path

from .obj import Objects

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

LOG = LazyLogger(__name__)

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


class Actions(object):

    def __init__(self, server_id=None, api_client=None):

        self.server_id = server_id or None
        if not api_client:
            LOG.debug("No api client provided, attempting to use config file")
            jellyfin_client = Jellyfin(server_id).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))

        self.api_client = api_client
        self.server = self.api_client.config.data["auth.server"]

        self.stack = []

    def get_playlist(self, item):

        if item["Type"] == "Audio":
            return xbmc.PlayList(xbmc.PLAYLIST_MUSIC)

        return xbmc.PlayList(xbmc.PLAYLIST_VIDEO)

    def play(self, item, db_id=None, transcode=False, playlist=False):
        """Play requested item"""
        listitem = xbmcgui.ListItem()
        LOG.info("[ play/%s ] %s", item["Id"], item["Name"])

        transcode = transcode or settings("playFromTranscode.bool")
        play = playutils.PlayUtils(
            item, transcode, self.server_id, self.server, self.api_client
        )
        source = play.select_source(play.get_sources())
        play.set_external_subs(source, listitem)

        self.set_playlist(item, listitem, db_id, transcode)

        # Using playlist approach for Cinema mode
        if len(self.stack) > 1:
            self._play_stack()
        else:
            self.stack[0][1].setPath(self.stack[0][0])

            if len(sys.argv) > 1:
                xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, self.stack[0][1])

    def set_playlist(self, item, listitem, db_id=None, transcode=False):
        """Verify seektime, set intros, set main item and set additional parts.
        Detect the seektime for video type content.
        Verify the default video action set in Kodi for accurate resume behavior.
        """

        if item["MediaType"] in ("Video", "Audio"):
            resume = item["UserData"].get("PlaybackPositionTicks")

            if resume and transcode:
                choice = self.resume_dialog(
                    api.API(item, self.server).adjust_resume((resume or 0) / 10000000.0)
                )

                if choice is None:
                    raise Exception("User backed out of resume dialog.")

                item["resumePlayback"] = bool(choice)

        if settings("enableCinema.bool") and not item["resumePlayback"]:
            self._set_intros(item)

        self.set_listitem(item, listitem, db_id, None)
        playutils.set_properties(item, item["PlaybackInfo"]["Method"], self.server_id)
        self.stack.append([item["PlaybackInfo"]["Path"], listitem])

        if item.get("PartCount"):
            self._set_additional_parts(item["Id"])

    def _set_intros(self, item):
        """if we have any play them when the movie/show is not being resumed."""
        intros = self.api_client.get_intros(item["Id"])

        if intros["Items"]:
            enabled = True

            if settings("askCinema") == "true":

                resp = dialog("yesno", "{jellyfin}", translate(33016))
                if not resp:

                    enabled = False
                    LOG.info("Skip trailers.")

            if enabled:
                for intro in intros["Items"]:

                    listitem = xbmcgui.ListItem()
                    LOG.info("[ intro/%s ] %s", intro["Id"], intro["Name"])

                    play = playutils.PlayUtils(
                        intro, False, self.server_id, self.server, self.api_client
                    )
                    play.select_source(play.get_sources())
                    self.set_listitem(intro, listitem, intro=True)
                    listitem.setPath(intro["PlaybackInfo"]["Path"])
                    playutils.set_properties(
                        intro, intro["PlaybackInfo"]["Method"], self.server_id
                    )

                    self.stack.append([intro["PlaybackInfo"]["Path"], listitem])

                window("jellyfin.skip.%s" % intro["Id"], value="true")

    def _play_stack(self):
        """Play multiple items from the stack using a playlist.

        This is used for Cinema Mode (intros + movie) and multi-part videos.
        Unlike setResolvedUrl which can only handle a single file, this method
        creates a Kodi playlist and adds all items from the stack.

        The key challenge is that when called from a plugin:// URL context, Kodi
        expects setResolvedUrl() to provide the playback URL. But setResolvedUrl
        can only handle a single item. To work around this, we:
        1. First cancel the plugin resolution with setResolvedUrl(False)
        2. Then build and start our playlist independently
        """
        playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
        player = xbmc.Player()

        # Stop any current playback first and wait for it to fully stop
        if player.isPlaying():
            player.stop()
            # Wait for playback to fully stop (up to 3 seconds)
            for _ in range(30):
                xbmc.sleep(100)
                if not player.isPlaying():
                    break

        # Clear the playlist before cancelling plugin resolution
        playlist.clear()

        # Cancel the plugin resolution - this must happen after stopping playback
        # to prevent race conditions with the previous playback session
        if len(sys.argv) > 1:
            xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, xbmcgui.ListItem())

        xbmc.executebuiltin("ActivateWindow(busydialognocancel)")

        # Small delay to ensure plugin resolution is fully cancelled
        xbmc.sleep(100)

        # Add all items from the stack to the playlist
        for index, (path, listitem) in enumerate(self.stack):
            listitem.setPath(path)
            playlist.add(path, listitem, index)
            LOG.info("[ stack/%s ] %s", index, path[:60])

        xbmc.executebuiltin("Dialog.Close(busydialognocancel)")

        # Start playback from position 0 (the first intro)
        player.play(playlist, startpos=0)

    def _set_additional_parts(self, item_id):
        """Create listitems and add them to the stack of playlist."""
        parts = self.api_client.get_additional_parts(item_id)
        for part in parts["Items"]:

            listitem = xbmcgui.ListItem()
            LOG.info("[ part/%s ] %s", part["Id"], part["Name"])

            play = playutils.PlayUtils(
                part, False, self.server_id, self.server, self.api_client
            )
            source = play.select_source(play.get_sources())
            play.set_external_subs(source, listitem)
            self.set_listitem(part, listitem)
            listitem.setPath(part["PlaybackInfo"]["Path"])
            playutils.set_properties(
                part, part["PlaybackInfo"]["Method"], self.server_id
            )

            self.stack.append([part["PlaybackInfo"]["Path"], listitem])

    def play_playlist(
        self, items, clear=True, seektime=None, audio=None, subtitle=None
    ):
        """Play a list of items. Creates a new playlist. Add additional items as plugin listing."""
        item = items["Items"][0]
        playlist = self.get_playlist(item)
        player = xbmc.Player()

        if clear:
            if player.isPlaying():
                player.stop()

            xbmc.executebuiltin("ActivateWindow(busydialognocancel)")
            playlist.clear()
            index = 0
        else:
            index = max(playlist.getposition(), 0) + 1  # Can return -1

        listitem = xbmcgui.ListItem()
        LOG.info("[ playlist/%s ] %s", item["Id"], item["Name"])

        # Resume from the cast command's StartPositionTicks (seektime) when it is
        # given, otherwise the item's stored server-side position. Casting
        # "continue from X" sends StartPositionTicks and must honor it, rather
        # than the item's saved position (which may be stale or empty).
        resume = seektime or item["UserData"].get("PlaybackPositionTicks")
        item["UserData"]["PlaybackPositionTicks"] = resume
        item["resumePlayback"] = bool(resume)

        play = playutils.PlayUtils(
            item, False, self.server_id, self.server, self.api_client
        )
        source = play.select_source(play.get_sources())
        play.set_external_subs(source, listitem)

        item["PlaybackInfo"]["AudioStreamIndex"] = (
            audio or item["PlaybackInfo"]["AudioStreamIndex"]
        )
        item["PlaybackInfo"]["SubtitleStreamIndex"] = subtitle or item[
            "PlaybackInfo"
        ].get("SubtitleStreamIndex")

        self.set_listitem(item, listitem, None, True if seektime else False)
        listitem.setPath(item["PlaybackInfo"]["Path"])
        playutils.set_properties(item, item["PlaybackInfo"]["Method"], self.server_id)

        playlist.add(item["PlaybackInfo"]["Path"], listitem, index)

        if clear:
            xbmc.executebuiltin("Dialog.Close(busydialognocancel)")
            player.play(playlist, startpos=index)

        index += 1

        server_address = item["PlaybackInfo"]["ServerAddress"]
        token = item["PlaybackInfo"]["Token"]

        for item in items["Items"][1:]:
            listitem = xbmcgui.ListItem()
            LOG.info("[ playlist/%s ] %s", item["Id"], item["Name"])

            self.set_listitem(item, listitem, None, False)
            path = "{}/Audio/{}/stream.mp3?static=true&ApiKey={}".format(
                server_address, item["Id"], token
            )
            listitem.setPath(path)

            play = playutils.PlayUtils(
                item, False, self.server_id, self.server, self.api_client
            )
            source = play.select_source(play.get_sources())
            play.set_external_subs(source, listitem)

            playutils.set_properties(
                item, item["PlaybackInfo"]["Method"], self.server_id
            )

            playlist.add(path, listitem, index)
            index += 1

    def set_listitem(self, item, listitem, db_id=None, seektime=None, intro=False):

        objects = Objects()
        API = api.API(item, self.server)

        if item["Type"] in ("MusicArtist", "MusicAlbum", "Audio"):

            obj = objects.map(item, "BrowseAudio")
            obj["DbId"] = db_id
            obj["Artwork"] = API.get_all_artwork(
                objects.map(item, "ArtworkMusic"), True
            )
            self.listitem_music(obj, listitem, item)

        elif item["Type"] in ("Photo", "PhotoAlbum"):

            obj = objects.map(item, "BrowsePhoto")
            obj["Artwork"] = API.get_all_artwork(objects.map(item, "Artwork"))
            self.listitem_photo(obj, listitem, item)

        elif item["Type"] in ("TvChannel",):

            obj = objects.map(item, "BrowseChannel")
            obj["Artwork"] = API.get_all_artwork(objects.map(item, "Artwork"))
            self.listitem_channel(obj, listitem, item)

        else:
            obj = objects.map(item, "BrowseVideo")
            obj["DbId"] = db_id
            obj["Artwork"] = API.get_all_artwork(
                objects.map(item, "ArtworkParent"), True
            )

            if intro:
                obj["Artwork"]["Primary"] = "&KodiCinemaMode=true"

            self.listitem_video(obj, listitem, item, seektime, intro)

            if "PlaybackInfo" in item:

                if seektime:
                    item["PlaybackInfo"]["CurrentPosition"] = obj["Resume"]

                if "SubtitleUrl" in item["PlaybackInfo"]:

                    LOG.info("[ subtitles ] %s", item["PlaybackInfo"]["SubtitleUrl"])
                    listitem.setSubtitles([item["PlaybackInfo"]["SubtitleUrl"]])

                if item["Type"] == "Episode":

                    item["PlaybackInfo"]["CurrentEpisode"] = objects.map(item, "UpNext")
                    item["PlaybackInfo"]["CurrentEpisode"]["art"] = {
                        "tvshow.poster": obj["Artwork"].get("Series.Primary"),
                        "thumb": obj["Artwork"].get("Primary"),
                        "tvshow.fanart": None,
                    }
                    if obj["Artwork"]["Backdrop"]:
                        item["PlaybackInfo"]["CurrentEpisode"]["art"][
                            "tvshow.fanart"
                        ] = obj["Artwork"]["Backdrop"][0]

        listitem.setContentLookup(False)

    def listitem_video(self, obj, listitem, item, seektime=None, intro=False):
        """Set listitem for video content. That also include streams."""
        API = api.API(item, self.server)
        is_video = obj["MediaType"] in ("Video", "Audio")  # audiobook

        obj["Genres"] = " / ".join(obj["Genres"] or [])
        obj["Studios"] = [
            API.validate_studio(studio) for studio in (obj["Studios"] or [])
        ]
        obj["Studios"] = " / ".join(obj["Studios"])
        obj["Mpaa"] = API.get_mpaa(obj["Mpaa"])
        obj["People"] = obj["People"] or []
        obj["Countries"] = " / ".join(obj["Countries"] or [])
        obj["Directors"] = " / ".join(obj["Directors"] or [])
        obj["Writers"] = " / ".join(obj["Writers"] or [])
        obj["Plot"] = API.get_overview(obj["Plot"])
        obj["ShortPlot"] = API.get_overview(obj["ShortPlot"])
        obj["DateAdded"] = obj["DateAdded"].split(".")[0].replace("T", " ")
        obj["Rating"] = obj["Rating"] or 0
        obj["FileDate"] = "%s.%s.%s" % tuple(
            reversed(obj["DateAdded"].split("T")[0].split("-"))
        )
        obj["Runtime"] = round(float((obj["Runtime"] or 0) / 10000000.0), 6)
        obj["Resume"] = API.adjust_resume((obj["Resume"] or 0) / 10000000.0)
        obj["PlayCount"] = API.get_playcount(obj["Played"], obj["PlayCount"]) or 0
        obj["Overlay"] = 7 if obj["Played"] else 6
        obj["Video"] = API.video_streams(obj["Video"] or [], obj["Container"])
        obj["Audio"] = API.audio_streams(obj["Audio"] or [])
        obj["Streams"] = API.media_streams(obj["Video"], obj["Audio"], obj["Subtitles"])
        obj["ChildCount"] = obj["ChildCount"] or 0
        obj["RecursiveCount"] = obj["RecursiveCount"] or 0
        obj["Unwatched"] = obj["Unwatched"] or 0
        obj["Artwork"]["Backdrop"] = obj["Artwork"]["Backdrop"] or []
        obj["Artwork"]["Thumb"] = obj["Artwork"]["Thumb"] or ""

        if not intro and obj["Type"] != "Trailer":
            obj["Artwork"]["Primary"] = (
                obj["Artwork"]["Primary"]
                or "special://home/addons/plugin.video.jellyfin/resources/icon.png"
            )
        else:
            obj["Artwork"]["Primary"] = (
                obj["Artwork"]["Primary"]
                or obj["Artwork"]["Thumb"]
                or (
                    obj["Artwork"]["Backdrop"][0]
                    if len(obj["Artwork"]["Backdrop"])
                    else "special://home/addons/plugin.video.jellyfin/resources/fanart.png"
                )
            )
            obj["Artwork"]["Primary"] += (
                "&KodiTrailer=true"
                if obj["Type"] == "Trailer"
                else "&KodiCinemaMode=true"
            )
            obj["Artwork"]["Backdrop"] = [obj["Artwork"]["Primary"]]

        self.set_artwork(obj["Artwork"], listitem, obj["Type"])

        if intro or obj["Type"] == "Trailer":
            listitem.setArt(
                {"poster": ""}
            )  # Clear the poster value for intros / trailers to prevent issues in skins

        listitem.setArt(
            {
                "icon": "DefaultVideo.png",
                "thumb": obj["Artwork"]["Primary"],
            }
        )

        if obj["Premiere"]:
            obj["Premiere"] = obj["Premiere"].split("T")[0]

        if obj["DatePlayed"]:
            obj["DatePlayed"] = obj["DatePlayed"].split(".")[0].replace("T", " ")

        metadata = {
            "title": obj["Title"],
            "originaltitle": obj["Title"],
            "sorttitle": obj["SortTitle"],
            "country": obj["Countries"],
            "genre": obj["Genres"],
            "year": obj["Year"],
            "rating": obj["Rating"],
            "playcount": obj["PlayCount"],
            "overlay": obj["Overlay"],
            "director": obj["Directors"],
            "mpaa": obj["Mpaa"],
            "plot": obj["Plot"],
            "plotoutline": obj["ShortPlot"],
            "studio": obj["Studios"],
            "tagline": obj["Tagline"],
            "writer": obj["Writers"],
            "premiered": obj["Premiere"],
            "votes": obj["Votes"],
            "dateadded": obj["DateAdded"],
            "aired": obj["Year"],
            "date": obj["FileDate"],
            "dbid": obj["DbId"],
        }
        listitem.setCast(API.get_actors())

        if obj["Premiere"]:
            metadata["date"] = obj["Premiere"]

        if obj["Type"] == "Episode":
            metadata.update(
                {
                    "mediatype": "episode",
                    "tvshowtitle": obj["SeriesName"],
                    "season": obj["Season"] or 0,
                    "sortseason": obj["Season"] or 0,
                    "episode": obj["Index"] or 0,
                    "sortepisode": obj["Index"] or 0,
                    "lastplayed": obj["DatePlayed"],
                    "duration": obj["Runtime"],
                    "aired": obj["Premiere"],
                }
            )

        elif obj["Type"] == "Season":
            metadata.update(
                {
                    "mediatype": "season",
                    "tvshowtitle": obj["SeriesName"],
                    "season": obj["Index"] or 0,
                    "sortseason": obj["Index"] or 0,
                }
            )
            listitem.setProperty("NumEpisodes", str(obj["RecursiveCount"]))
            listitem.setProperty(
                "WatchedEpisodes", str(obj["RecursiveCount"] - obj["Unwatched"])
            )
            listitem.setProperty("UnWatchedEpisodes", str(obj["Unwatched"]))
            listitem.setProperty("IsFolder", "true")

        elif obj["Type"] == "Series":

            if obj["Status"] != "Ended":
                obj["Status"] = None

            metadata.update(
                {
                    "mediatype": "tvshow",
                    "tvshowtitle": obj["Title"],
                    "status": obj["Status"],
                }
            )
            listitem.setProperty("TotalSeasons", str(obj["ChildCount"]))
            listitem.setProperty("TotalEpisodes", str(obj["RecursiveCount"]))
            listitem.setProperty(
                "WatchedEpisodes", str(obj["RecursiveCount"] - obj["Unwatched"])
            )
            listitem.setProperty("UnWatchedEpisodes", str(obj["Unwatched"]))
            listitem.setProperty("IsFolder", "true")

        elif obj["Type"] == "Movie":
            metadata.update(
                {
                    "mediatype": "movie",
                    "imdbnumber": obj["UniqueId"],
                    "lastplayed": obj["DatePlayed"],
                    "duration": obj["Runtime"],
                }
            )

        elif obj["Type"] == "MusicVideo":
            metadata.update(
                {
                    "mediatype": "musicvideo",
                    "album": obj["Album"],
                    "artist": obj["Artists"] or [],
                    "lastplayed": obj["DatePlayed"],
                    "duration": obj["Runtime"],
                }
            )

        elif obj["Type"] == "BoxSet":
            metadata["mediatype"] = "set"
            listitem.setProperty("IsFolder", "true")
        else:
            metadata.update(
                {
                    "mediatype": "video",
                    "lastplayed": obj["DatePlayed"],
                    "year": obj["Year"],
                    "duration": obj["Runtime"],
                }
            )

        if is_video:

            listitem.setProperty("IsPlayable", "true")
            listitem.setProperty("IsFolder", "false")

            # setResumePoint records the resume metadata (replaces the deprecated
            # resumetime/totaltime properties). StartOffset is what actually makes
            # the player begin at the resume position: it feeds the player start
            # time (CApplication) and is honored for playlist/cast playback too.
            tag = listitem.getVideoInfoTag()
            tag.setResumePoint(obj["Resume"] or 0, obj["Runtime"] or 0)
            if obj["Resume"] and item.get("resumePlayback"):
                listitem.setProperty("StartOffset", str(obj["Resume"]))
            else:
                listitem.setProperty("StartOffset", "0")
                listitem.setProperty("StartPercent", "0")

            for track in obj["Streams"]["video"]:
                listitem.addStreamInfo(
                    "video",
                    {
                        "hdrtype": track["hdrtype"],
                        "duration": obj["Runtime"],
                        "aspect": track["aspect"],
                        "codec": track["codec"],
                        "width": track["width"],
                        "height": track["height"],
                    },
                )

            for track in obj["Streams"]["audio"]:
                listitem.addStreamInfo(
                    "audio", {"codec": track["codec"], "channels": track["channels"]}
                )

            for track in obj["Streams"]["subtitle"]:
                listitem.addStreamInfo("subtitle", {"language": track})

        listitem.setLabel(obj["Title"])
        listitem.setInfo("video", metadata)
        listitem.setContentLookup(False)

    def listitem_channel(self, obj, listitem, item):
        """Set listitem for channel content."""
        API = api.API(item, self.server)

        obj["Title"] = "%s - %s" % (obj["Title"], obj["ProgramName"])
        obj["Runtime"] = round(float((obj["Runtime"] or 0) / 10000000.0), 6)
        obj["PlayCount"] = API.get_playcount(obj["Played"], obj["PlayCount"]) or 0
        obj["Overlay"] = 7 if obj["Played"] else 6
        obj["Artwork"]["Primary"] = (
            obj["Artwork"]["Primary"]
            or "special://home/addons/plugin.video.jellyfin/resources/icon.png"
        )
        obj["Artwork"]["Thumb"] = (
            obj["Artwork"]["Thumb"]
            or "special://home/addons/plugin.video.jellyfin/resources/fanart.png"
        )
        obj["Artwork"]["Backdrop"] = obj["Artwork"]["Backdrop"] or [
            "special://home/addons/plugin.video.jellyfin/resources/fanart.png"
        ]

        metadata = {
            "title": obj["Title"],
            "originaltitle": obj["Title"],
            "playcount": obj["PlayCount"],
            "overlay": obj["Overlay"],
        }

        listitem.setArt(
            {
                "icon": obj["Artwork"]["Thumb"],
                "thumb": obj["Artwork"]["Primary"],
            }
        )
        self.set_artwork(obj["Artwork"], listitem, obj["Type"])

        if obj["Artwork"]["Primary"]:
            listitem.setArt(
                {
                    "thumb": obj["Artwork"]["Primary"],
                }
            )

        if not obj["Artwork"]["Backdrop"]:
            listitem.setArt({"fanart": obj["Artwork"]["Primary"]})

        listitem.setProperty("totaltime", str(obj["Runtime"]))
        listitem.setProperty("IsPlayable", "true")
        listitem.setProperty("IsFolder", "false")

        listitem.setLabel(obj["Title"])
        listitem.setInfo("video", metadata)
        listitem.setContentLookup(False)

    def listitem_music(self, obj, listitem, item):
        API = api.API(item, self.server)

        obj["Runtime"] = round(float((obj["Runtime"] or 0) / 10000000.0), 6)
        obj["PlayCount"] = API.get_playcount(obj["Played"], obj["PlayCount"]) or 0
        obj["Rating"] = obj["Rating"] or 0

        if not obj["Played"]:
            obj["DatePlayed"] = None
        elif obj["FileDate"] or obj["DatePlayed"]:
            obj["DatePlayed"] = (
                (obj["DatePlayed"] or obj["FileDate"]).split(".")[0].replace("T", " ")
            )

        obj["FileDate"] = "%s.%s.%s" % tuple(
            reversed(obj["FileDate"].split("T")[0].split("-"))
        )

        metadata = {
            "title": obj["Title"],
            "genre": obj["Genre"],
            "year": obj["Year"],
            "album": obj["Album"],
            "artist": obj["Artists"],
            "rating": obj["Rating"],
            "comment": obj["Comment"],
            "date": obj["FileDate"],
        }
        self.set_artwork(obj["Artwork"], listitem, obj["Type"])

        if obj["Type"] == "Audio":
            metadata.update(
                {
                    "mediatype": "song",
                    "tracknumber": obj["Index"],
                    "discnumber": obj["Disc"],
                    "duration": obj["Runtime"],
                    "playcount": obj["PlayCount"],
                    "lastplayed": obj["DatePlayed"],
                    "musicbrainztrackid": obj["UniqueId"],
                }
            )
            listitem.setProperty("IsPlayable", "true")
            listitem.setProperty("IsFolder", "false")

        elif obj["Type"] == "Album":
            metadata.update(
                {"mediatype": "album", "musicbrainzalbumid": obj["UniqueId"]}
            )

        elif obj["Type"] in ("Artist", "MusicArtist"):
            metadata.update(
                {"mediatype": "artist", "musicbrainzartistid": obj["UniqueId"]}
            )
        else:
            metadata["mediatype"] = "music"

        listitem.setLabel(obj["Title"])
        listitem.setInfo("music", metadata)
        listitem.setContentLookup(False)

    def listitem_photo(self, obj, listitem, item):
        API = api.API(item, self.server)

        obj["Overview"] = API.get_overview(obj["Overview"])
        obj["FileDate"] = "%s.%s.%s" % tuple(
            reversed(obj["FileDate"].split("T")[0].split("-"))
        )

        metadata = {"title": obj["Title"]}
        listitem.setProperty("path", obj["Artwork"]["Primary"])
        listitem.setArt(
            {
                "thumb": obj["Artwork"]["Primary"],
            }
        )

        if obj["Type"] == "Photo":
            metadata.update(
                {
                    "picturepath": obj["Artwork"]["Primary"],
                    "date": obj["FileDate"],
                    "exif:width": str(obj.get("Width", 0)),
                    "exif:height": str(obj.get("Height", 0)),
                    "size": obj["Size"],
                    "exif:cameramake": obj["CameraMake"],
                    "exif:cameramodel": obj["CameraModel"],
                    "exif:exposuretime": str(obj["ExposureTime"]),
                    "exif:focallength": str(obj["FocalLength"]),
                }
            )
            listitem.setProperty("plot", obj["Overview"])
            listitem.setProperty("IsFolder", "false")
            listitem.setArt(
                {
                    "icon": "DefaultPicture.png",
                }
            )
        else:
            listitem.setProperty("IsFolder", "true")
            listitem.setArt(
                {
                    "icon": "DefaultFolder.png",
                }
            )

        listitem.setProperty("IsPlayable", "false")
        listitem.setLabel(obj["Title"])
        listitem.setInfo("pictures", metadata)
        listitem.setContentLookup(False)

    def set_artwork(self, artwork, listitem, media):

        if media == "Episode":

            art = {
                "poster": "Series.Primary",
                "tvshow.poster": "Series.Primary",
                "clearart": "Art",
                "tvshow.clearart": "Art",
                "clearlogo": "Logo",
                "tvshow.clearlogo": "Logo",
                "discart": "Disc",
                "fanart_image": "Backdrop",
                "landscape": "Thumb",
                "tvshow.landscape": "Thumb",
                "thumb": "Primary",
                "fanart": "Backdrop",
            }
        elif media in ("Artist", "Audio", "MusicAlbum"):

            art = {
                "clearlogo": "Logo",
                "discart": "Disc",
                "fanart": "Backdrop",
                "fanart_image": "Backdrop",  # in case
                "thumb": "Primary",
            }
        else:
            art = {
                "poster": "Primary",
                "clearart": "Art",
                "clearlogo": "Logo",
                "discart": "Disc",
                "fanart_image": "Backdrop",
                "landscape": "Thumb",
                "thumb": "Primary",
                "fanart": "Backdrop",
            }

        for k_art, e_art in art.items():

            if e_art == "Backdrop":
                self._set_art(
                    listitem, k_art, artwork[e_art][0] if artwork[e_art] else " "
                )
            else:
                self._set_art(listitem, k_art, artwork.get(e_art, " "))

    def _set_art(self, listitem, art, path):
        LOG.debug(" [ art/%s ] %s", art, path)

        if art in (
            "fanart_image",
            "small_poster",
            "tiny_poster",
            "medium_landscape",
            "medium_poster",
            "small_fanartimage",
            "medium_fanartimage",
            "fanart_noindicators",
            "discart",
            "tvshow.poster",
        ):

            listitem.setProperty(art, path)
        else:
            listitem.setArt({art: path})

    def resume_dialog(self, seektime):
        """Base resume dialog based on Kodi settings."""
        LOG.info("Resume dialog called.")
        XML_PATH = (
            xbmcaddon.Addon("plugin.video.jellyfin").getAddonInfo("path"),
            "default",
            "1080i",
        )

        dialog = resume.ResumeDialog("script-jellyfin-resume.xml", *XML_PATH)
        dialog.set_resume_point(
            "Resume from %s" % str(timedelta(seconds=seektime)).split(".")[0]
        )
        dialog.doModal()

        if dialog.is_selected():
            if not dialog.get_selected():  # Start from beginning selected.
                return False
        else:  # User backed out
            LOG.info("User exited without a selection.")
            return

        return True


class PlaylistWorker(threading.Thread):

    def __init__(self, server_id, items, *args):

        self.server_id = server_id
        self.items = items
        self.args = args
        threading.Thread.__init__(self)

    def run(self):
        Actions(self.server_id).play_playlist(self.items, *self.args)


def on_update(data, server):
    """Only for manually marking as watched/unwatched"""
    try:
        kodi_id = data["item"]["id"]
        media = data["item"]["type"]
        playcount = int(data["playcount"])
        LOG.info(" [ update/%s ] kodi_id: %s media: %s", playcount, kodi_id, media)
    except (KeyError, TypeError):
        LOG.debug("Invalid playstate update")

        return

    from .. import database

    item = database.get_item(kodi_id, media)

    if item:

        if not window("jellyfin.skip.%s.bool" % item[0]):
            server.jellyfin.item_played(item[0], playcount)

        window("jellyfin.skip.%s" % item[0], clear=True)


def on_play(data, server):
    """Setup progress for jellyfin playback."""
    player = xbmc.Player()

    try:
        kodi_id = None

        if player.isPlayingVideo():

            """Seems to misbehave when playback is not terminated prior to playing new content.
            The kodi id remains that of the previous title. Maybe onPlay happens before
            this information is updated. Added a failsafe further below.
            """
            item = player.getVideoInfoTag()
            kodi_id = item.getDbId()
            media = item.getMediaType()

        if (
            kodi_id is None
            or int(kodi_id) == -1
            or "item" in data
            and "id" in data["item"]
            and data["item"]["id"] != kodi_id
        ):

            item = data["item"]
            kodi_id = item["id"]
            media = item["type"]

        LOG.info(" [ play ] kodi_id: %s media: %s", kodi_id, media)

    except (KeyError, TypeError):
        LOG.debug("Invalid playstate update")

        return

    if settings("useDirectPaths") == "1" or media == "song":
        from .. import database

        item = database.get_item(kodi_id, media)

        if item:

            try:
                file = player.getPlayingFile()
            except Exception as error:
                LOG.exception(error)

                return

            item = server.jellyfin.get_item(item[0])
            item["PlaybackInfo"] = {"Path": file}
            playutils.set_properties(
                item,
                "DirectStream" if settings("useDirectPaths") == "0" else "DirectPlay",
            )


def special_listener():
    """Corner cases that needs to be listened to.
    This is run in a loop within monitor.py
    """
    player = xbmc.Player()
    is_playing = player.isPlaying()
    count = int(window("jellyfin.external_count") or 0)

    if is_playing and not window("jellyfin.external_check"):
        time = player.getTime()

        if time > 1:  # Not external player.

            window("jellyfin.external_check", value="true")
            window("jellyfin.external_count", value="0")
        elif count == 120:

            LOG.info("External player detected.")
            window("jellyfin.external.bool", True)
            window("jellyfin.external_check.bool", True)
            window("jellyfin.external_count", value="0")

        elif time == 0:
            window("jellyfin.external_count", value=str(count + 1))
