# -*- coding: utf-8 -*-

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

import logging
import sys

import xbmc
import xbmcgui
import xbmcvfs

import clientinfo
import downloadutils
from utils import window, settings, language as lang

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

log = logging.getLogger("EMBY."+__name__)

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


class PlayUtils():
    
    
    def __init__(self, item):

        self.item = item
        self.clientInfo = clientinfo.ClientInfo()

        self.userid = window('emby_currUser')
        self.server = window('emby_server%s' % self.userid)
        
        self.doUtils = downloadutils.DownloadUtils().downloadUrl
    
    def getPlayUrlNew(self):
        '''
            New style to retrieve the best playback method based on sending the profile to the server
            Based on capabilities the correct path is returned, including livestreams that need to be opened by the server
            TODO: Close livestream if needed (RequiresClosing in livestream source)
        '''
        playurl = None
        pbinfo = self.getPlaybackInfo()
        if pbinfo:
            xbmc.log("getPlayUrl pbinfo: %s" %(pbinfo))
            
            if pbinfo["Protocol"] == "SupportsDirectPlay":
                playmethod = "DirectPlay"
            elif pbinfo["Protocol"] == "SupportsDirectStream":
                playmethod = "DirectStream"
            elif pbinfo.get('LiveStreamId'):
                playmethod = "LiveStream"
            else:
                playmethod = "Transcode"

            playurl = pbinfo["Path"]
            xbmc.log("getPlayUrl playmethod: %s - playurl: %s" %(playmethod, playurl))
            window('emby_%s.playmethod' % playurl, value=playmethod)
            if pbinfo["RequiresClosing"] and pbinfo.get('LiveStreamId'):
                window('emby_%s.livestreamid' % playurl, value=pbinfo["LiveStreamId"])

        return playurl


    def getPlayUrl(self):

        playurl = None
        
        if (self.item.get('Type') in ("Recording", "TvChannel") and self.item.get('MediaSources')
                and self.item['MediaSources'][0]['Protocol'] == "Http"):
            # Play LiveTV or recordings
            log.info("File protocol is http (livetv).")
            playurl = "%s/emby/Videos/%s/stream.ts?audioCodec=copy&videoCodec=copy" % (self.server, self.item['Id'])
            window('emby_%s.playmethod' % playurl, value="DirectPlay")
            

        elif self.item.get('MediaSources') and self.item['MediaSources'][0]['Protocol'] == "Http":
            # Only play as http, used for channels, or online hosting of content
            log.info("File protocol is http.")
            playurl = self.httpPlay()
            window('emby_%s.playmethod' % playurl, value="DirectStream")

        elif self.isDirectPlay():

            log.info("File is direct playing.")
            playurl = self.directPlay()
            playurl = playurl.encode('utf-8')
            # Set playmethod property
            window('emby_%s.playmethod' % playurl, value="DirectPlay")

        elif self.isDirectStream():
            
            log.info("File is direct streaming.")
            playurl = self.directStream()
            playurl = playurl.encode('utf-8')
            # Set playmethod property
            window('emby_%s.playmethod' % playurl, value="DirectStream")

        elif self.isTranscoding():
            
            log.info("File is transcoding.")
            playurl = self.transcoding()
            # Set playmethod property
            window('emby_%s.playmethod' % playurl, value="Transcode")

        return playurl

    def httpPlay(self):
        # Audio, Video, Photo

        itemid = self.item['Id']
        mediatype = self.item['MediaType']

        if mediatype == "Audio":
            playurl = "%s/emby/Audio/%s/stream" % (self.server, itemid)
        else:
            playurl = "%s/emby/Videos/%s/stream?static=true" % (self.server, itemid)

        return playurl

    def isDirectPlay(self):

        # Requirement: Filesystem, Accessible path
        if settings('playFromStream') == "true":
            # User forcing to play via HTTP
            log.info("Can't direct play, play from HTTP enabled.")
            return False

        videotrack = self.item['MediaSources'][0]['Name']
        transcodeH265 = settings('transcodeH265')
        videoprofiles = [x['Profile'] for x in self.item['MediaSources'][0]['MediaStreams'] if 'Profile' in x]
        transcodeHi10P = settings('transcodeHi10P')        

        if transcodeHi10P == "true" and "H264" in videotrack and "High 10" in videoprofiles:
            return False   

        if transcodeH265 in ("1", "2", "3") and ("HEVC" in videotrack or "H265" in videotrack):
            # Avoid H265/HEVC depending on the resolution
            resolution = int(videotrack.split("P", 1)[0])
            res = {

                '1': 480,
                '2': 720,
                '3': 1080
            }
            log.info("Resolution is: %sP, transcode for resolution: %sP+"
                % (resolution, res[transcodeH265]))
            if res[transcodeH265] <= resolution:
                return False

        canDirectPlay = self.item['MediaSources'][0]['SupportsDirectPlay']
        # Make sure direct play is supported by the server
        if not canDirectPlay:
            log.info("Can't direct play, server doesn't allow/support it.")
            return False

        location = self.item['LocationType']
        if location == "FileSystem":
            # Verify the path
            if not self.fileExists():
                log.info("Unable to direct play.")
                log.info(self.directPlay())
                xbmcgui.Dialog().ok(
                            heading=lang(29999),
                            line1=lang(33011),
                            line2=(self.directPlay()))                            
                sys.exit()

        return True

    def directPlay(self):

        try:
            playurl = self.item['MediaSources'][0]['Path']
        except (IndexError, KeyError):
            playurl = self.item['Path']

        if self.item.get('VideoType'):
            # Specific format modification
            if self.item['VideoType'] == "Dvd":
                playurl = "%s/VIDEO_TS/VIDEO_TS.IFO" % playurl
            elif self.item['VideoType'] == "BluRay":
                playurl = "%s/BDMV/index.bdmv" % playurl

        # Assign network protocol
        if playurl.startswith('\\\\'):
            playurl = playurl.replace("\\\\", "smb://")
            playurl = playurl.replace("\\", "/")

        if "apple.com" in playurl:
            USER_AGENT = "QuickTime/7.7.4"
            playurl += "?|User-Agent=%s" % USER_AGENT

        return playurl

    def fileExists(self):

        if 'Path' not in self.item:
            # File has no path defined in server
            return False

        # Convert path to direct play
        path = self.directPlay()
        log.info("Verifying path: %s" % path)

        if xbmcvfs.exists(path):
            log.info("Path exists.")
            return True

        elif ":" not in path:
            log.info("Can't verify path, assumed linux. Still try to direct play.")
            return True

        else:
            log.info("Failed to find file.")
            return False

    def isDirectStream(self):

        videotrack = self.item['MediaSources'][0]['Name']
        transcodeH265 = settings('transcodeH265')
        videoprofiles = [x['Profile'] for x in self.item['MediaSources'][0]['MediaStreams'] if 'Profile' in x]
        transcodeHi10P = settings('transcodeHi10P')        

        if transcodeHi10P == "true" and "H264" in videotrack and "High 10" in videoprofiles:
            return False   

        if transcodeH265 in ("1", "2", "3") and ("HEVC" in videotrack or "H265" in videotrack):
            # Avoid H265/HEVC depending on the resolution
            resolution = int(videotrack.split("P", 1)[0])
            res = {

                '1': 480,
                '2': 720,
                '3': 1080
            }
            log.info("Resolution is: %sP, transcode for resolution: %sP+"
                % (resolution, res[transcodeH265]))
            if res[transcodeH265] <= resolution:
                return False

        # Requirement: BitRate, supported encoding
        canDirectStream = self.item['MediaSources'][0]['SupportsDirectStream']
        # Make sure the server supports it
        if not canDirectStream:
            return False

        # Verify the bitrate
        if not self.isNetworkSufficient():
            log.info("The network speed is insufficient to direct stream file.")
            return False

        return True

    def directStream(self):

        if 'Path' in self.item and self.item['Path'].endswith('.strm'):
            # Allow strm loading when direct streaming
            playurl = self.directPlay()
        elif self.item['Type'] == "Audio":
            playurl = "%s/emby/Audio/%s/stream.mp3" % (self.server, self.item['Id'])
        else:
            playurl = "%s/emby/Videos/%s/stream?static=true" % (self.server, self.item['Id'])

        return playurl

    def isNetworkSufficient(self):

        settings = self.getBitrate()*1000

        try:
            sourceBitrate = int(self.item['MediaSources'][0]['Bitrate'])
        except (KeyError, TypeError):
            log.info("Bitrate value is missing.")
        else:
            log.info("The add-on settings bitrate is: %s, the video bitrate required is: %s"
                % (settings, sourceBitrate))
            if settings < sourceBitrate:
                return False

        return True

    def isTranscoding(self):
        # Make sure the server supports it
        if not self.item['MediaSources'][0]['SupportsTranscoding']:
            return False

        return True

    def transcoding(self):

        if 'Path' in self.item and self.item['Path'].endswith('.strm'):
            # Allow strm loading when transcoding
            playurl = self.directPlay()
        else:
            itemid = self.item['Id']
            deviceId = self.clientInfo.get_device_id()
            playurl = (
                "%s/emby/Videos/%s/master.m3u8?MediaSourceId=%s"
                % (self.server, itemid, itemid)
            )
            playurl = (
                "%s&VideoCodec=h264&AudioCodec=ac3&MaxAudioChannels=6&deviceId=%s&VideoBitrate=%s"
                % (playurl, deviceId, self.getBitrate()*1000))

        return playurl

    def getBitrate(self):

        # get the addon video quality
        bitrate = {

            '0': 664,
            '1': 996,
            '2': 1320,
            '3': 2000,
            '4': 3200,
            '5': 4700,
            '6': 6200,
            '7': 7700,
            '8': 9200,
            '9': 10700,
            '10': 12200,
            '11': 13700,
            '12': 15200,
            '13': 16700,
            '14': 18200,
            '15': 20000,
            '16': 25000,
            '17': 30000,
            '18': 35000,
            '16': 40000,
            '17': 100000,
            '18': 1000000
        }

        # max bit rate supported by server (max signed 32bit integer)
        return bitrate.get(settings('videoBitrate'), 2147483)

    def audioSubsPref(self, url, listitem):

        dialog = xbmcgui.Dialog()
        # For transcoding only
        # Present the list of audio to select from
        audioStreamsList = {}
        audioStreams = []
        audioStreamsChannelsList = {}
        subtitleStreamsList = {}
        subtitleStreams = ['No subtitles']
        downloadableStreams = []
        selectAudioIndex = ""
        selectSubsIndex = ""
        playurlprefs = "%s" % url

        try:
            mediasources = self.item['MediaSources'][0]
            mediastreams = mediasources['MediaStreams']
        except (TypeError, KeyError, IndexError):
            return

        for stream in mediastreams:
            # Since Emby returns all possible tracks together, have to sort them.
            index = stream['Index']

            if 'Audio' in stream['Type']:
                codec = stream['Codec']
                channelLayout = stream.get('ChannelLayout', "")
               
                try:
                    track = "%s - %s - %s %s" % (index, stream['Language'], codec, channelLayout)
                except:
                    track = "%s - %s %s" % (index, codec, channelLayout)
                
                audioStreamsChannelsList[index] = stream['Channels']
                audioStreamsList[track] = index
                audioStreams.append(track)

            elif 'Subtitle' in stream['Type']:
                try:
                    track = "%s - %s" % (index, stream['Language'])
                except:
                    track = "%s - %s" % (index, stream['Codec'])

                default = stream['IsDefault']
                forced = stream['IsForced']
                downloadable = stream['IsTextSubtitleStream']

                if default:
                    track = "%s - Default" % track
                if forced:
                    track = "%s - Forced" % track
                if downloadable:
                    downloadableStreams.append(index)

                subtitleStreamsList[track] = index
                subtitleStreams.append(track)


        if len(audioStreams) > 1:
            resp = dialog.select(lang(33013), audioStreams)
            if resp > -1:
                # User selected audio
                selected = audioStreams[resp]
                selectAudioIndex = audioStreamsList[selected]
                playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
            else: # User backed out of selection
                playurlprefs += "&AudioStreamIndex=%s" % mediasources['DefaultAudioStreamIndex']
        else: # There's only one audiotrack.
            selectAudioIndex = audioStreamsList[audioStreams[0]]
            playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex

        if len(subtitleStreams) > 1:
            resp = dialog.select(lang(33014), subtitleStreams)
            if resp == 0:
                # User selected no subtitles
                pass
            elif resp > -1:
                # User selected subtitles
                selected = subtitleStreams[resp]
                selectSubsIndex = subtitleStreamsList[selected]

                # Load subtitles in the listitem if downloadable
                if selectSubsIndex in downloadableStreams:

                    itemid = self.item['Id']
                    url = [("%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
                        % (self.server, itemid, itemid, selectSubsIndex))]
                    log.info("Set up subtitles: %s %s" % (selectSubsIndex, url))
                    listitem.setSubtitles(url)
                else:
                    # Burn subtitles
                    playurlprefs += "&SubtitleStreamIndex=%s" % selectSubsIndex

            else: # User backed out of selection
                playurlprefs += "&SubtitleStreamIndex=%s" % mediasources.get('DefaultSubtitleStreamIndex', "")

        # Get number of channels for selected audio track
        audioChannels = audioStreamsChannelsList.get(selectAudioIndex, 0)
        if audioChannels > 2:
            playurlprefs += "&AudioBitrate=384000"
        else:
            playurlprefs += "&AudioBitrate=192000"

        return playurlprefs
    
    def getPlaybackInfo(self):
        #Gets the playback Info for the current item
        url = "{server}/emby/Items/%s/PlaybackInfo?format=json" %self.item['Id']
        body = {   
                "UserId": self.userid,
                "DeviceProfile": self.getDeviceProfile(),
                "StartTimeTicks": 0, #TODO
                "AudioStreamIndex": None, #TODO
                "SubtitleStreamIndex": None, #TODO
                "MediaSourceId": None, 
                "LiveStreamId": None 
                }
        pbinfo = self.doUtils(url, postBody=body, action_type="POST")
        xbmc.log("getPlaybackInfo: %s" %pbinfo)
        mediaSource = self.getOptimalMediaSource(pbinfo["MediaSources"])
        if mediaSource and mediaSource["RequiresOpening"]:
            mediaSource = self.getLiveStream(pbinfo["PlaySessionId"], mediaSource)
        
        return mediaSource

    def getOptimalMediaSource(self, mediasources):
        '''
        Select the best possible mediasource for playback
        Because we posted our deviceprofile to the server, 
        only streams will be returned that can actually be played by this client so no need to check bitrates etc.
        '''
        preferredStreamOrder = ["SupportsDirectPlay","SupportsDirectStream","SupportsTranscoding"]
        bestSource = {}
        for prefstream in preferredStreamOrder:
            for source in mediasources:
                if source[prefstream] == True:
                    if prefstream == "SupportsDirectPlay":
                        #always prefer direct play
                        alt_playurl = self.checkDirectPlayPath(source["Path"])
                        if alt_playurl:
                            bestSource = source
                            source["Path"] = alt_playurl
                    elif bestSource.get("BitRate",0) < source.get("Bitrate",0):
                        #prefer stream with highest bitrate for http sources
                        bestSource = source
                    elif not source.get("Bitrate") and source.get("RequiresOpening"):
                        #livestream
                        bestSource = source
        xbmc.log("getOptimalMediaSource: %s" %bestSource)
        return bestSource
        
    def getLiveStream(self, playSessionId, mediaSource):
        url = "{server}/emby/LiveStreams/Open?format=json"
        body = {   
                "UserId": self.userid,
                "DeviceProfile": self.getDeviceProfile(),
                "ItemId": self.item["Id"],
                "PlaySessionId": playSessionId,
                "OpenToken": mediaSource["OpenToken"],
                "StartTimeTicks": 0, #TODO
                "AudioStreamIndex": None, #TODO
                "SubtitleStreamIndex": None #TODO
                }
        streaminfo = self.doUtils(url, postBody=body, action_type="POST")
        xbmc.log("getLiveStream: %s" %streaminfo)
        return streaminfo["MediaSource"]
            
    def checkDirectPlayPath(self, playurl):

        if self.item.get('VideoType'):
            # Specific format modification
            if self.item['VideoType'] == "Dvd":
                playurl = "%s/VIDEO_TS/VIDEO_TS.IFO" % playurl
            elif self.item['VideoType'] == "BluRay":
                playurl = "%s/BDMV/index.bdmv" % playurl

        # Assign network protocol
        if playurl.startswith('\\\\'):
            playurl = playurl.replace("\\\\", "smb://")
            playurl = playurl.replace("\\", "/")
            
        if xbmcvfs.exists(playurl):
            return playurl
        else:
            return None
    
    def getDeviceProfile(self):
        return {
            "Name": "Kodi",
            "MaxStreamingBitrate": self.getBitrate()*1000,
            "MusicStreamingTranscodingBitrate": 1280000,
            "TimelineOffsetSeconds": 5,
            
            "Identification": {
              "ModelName": "Kodi",
              "Headers": [
                {
                  "Name": "User-Agent",
                  "Value": "Kodi",
                  "Match": 2
                }
              ]
            },
            
            "TranscodingProfiles": [
              {
                "Container": "mp3",
                "AudioCodec": "mp3",
                "Type": 0
              },
              {
                "Container": "ts",
                "AudioCodec": "aac",
                "VideoCodec": "h264",
                "Type": 1
              },
              {
                "Container": "jpeg",
                "Type": 2
              }
            ],
            
            "DirectPlayProfiles": [
              {
                "Container": "",
                "Type": 0
              },
              {
                "Container": "",
                "Type": 1
              },
              {
                "Container": "",
                "Type": 2
              }
            ],
            
            "ResponseProfiles": [],
            "ContainerProfiles": [],
            "CodecProfiles": [],
            
            "SubtitleProfiles": [
              {
                "Format": "srt",
                "Method": 2
              },
              {
                "Format": "sub",
                "Method": 2
              },
              {
                "Format": "srt",
                "Method": 1
              },
              {
                "Format": "ass",
                "Method": 1,
                "DidlMode": ""
              },
              {
                "Format": "ssa",
                "Method": 1,
                "DidlMode": ""
              },
              {
                "Format": "smi",
                "Method": 1,
                "DidlMode": ""
              },
              {
                "Format": "dvdsub",
                "Method": 1,
                "DidlMode": ""
              },
              {
                "Format": "pgs",
                "Method": 1,
                "DidlMode": ""
              },
              {
                "Format": "pgssub",
                "Method": 1,
                "DidlMode": ""
              },
              {
                "Format": "sub",
                "Method": 1,
                "DidlMode": ""
              }
            ]
        }