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

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

import threading

import xbmc
import xbmcgui
import xbmcaddon
import xbmcvfs

import utils
import downloadutils

import PlexAPI
from PlexFunctions import GetMachineIdentifier

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


@utils.logging
@utils.ThreadMethodsAdditionalSuspend('suspend_Userclient')
@utils.ThreadMethods
class UserClient(threading.Thread):

    # Borg - multiple instances, shared state
    __shared_state = {}

    def __init__(self):
        self.__dict__ = self.__shared_state

        self.auth = True
        self.retry = 0

        self.currUser = None
        self.currUserId = None
        self.currServer = None
        self.currToken = None
        self.HasAccess = True
        self.AdditionalUser = []

        self.userSettings = None

        self.addon = xbmcaddon.Addon()
        self.doUtils = downloadutils.DownloadUtils()

        threading.Thread.__init__(self)

    def getUsername(self):
        """
        Returns username as unicode
        """

        username = utils.settings('username')

        if not username:
            self.logMsg("No username saved, trying to get Plex username", 0)
            username = utils.settings('plexLogin')
            if not username:
                self.logMsg("Also no Plex username found", 0)
                return ""

        return username

    def getServer(self, prefix=True):

        settings = utils.settings

        # Original host
        self.servername = settings('plex_servername')
        HTTPS = settings('https') == "true"
        host = settings('ipaddress')
        port = settings('port')
        self.machineIdentifier = settings('plex_machineIdentifier')

        server = host + ":" + port

        if not host:
            self.logMsg("No server information saved.", 2)
            return False

        # If https is true
        if prefix and HTTPS:
            server = "https://%s" % server
        # If https is false
        elif prefix and not HTTPS:
            server = "http://%s" % server
        # User entered IP; we need to get the machineIdentifier
        if self.machineIdentifier == '' and prefix is True:
            self.machineIdentifier = GetMachineIdentifier(server)
            if self.machineIdentifier is None:
                self.machineIdentifier = ''
            settings('plex_machineIdentifier', value=self.machineIdentifier)
        self.logMsg('Returning active server: %s' % server)
        return server

    def getSSLverify(self):
        # Verify host certificate
        return None if utils.settings('sslverify') == 'true' else False

    def getSSL(self):
        # Client side certificate
        return None if utils.settings('sslcert') == 'None' \
            else utils.settings('sslcert')

    def setUserPref(self):
        self.logMsg('Setting user preferences', 0)
        # Only try to get user avatar if there is a token
        if self.currToken:
            url = PlexAPI.PlexAPI().GetUserArtworkURL(self.currUser)
            if url:
                utils.window('EmbyUserImage', value=url)
        # Set resume point max
        # url = "{server}/emby/System/Configuration?format=json"
        # result = doUtils.downloadUrl(url)

        # utils.settings('markPlayed', value=str(result['MaxResumePct']))

    def hasAccess(self):
        # Plex: always return True for now
        return True
        # hasAccess is verified in service.py
        log = self.logMsg
        window = utils.window

        url = "{server}/emby/Users?format=json"
        result = self.doUtils.downloadUrl(url)

        if result == False:
            # Access is restricted, set in downloadutils.py via exception
            log("Access is restricted.", 1)
            self.HasAccess = False

        elif window('emby_online') != "true":
            # Server connection failed
            pass

        elif window('emby_serverStatus') == "restricted":
            log("Access is granted.", 1)
            self.HasAccess = True
            window('emby_serverStatus', clear=True)
            xbmcgui.Dialog().notification(self.addonName,
                                          utils.language(33007))

    def loadCurrUser(self, username, userId, usertoken, authenticated=False):
        self.logMsg('Loading current user', 0)
        window = utils.window
        settings = utils.settings
        doUtils = self.doUtils

        self.currUserId = userId
        self.currToken = usertoken
        self.currServer = self.getServer()
        self.ssl = self.getSSLverify()
        self.sslcert = self.getSSL()

        if authenticated is False:
            self.logMsg('Testing validity of current token', 0)
            res = PlexAPI.PlexAPI().CheckConnection(self.currServer,
                                                    token=self.currToken,
                                                    verifySSL=self.ssl)
            if res is False:
                self.logMsg('Answer from PMS is not as expected. Retrying', -1)
                return False
            elif res == 401:
                self.logMsg('Token is no longer valid', -1)
                return False
            elif res >= 400:
                self.logMsg('Answer from PMS is not as expected. Retrying', -1)
                return False

        # Set to windows property
        window('currUserId', value=userId)
        window('plex_username', value=username)
        # This is the token for the current PMS (might also be '')
        window('pms_token', value=self.currToken)
        # This is the token for plex.tv for the current user
        # Is only '' if user is not signed in to plex.tv
        window('plex_token', value=settings('plexToken'))
        window('pms_server', value=self.currServer)
        window('plex_machineIdentifier', value=self.machineIdentifier)
        window('plex_servername', value=self.servername)
        window('plex_authenticated', value='true')

        window('useDirectPaths', value='true'
               if utils.settings('useDirectPaths') == "1" else 'false')
        window('replaceSMB', value='true'
               if utils.settings('replaceSMB') == "true" else 'false')
        window('remapSMB', value='true'
               if utils.settings('remapSMB') == "true" else 'false')
        if window('remapSMB') == 'true':
            items = ('movie', 'tv', 'music')
            for item in items:
                # Normalize! Get rid of potential (back)slashes at the end
                org = settings('remapSMB%sOrg' % item)
                new = settings('remapSMB%sNew' % item)
                if org.endswith('\\') or org.endswith('/'):
                    org = org[:-1]
                if new.endswith('\\') or new.endswith('/'):
                    new = new[:-1]
                window('remapSMB%sOrg' % item, value=org)
                window('remapSMB%sNew' % item, value=new)

        # Start DownloadUtils session
        doUtils.startSession()
        # self.getAdditionalUsers()
        # Set user preferences in settings
        self.currUser = username
        self.setUserPref()

        # Writing values to settings file
        settings('username', value=username)
        settings('userid', value=userId)
        settings('accessToken', value=usertoken)

        dialog = xbmcgui.Dialog()
        if utils.settings('connectMsg') == "true":
            if username:
                dialog.notification(
                    heading=self.addonName,
                    message="Welcome " + username,
                    icon="special://home/addons/plugin.video.plexkodiconnect/icon.png")
            else:
                dialog.notification(
                    heading=self.addonName,
                    message="Welcome",
                    icon="special://home/addons/plugin.video.plexkodiconnect/icon.png")
        return True

    def authenticate(self):
        log = self.logMsg
        log('Authenticating user', 1)
        lang = utils.language
        window = utils.window
        settings = utils.settings
        dialog = xbmcgui.Dialog()

        # Give attempts at entering password / selecting user
        if self.retry >= 2:
            log("Too many retries to login.", -1)
            window('emby_serverStatus', value="Stop")
            dialog.ok(lang(33001),
                      lang(39023))
            xbmc.executebuiltin(
                'Addon.OpenSettings(plugin.video.plexkodiconnect)')
            return False

        # Get /profile/addon_data
        addondir = xbmc.translatePath(
            self.addon.getAddonInfo('profile')).decode('utf-8')
        hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)

        # If there's no settings.xml
        if not hasSettings:
            log("Error, no settings.xml found.", -1)
            self.auth = False
            return False
        server = self.getServer()
        # If there is no server we can connect to
        if not server:
            log("Missing server information.", 0)
            self.auth = False
            return False

        # If there is a username in the settings, try authenticating
        username = settings('username')
        userId = settings('userid')
        usertoken = settings('accessToken')
        enforceLogin = settings('enforceUserLogin')
        # Found a user in the settings, try to authenticate
        if username and enforceLogin == 'false':
            log('Trying to authenticate with old settings', 0)
            if self.loadCurrUser(username,
                                 userId,
                                 usertoken,
                                 authenticated=False):
                # SUCCESS: loaded a user from the settings
                return True
            else:
                # Failed to use the settings - delete them!
                log("Failed to use the settings credentials. Deleting them", 1)
                settings('username', value='')
                settings('userid', value='')
                settings('accessToken', value='')

        plx = PlexAPI.PlexAPI()

        # Could not use settings - try to get Plex user list from plex.tv
        plextoken = settings('plexToken')
        if plextoken:
            log("Trying to connect to plex.tv to get a user list", 0)
            userInfo = plx.ChoosePlexHomeUser(plextoken)
            if userInfo is False:
                # FAILURE: Something went wrong, try again
                self.auth = True
                self.retry += 1
                return False
            username = userInfo['username']
            userId = userInfo['userid']
            usertoken = userInfo['token']
        else:
            log("Trying to authenticate without a token", 0)
            username = ''
            userId = ''
            usertoken = ''

        if self.loadCurrUser(username, userId, usertoken, authenticated=False):
            # SUCCESS: loaded a user from the settings
            return True
        else:
            # FAILUR: Something went wrong, try again
            self.auth = True
            self.retry += 1
            return False

    def resetClient(self):
        self.logMsg("Reset UserClient authentication.", 1)

        self.doUtils.stopSession()

        settings = utils.settings
        window = utils.window

        window('plex_authenticated', clear=True)
        window('pms_token', clear=True)
        window('plex_token', clear=True)
        window('pms_server', clear=True)
        window('plex_machineIdentifier', clear=True)
        window('plex_servername', clear=True)
        window('currUserId', clear=True)
        window('plex_username', clear=True)

        settings('username', value='')
        settings('userid', value='')
        settings('accessToken', value='')

        # Reset token in downloads
        self.doUtils.setToken('')
        self.doUtils.setUserId('')
        self.doUtils.setUsername('')

        self.currToken = None
        self.auth = True
        self.currUser = None
        self.currUserId = None

        self.retry = 0

    def run(self):
        log = self.logMsg
        window = utils.window

        log("----===## Starting UserClient ##===----", 0)
        while not self.threadStopped():
            while self.threadSuspended():
                if self.threadStopped():
                    break
                xbmc.sleep(1000)

            status = window('emby_serverStatus')

            if status == "Stop":
                xbmc.sleep(500)
                continue

            # Verify the connection status to server
            elif status == "restricted":
                # Parental control is restricting access
                self.HasAccess = False

            elif status == "401":
                # Unauthorized access, revoke token
                window('emby_serverStatus', value="Auth")
                self.resetClient()
                xbmc.sleep(2000)

            if self.auth and (self.currUser is None):
                # Try to authenticate user
                if not status or status == "Auth":
                    # Set auth flag because we no longer need
                    # to authenticate the user
                    self.auth = False
                    if self.authenticate():
                        # Successfully authenticated and loaded a user
                        log("Successfully authenticated!", 1)
                        log("Current user: %s" % self.currUser, 1)
                        log("Current userId: %s" % self.currUserId, 1)
                        log("Current accessToken: xxxx", 1)
                        self.retry = 0
                        window('suspend_LibraryThread', clear=True)
                        window('emby_serverStatus', clear=True)

            if not self.auth and (self.currUser is None):
                # Loop if no server found
                server = self.getServer()

                # The status Stop is for when user cancelled password dialog.
                # Or retried too many times
                if server and status != "Stop":
                    # Only if there's information found to login
                    log("Server found: %s" % server, 2)
                    self.auth = True

            # Minimize CPU load
            xbmc.sleep(100)

        self.doUtils.stopSession()
        log("##===---- UserClient Stopped ----===##", 0)