import xbmc
import xbmcgui
import xbmcaddon

import requests
import json
import logging

import Utils as utils
from ClientInformation import ClientInformation
from requests.packages.urllib3.exceptions import InsecureRequestWarning

# Disable requests logging
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
logging.getLogger("requests").setLevel(logging.WARNING)

class DownloadUtils():
    
    # Borg - multiple instances, shared state
    _shared_state = {}
    clientInfo = ClientInformation()

    addonName = clientInfo.getAddonName()
    addonId = clientInfo.getAddonId()
    addon = xbmcaddon.Addon(id=addonId)
    WINDOW = xbmcgui.Window(10000)

    # Requests session
    s = None
    timeout = 30

    def __init__(self):

        self.__dict__ = self._shared_state

    def logMsg(self, msg, lvl=1):

        self.className = self.__class__.__name__
        utils.logMsg("%s %s" % (self.addonName, self.className), msg, int(lvl))

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

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

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

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

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

    def postCapabilities(self, deviceId):

        # Post settings to session
        url = "{server}/mediabrowser/Sessions/Capabilities/Full"
        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,"

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

        self.logMsg("Capabilities URL: %s" % url, 2)
        self.logMsg("PostData: %s" % data, 2)

        try:
            self.downloadUrl(url, postBody=data, type="POST")
            self.logMsg("Posted capabilities to %s" % self.server, 1)
        except:
            self.logMsg("Posted capabilities failed.")

        # Attempt at getting sessionId
        url = "{server}/mediabrowser/Sessions?DeviceId=%s&format=json" % deviceId

        try:
            result = self.downloadUrl(url)
            self.logMsg("Session: %s" % result, 2)
            
            sessionId = result[0][u'Id']
            self.logMsg("SessionId: %s" % sessionId)
            self.WINDOW.setProperty("sessionId%s" % self.username, sessionId)
        except:
            self.logMsg("Failed to retrieve sessionId.", 1)

    def startSession(self):

        self.deviceId = self.clientInfo.getMachineId()

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

        # If user enabled host certificate verification
        try:
            verify = self.sslverify
            cert = self.sslclient
        except:
            self.logMsg("Could not load SSL settings.", 1)
        
        # Start session
        self.s = requests.Session()
        self.s.headers = header
        self.s.verify = verify
        self.s.cert = cert
        # 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))

        self.logMsg("Requests session started on: %s" % self.server)

    def imageUrl(self, id, type, index, width, height):
        # To move to API.py
        return "%s/mediabrowser/Items/%s/Images/%s?MaxWidth=%s&MaxHeight=%s&Index=%s" % (self.server, id, type, width, height, index)

    def getHeader(self, authenticate=True):

        clientInfo = self.clientInfo

        deviceName = clientInfo.getDeviceName()
        deviceId = clientInfo.getMachineId()
        version = 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}      
            
            self.logMsg("Header: %s" % header, 2)
            return header
        
        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}        
                    
            self.logMsg("Header: %s" % header, 2)
            return header

    def downloadUrl(self, url, postBody=None, type="GET", authenticate=True):
        
        self.logMsg("=== ENTER downloadUrl ===", 2)

        WINDOW = self.WINDOW
        timeout = self.timeout
        default_link = ""

        try:

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

                    self.logMsg("URL: %s" % url, 2)
                    # Prepare request
                    if type == "GET":
                        r = s.get(url, json=postBody, timeout=timeout)
                    elif type == "POST":
                        r = s.post(url, json=postBody, timeout=timeout)
                    elif type == "DELETE":
                        r = s.delete(url, json=postBody, timeout=timeout)
                
                except AttributeError:
                    
                    # Get user information
                    self.username = WINDOW.getProperty('currUser')
                    self.userId = WINDOW.getProperty('userId%s' % self.username)
                    self.server = WINDOW.getProperty('server%s' % self.username)
                    self.token = WINDOW.getProperty('accessToken%s' % self.username)
                    header = self.getHeader()
                    verifyssl = False
                    cert = None

                    # IF user enables ssl verification
                    try:
                        if self.addon.getSetting('sslverify') == "true":
                            verifyssl = True
                        if self.addon.getSetting('sslcert') != "None":
                            cert = self.addon.getSetting('sslcert')
                    except:
                        self.logMsg("Could not load SSL settings.", 1)
                        pass

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

                    self.logMsg("URL: %s" % url, 2)
                    # Prepare request
                    if type == "GET":
                        r = requests.get(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
                    elif type == "POST":
                        r = requests.post(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
                    elif type == "DELETE":
                        r = requests.delete(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)

            # If user is not authenticated
            elif not authenticate:
                
                self.logMsg("URL: %s" % url, 2)
                header = self.getHeader(authenticate=False)
                verifyssl = False

                # If user enables ssl verification
                try:
                    verifyssl = self.sslverify
                except AttributeError:
                    pass
                
                # Prepare request
                if type == "GET":
                    r = requests.get(url, json=postBody, headers=header, timeout=timeout, verify=verifyssl)
                elif type == "POST":
                    r = requests.post(url, json=postBody, headers=header, timeout=timeout, verify=verifyssl)
        
            # Process the response
            if r.status_code == 204:
                # No body in the response
                self.logMsg("====== 204 Success ======", 2)
                return default_link

            elif r.status_code == requests.codes.ok:
                try: 
                    # UTF-8 - JSON object
                    r = r.json()
                    self.logMsg("====== 200 Success ======", 2)
                    self.logMsg("Response: %s" % r, 2)
                    return r
                except:
                    if r.headers['content-type'] == "text/html":
                        pass
                    else:
                        self.logMsg("Unable to convert the response for: %s" % url, 1)
            else:
                r.raise_for_status()

            return default_link
        
        # TO REVIEW EXCEPTIONS
        except requests.exceptions.ConnectionError as e:
            # Make the addon aware of status
            if WINDOW.getProperty("Server_online") != "false":
                self.logMsg("Server unreachable at: %s" % url, 0)
                self.logMsg(e, 2)
                WINDOW.setProperty("Server_online", "false")
            pass

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

        except requests.exceptions.HTTPError as e:

            if r.status_code == 401:
                # Unauthorized
                status = WINDOW.getProperty("Server_status")

                if 'x-application-error-code' in r.headers:
                    if r.headers['X-Application-Error-Code'] == "ParentalControl":
                        # Parental control - access restricted
                        WINDOW.setProperty("Server_status", "restricted")
                        xbmcgui.Dialog().notification("Emby server", "Access restricted.", xbmcgui.NOTIFICATION_ERROR, time=5000)
                        return False

                if (status == "401") or (status == "Auth"):
                    pass

                else:
                    # Tell UserClient token has been revoked.
                    WINDOW.setProperty("Server_status", "401")
                    self.logMsg("HTTP Error: %s" % e, 0)
                    xbmcgui.Dialog().notification("Error connecting", "Unauthorized.", xbmcgui.NOTIFICATION_ERROR)
                    return 401

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

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

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

        return default_link