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

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

import json
import requests
import logging

import xbmc
import xbmcgui

import clientinfo
from utils import Logging, window, settings

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

# Disable requests logging
from requests.packages.urllib3.exceptions import InsecureRequestWarning, InsecurePlatformWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
requests.packages.urllib3.disable_warnings(InsecurePlatformWarning)
#logging.getLogger('requests').setLevel(logging.WARNING)

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


class DownloadUtils():

    # Borg - multiple instances, shared state
    _shared_state = {}
    clientInfo = clientinfo.ClientInfo()
    addonName = clientInfo.getAddonName()

    # Requests session
    s = None
    timeout = 30


    def __init__(self):

        global log
        log = Logging(self.__class__.__name__).log

        self.__dict__ = self._shared_state


    def setUsername(self, username):
        # Reserved for userclient only
        self.username = username
        log("Set username: %s" % username, 2)

    def setUserId(self, userId):
        # Reserved for userclient only
        self.userId = userId
        log("Set userId: %s" % userId, 2)

    def setServer(self, server):
        # Reserved for userclient only
        self.server = server
        log("Set server: %s" % server, 2)

    def setToken(self, token):
        # Reserved for userclient only
        self.token = token
        log("Set token: %s" % token, 2)

    def setSSL(self, ssl, sslclient):
        # Reserved for userclient only
        self.sslverify = ssl
        self.sslclient = sslclient
        log("Verify SSL host certificate: %s" % ssl, 2)
        log("SSL client side certificate: %s" % sslclient, 2)


    def postCapabilities(self, deviceId):

        # Post settings to session
        url = "{server}/emby/Sessions/Capabilities/Full?format=json"
        data = {

            'PlayableMediaTypes': "Audio,Video",
            'SupportsMediaControl': True,
            'SupportedCommands': (

                "MoveUp,MoveDown,MoveLeft,MoveRight,Select,"
                "Back,ToggleContextMenu,ToggleFullscreen,ToggleOsdMenu,"
                "GoHome,PageUp,NextLetter,GoToSearch,"
                "GoToSettings,PageDown,PreviousLetter,TakeScreenshot,"
                "VolumeUp,VolumeDown,ToggleMute,SendString,DisplayMessage,"
                "SetAudioStreamIndex,SetSubtitleStreamIndex,"

                "Mute,Unmute,SetVolume,"
                "Play,Playstate,PlayNext"
            )
        }

        log("Capabilities URL: %s" % url, 2)
        log("Postdata: %s" % data, 2)

        self.downloadUrl(url, postBody=data, action_type="POST")
        log("Posted capabilities to %s" % self.server, 2)

        # Attempt at getting sessionId
        url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId
        result = self.downloadUrl(url)
        try:
            sessionId = result[0]['Id']

        except (KeyError, TypeError):
            log("Failed to retrieve sessionId.", 1)

        else:
            log("Session: %s" % result, 2)
            log("SessionId: %s" % sessionId, 1)
            window('emby_sessionId', value=sessionId)

            # Post any permanent additional users
            additionalUsers = settings('additionalUsers')
            if additionalUsers:

                additionalUsers = additionalUsers.split(',')
                log("List of permanent users added to the session: %s"
                    % additionalUsers, 1)

                # Get the user list from server to get the userId
                url = "{server}/emby/Users?format=json"
                result = self.downloadUrl(url)

                for additional in additionalUsers:
                    addUser = additional.decode('utf-8').lower()

                    # Compare to server users to list of permanent additional users
                    for user in result:
                        username = user['Name'].lower()

                        if username in addUser:
                            userId = user['Id']
                            url = (
                                    "{server}/emby/Sessions/%s/Users/%s?format=json"
                                    % (sessionId, userId)
                            )
                            self.downloadUrl(url, postBody={}, action_type="POST")


    def startSession(self):

        self.deviceId = self.clientInfo.getDeviceId()

        # User is identified from this point
        # Attach authenticated header to the session
        verify = False
        header = self.getHeader()

        # If user enabled host certificate verification
        try:
            verify = self.sslverify
            if self.sslclient is not None:
                verify = self.sslclient
        except:
            log("Could not load SSL settings.", 1)

        # Start session
        self.s = requests.Session()
        self.s.headers = header
        self.s.verify = verify
        # Retry connections to the server
        self.s.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
        self.s.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))

        log("Requests session started on: %s" % self.server, 1)

    def stopSession(self):
        try:
            self.s.close()
        except:
            log("Requests session could not be terminated.", 1)

    def getHeader(self, authenticate=True):

        deviceName = self.clientInfo.getDeviceName()
        deviceName = deviceName.encode('utf-8')
        deviceId = self.clientInfo.getDeviceId()
        version = self.clientInfo.getVersion()

        if not authenticate:
            # If user is not authenticated
            auth = (
                'MediaBrowser Client="Kodi", Device="%s", DeviceId="%s", Version="%s"'
                % (deviceName, deviceId, version))
            header = {

                'Content-type': 'application/json',
                'Accept-encoding': 'gzip',
                'Accept-Charset': 'UTF-8,*',
                'Authorization': auth
            }
            log("Header: %s" % header, 2)

        else:
            userId = self.userId
            token = self.token
            # Attached to the requests session
            auth = (
                'MediaBrowser UserId="%s", Client="Kodi", Device="%s", DeviceId="%s", Version="%s"'
                % (userId, deviceName, deviceId, version))
            header = {

                'Content-type': 'application/json',
                'Accept-encoding': 'gzip',
                'Accept-Charset': 'UTF-8,*',
                'Authorization': auth,
                'X-MediaBrowser-Token': token
            }
            log("Header: %s" % header, 2)

        return header

    def downloadUrl(self, url, postBody=None, action_type="GET", parameters=None,
                    authenticate=True):

        log("=== ENTER downloadUrl ===", 2)

        default_link = ""

        try:
            # If user is authenticated
            if authenticate:
                # Get requests session
                try:
                    s = self.s
                    # Replace for the real values
                    url = url.replace("{server}", self.server)
                    url = url.replace("{UserId}", self.userId)

                    # Prepare request
                    if action_type == "GET":
                        r = s.get(url, json=postBody, params=parameters, timeout=self.timeout)
                    elif action_type == "POST":
                        r = s.post(url, json=postBody, timeout=self.timeout)
                    elif action_type == "DELETE":
                        r = s.delete(url, json=postBody, timeout=self.timeout)

                except AttributeError:
                    # request session does not exists
                    # Get user information
                    self.userId = window('emby_currUser')
                    self.server = window('emby_server%s' % self.userId)
                    self.token = window('emby_accessToken%s' % self.userId)
                    header = self.getHeader()
                    verifyssl = False
                    cert = None

                    # IF user enables ssl verification
                    if settings('sslverify') == "true":
                        verifyssl = True
                    if settings('sslcert') != "None":
                        verifyssl = settings('sslcert')

                    # Replace for the real values
                    url = url.replace("{server}", self.server)
                    url = url.replace("{UserId}", self.userId)

                    # Prepare request
                    if action_type == "GET":
                        r = requests.get(url,
                                        json=postBody,
                                        params=parameters,
                                        headers=header,
                                        timeout=self.timeout,
                                        verify=verifyssl)

                    elif action_type == "POST":
                        r = requests.post(url,
                                        json=postBody,
                                        headers=header,
                                        timeout=self.timeout,
                                        verify=verifyssl)

                    elif action_type == "DELETE":
                        r = requests.delete(url,
                                        json=postBody,
                                        headers=header,
                                        timeout=self.timeout,
                                        verify=verifyssl)

            # If user is not authenticated
            elif not authenticate:

                header = self.getHeader(authenticate=False)
                verifyssl = False

                # If user enables ssl verification
                try:
                    verifyssl = self.sslverify
                    if self.sslclient is not None:
                        verifyssl = self.sslclient
                except AttributeError:
                    pass

                # Prepare request
                if action_type == "GET":
                    r = requests.get(url,
                                    json=postBody,
                                    params=parameters,
                                    headers=header,
                                    timeout=self.timeout,
                                    verify=verifyssl)

                elif action_type == "POST":
                    r = requests.post(url,
                                    json=postBody,
                                    headers=header,
                                    timeout=self.timeout,
                                    verify=verifyssl)

            ##### THE RESPONSE #####
            log(r.url, 2)
            if r.status_code == 204:
                # No body in the response
                log("====== 204 Success ======", 2)

            elif r.status_code == requests.codes.ok:

                try:
                    # UNICODE - JSON object
                    r = r.json()
                    log("====== 200 Success ======", 2)
                    log("Response: %s" % r, 2)
                    return r

                except:
                    if r.headers.get('content-type') != "text/html":
                        log("Unable to convert the response for: %s" % url, 1)
            else:
                r.raise_for_status()

        ##### EXCEPTIONS #####

        except requests.exceptions.ConnectionError as e:
            # Make the addon aware of status
            if window('emby_online') != "false":
                log("Server unreachable at: %s" % url, 0)
                log(e, 2)
                window('emby_online', value="false")

        except requests.exceptions.ConnectTimeout as e:
            log("Server timeout at: %s" % url, 0)
            log(e, 1)

        except requests.exceptions.HTTPError as e:

            if r.status_code == 401:
                # Unauthorized
                status = window('emby_serverStatus')

                if 'X-Application-Error-Code' in r.headers:
                    # Emby server errors
                    if r.headers['X-Application-Error-Code'] == "ParentalControl":
                        # Parental control - access restricted
                        window('emby_serverStatus', value="restricted")
                        xbmcgui.Dialog().notification(
                                                heading="Emby server",
                                                message="Access restricted.",
                                                icon=xbmcgui.NOTIFICATION_ERROR,
                                                time=5000)
                        return False

                    elif r.headers['X-Application-Error-Code'] == "UnauthorizedAccessException":
                        # User tried to do something his emby account doesn't allow
                        pass

                elif status not in ("401", "Auth"):
                    # Tell userclient token has been revoked.
                    window('emby_serverStatus', value="401")
                    log("HTTP Error: %s" % e, 0)
                    xbmcgui.Dialog().notification(
                                            heading="Error connecting",
                                            message="Unauthorized.",
                                            icon=xbmcgui.NOTIFICATION_ERROR)
                    return 401

            elif r.status_code in (301, 302):
                # Redirects
                pass
            elif r.status_code == 400:
                # Bad requests
                pass

        except requests.exceptions.SSLError as e:
            log("Invalid SSL certificate for: %s" % url, 0)
            log(e, 1)

        except requests.exceptions.RequestException as e:
            log("Unknown error connecting to: %s" % url, 0)
            log(e, 1)

        return default_link