# Most of this is ported from Roku code and much of it is currently unused
# TODO: Perhaps remove unnecessary code
from __future__ import absolute_import
import time

from . import util
from six import moves
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
import six.moves.urllib.parse
from . import plexrequest
from . import callback
from . import http


class ServerTimeline(util.AttributeDict):
    def reset(self):
        self.expires = time.time() + 15

    def isExpired(self):
        return time.time() > self.get('expires', 0)


class TimelineData(util.AttributeDict):
    def __init__(self, timelineType, *args, **kwargs):
        util.AttributeDict.__init__(self, *args, **kwargs)
        self.type = timelineType
        self.state = "stopped"
        self.item = None
        self.choice = None
        self.playQueue = None

        self.controllable = util.AttributeDict()
        self.controllableStr = None

        self.attrs = util.AttributeDict()

        # Set default controllable for all content. Other controllable aspects
        # will be set based on the players content.
        #
        self.setControllable("playPause", True)
        self.setControllable("stop", True)

    def setControllable(self, name, isControllable):
        if isControllable:
            self.controllable[name] = ""
        else:
            if name in self.controllable:
                del self.controllable[name]

        self.controllableStr = None

    def updateControllableStr(self):
        if not self.controllableStr:
            self.controllableStr = ""
            prependComma = False

            for name in self.controllable:
                if prependComma:
                    self.controllableStr += ','
                else:
                    prependComma = True
                self.controllableStr += name

    def toXmlAttributes(self, elem):
        self.updateControllableStr()
        elem.attrib["type"] = self.type
        elem.attrib["start"] = self.state
        elem.attrib["controllable"] = self.controllableStr

        if self.item:
            if self.item.duration:
                elem.attrib['duration'] = self.item.duration
            if self.item.ratingKey:
                elem.attrib['ratingKey'] = self.item.ratingKey
            if self.item.key:
                elem.attrib['key'] = self.item.key
            if self.item.container.address:
                elem.attrib['containerKey'] = self.item.container.address

            # Send the audio, video and subtitle choice if it's available
            if self.choice:
                for stream in ("audioStream", "videoStream", "subtitleStream"):
                    if self.choice.get(stream) and self.choice[stream].id:
                        elem.attrib[stream + "ID"] = self.choice[stream].id

            server = self.item.getServer()
            if server:
                elem.attrib["machineIdentifier"] = server.uuid

                if server.activeConnection:
                    parts = six.moves.urllib.parse.uslparse(server.activeConnection.address)
                    elem.attrib["protocol"] = parts.scheme
                    elem.attrib["address"] = parts.netloc.split(':', 1)[0]
                    if ':' in parts.netloc:
                        elem.attrib["port"] = parts.netloc.split(':', 1)[-1]
                    elif parts.scheme == 'https':
                        elem.attrib["port"] = '443'
                    else:
                        elem.attrib["port"] = '80'

        if self.playQueue:
            elem.attrib["playQueueID"] = str(self.playQueue.id)
            elem.attrib["playQueueItemID"] = str(self.playQueue.selectedId)
            elem.attrib["playQueueVersion"] = str(self.playQueue.version)

        for key, val in self.attrs.items():
            elem.attrib[key] = val


class NowPlayingManager(object):
    def __init__(self):
        # Constants
        self.NAVIGATION = "navigation"
        self.FULLSCREEN_VIDEO = "fullScreenVideo"
        self.FULLSCREEN_MUSIC = "fullScreenMusic"
        self.FULLSCREEN_PHOTO = "fullScreenPhoto"
        self.TIMELINE_TYPES = ["video", "music", "photo"]

        # Members
        self.serverTimelines = util.AttributeDict()
        self.subscribers = util.AttributeDict()
        self.pollReplies = util.AttributeDict()
        self.timelines = util.AttributeDict()
        self.location = self.NAVIGATION

        self.textFieldName = None
        self.textFieldContent = None
        self.textFieldSecure = None

        # Initialization
        for timelineType in self.TIMELINE_TYPES:
            self.timelines[timelineType] = TimelineData(timelineType)

    def updatePlaybackState(self, timelineType, playerObject, state, t, playQueue=None, duration=0, force=False):
        timeline = self.timelines[timelineType]
        timeline.state = state
        timeline.item = playerObject.item
        timeline.choice = playerObject.choice
        timeline.playQueue = playQueue
        timeline.attrs["time"] = str(t)
        timeline.duration = duration

        # self.sendTimelineToAll()

        self.sendTimelineToServer(timelineType, timeline, t, force=force)

    def sendTimelineToServer(self, timelineType, timeline, t, force=False):
        if not hasattr(timeline.item, 'getServer') or not timeline.item.getServer():
            return

        serverTimeline = self.getServerTimeline(timelineType)

        # Only send timeline if it's the first, item changes, playstate changes or timer pops
        itemsEqual = timeline.item and serverTimeline.item and timeline.item.ratingKey == serverTimeline.item.ratingKey
        if itemsEqual and timeline.state == serverTimeline.state and not serverTimeline.isExpired() and not force:
            return

        serverTimeline.reset()
        serverTimeline.item = timeline.item
        serverTimeline.state = timeline.state

        # Ignore sending timelines for multi part media with no duration
        obj = timeline.choice
        if obj and obj.part and obj.part.duration.asInt() == 0 and obj.media.parts and len(obj.media.parts) > 1:
            util.WARN_LOG("Timeline not supported: the current part doesn't have a valid duration")
            return

        # It's possible with timers and in player seeking for the time to be greater than the
        # duration, which causes a 400, so in that case we'll set the time to the duration.
        duration = timeline.item.duration.asInt() or timeline.duration
        if t > duration:
            t = duration

        params = util.AttributeDict()
        params["time"] = t
        params["duration"] = duration
        params["state"] = timeline.state
        params["guid"] = timeline.item.guid
        params["ratingKey"] = timeline.item.ratingKey
        params["url"] = timeline.item.url
        params["key"] = timeline.item.key
        params["containerKey"] = timeline.item.container.address
        if timeline.playQueue:
            params["playQueueItemID"] = timeline.playQueue.selectedId

        path = "/:/timeline"
        for paramKey in params:
            if params[paramKey]:
                path = http.addUrlParam(path, paramKey + "=" + six.moves.urllib.parse.quote(str(params[paramKey])))

        request = plexrequest.PlexRequest(timeline.item.getServer(), path)

        context = request.createRequestContext("timelineUpdate", callback.Callable(self.onTimelineResponse))
        context.playQueue = timeline.playQueue
        util.APP.startRequest(request, context)

    def getServerTimeline(self, timelineType):
        if not self.serverTimelines.get(timelineType):
            serverTL = ServerTimeline()
            serverTL.reset()

            self.serverTimelines[timelineType] = serverTL

        return self.serverTimelines[timelineType]

    def nowPlayingSetControllable(self, timelineType, name, isControllable):
        self.timelines[timelineType].setControllable(name, isControllable)

    def onTimelineResponse(self, request, response, context):
        context.request.server.trigger("np:timelineResponse", response=response)

        if not context.playQueue or not context.playQueue.refreshOnTimeline:
            return
        context.playQueue.refreshOnTimeline = False
        context.playQueue.refresh(False)
