# -*- coding: utf-8 -*-
import json
import threading
import os

import xbmc
import xbmcvfs
import xbmcaddon

import helper.loghandler
import helper.xmls
import helper.jsonrpc
import helper.context
import helper.pluginmenu
import database.database
import database.library
import emby.connect
import hooks.webservice
import core.listitem
import core.artwork
from . import player

try:
    import queue as Queue
except ImportError:
    import Queue

class Monitor(xbmc.Monitor):
    def __init__(self, Service):
        self.LOG = helper.loghandler.LOG('EMBY.hooks.monitor.Monitor')
        self.sleep = False
        self.EmbyServers = {}
        self.library = {}
        self.ServerIP = None
        self.ServerToken = None
        self.Server = None
        self.WebServiceThread = None
        self.WebserviceOnPlayThread = None
        self.WebserviceEventOut = Queue.Queue()
        self.WebserviceEventIn = Queue.Queue()
        self.Service = Service
        self.player = player.PlayerEvents()
        self.Context = helper.context.Context(self.Service.Utils, self.EmbyServers, self.library)
        self.Menu = helper.pluginmenu.Menu(self.Service.Utils, self.EmbyServers, self.player)
        self.Xmls = helper.xmls.Xmls(self.Service.Utils)
        self.connect = emby.connect.Connect(self.Service.Utils) #multipe servers here!!!!!!!!!!!!!!!!!!!!!!!!!!!

    def EmbyServer_ReconnectAll(self):
        for server_id in self.EmbyServers:
            self.Service.ServerReconnect(server_id, False)

    def EmbyServer_DisconnectAll(self):
        for server_id in self.EmbyServers:
            self.EmbyServers[server_id].stop()
            self.EmbyServers[server_id].Online = False
            self.StopServer({'ServerId': server_id})

    def EmbyServer_Connect(self):
        server_id, EmbyServer = self.connect.register({})

        if server_id == 'cancel':
            return False

        if not server_id:
            self.LOG.error("EmbyServer Connect error")
            return False

        self.EmbyServers[server_id] = EmbyServer
        self.EmbyServers[server_id].start()
        self.ServerOnline({'ServerId': server_id})
        return server_id

    #Add theme media locally, via strm. This is only for tv tunes.
    #If another script is used, adjust this code
    def SyncThemes(self, data):
        library = self.Service.Utils.translatePath("special://profile/addon_data/plugin.video.emby-next-gen/library")

        if not xbmcvfs.exists(library + '/'):
            xbmcvfs.mkdir(library)

        if xbmc.getCondVisibility('System.HasAddon(script.tvtunes)'):
            tvtunes = xbmcaddon.Addon(id="script.tvtunes")
            tvtunes.setSetting('custom_path_enable', "true")
            tvtunes.setSetting('custom_path', library)
            self.LOG.info("TV Tunes custom path is enabled and set.")
        elif xbmc.getCondVisibility('System.HasAddon(service.tvtunes)'):
            tvtunes = xbmcaddon.Addon(id="service.tvtunes")
            tvtunes.setSetting('custom_path_enable', "true")
            tvtunes.setSetting('custom_path', library)
            self.LOG.info("TV Tunes custom path is enabled and set.")
        else:
            self.Service.Utils.dialog("ok", heading="{emby}", line1=self.Service.Utils.Translate(33152))
            return

        with database.database.Database(self.Service.Utils, 'emby', False) as embydb:
            all_views = database.emby_db.EmbyDatabase(embydb.cursor).get_views()
            views = [x[0] for x in all_views if x[2] in ('movies', 'tvshows', 'mixed')]

        items = {}

        for view in views:
            for result in self.EmbyServers[data['ServerId']].API.get_itemsSync(view, None, False, {'HasThemeVideo': True}):
                for item in result['Items']:
                    folder = self.Service.Utils.normalize_string(item['Name'])
                    items[item['Id']] = folder

            for result in self.EmbyServers[data['ServerId']].API.get_itemsSync(view, None, False, {'HasThemeSong': True}):
                for item in result['Items']:
                    folder = self.Service.Utils.normalize_string(item['Name'])
                    items[item['Id']] = folder

        for item in items:
            nfo_path = os.path.join(library, items[item])
            nfo_file = os.path.join(nfo_path, "tvtunes.nfo")

            if not xbmcvfs.exists(nfo_path):
                xbmcvfs.mkdir(nfo_path)

            themes = self.EmbyServers[data['ServerId']].API.get_themes(item)
            paths = []

            for theme in themes['ThemeVideosResult']['Items'] + themes['ThemeSongsResult']['Items']:
                if self.Service.Utils.direct_path:
                    paths.append(theme['MediaSources'][0]['Path'])
                else:
                    paths.append(self.Service.Utils.direct_url(theme))

            self.Xmls.tvtunes_nfo(nfo_file, paths)

        self.Service.Utils.dialog("notification", heading="{emby}", message=self.Service.Utils.Translate(33153), icon="{emby}", time=1000, sound=False)

    def DatabaseReset(self):
        database.database.reset(self.Service.Utils, False)

    def TextureCache(self):
        core.artwork.Artwork(None, self.Service.Utils).cache_textures()

    def LibraryStopAll(self):
        for server_id in self.library:
            self.library[server_id].stop_thread = True

        self.library = {}

    def LibraryStop(self, server_id):
        if server_id in self.library:
            self.library[server_id].stop_thread = True
            del self.library[server_id]

    def LibraryLoad(self, server_id):
        if not server_id in self.library:
            self.ServerOnline({'ServerId': server_id})
            self.library[server_id] = database.library.Library(self.player, self.EmbyServers[server_id])

    def onScanStarted(self, library):
        self.LOG.info("-->[ kodi scan/%s ]" % library)

    def onScanFinished(self, library):
        self.LOG.info("--<[ kodi scan/%s ]" % library)

    def onSettingsChanged(self):
        new_thread = MonitorWorker(self, None, "settingschanged", None)
        new_thread.start()

    def onNotification(self, sender, method, data):
        if sender.lower() not in ('plugin.video.emby-next-gen', 'plugin.video.emby', 'xbmc', 'upnextprovider.signal'):
            return

        if self.sleep:
            self.LOG.info("System.OnSleep detected, ignore monitor request.")
            return

        new_thread = MonitorWorker(self, sender, method, data)
        new_thread.start()

    #Emby playstate updates.
    def Playstate(self, data):
        command = data['Command']
        actions = {
            'Stop': self.player.stop,
            'Unpause': self.player.pause,
            'Pause': self.player.pause,
            'PlayPause': self.player.pause,
            'NextTrack': self.player.playnext,
            'PreviousTrack': self.player.playprevious
        }

        if command == 'Seek':
            if self.player.isPlaying():
                seektime = data['SeekPositionTicks'] / 10000000.0
                self.player.seekTime(seektime)
                self.LOG.info("[ seek/%s ]" % seektime)
        elif command in actions:
            actions[command]()
            self.LOG.info("[ command/%s ]" % command)

    #General commands from Emby to control the Kodi interface.
    def GeneralCommand(self, data):
        command = data['Name']
        args = data['Arguments']

        if command in ('Mute', 'Unmute', 'SetVolume', 'SetSubtitleStreamIndex', 'SetAudioStreamIndex', 'SetRepeatMode'):
            if command == 'Mute':
                xbmc.executebuiltin('Mute')
            elif command == 'Unmute':
                xbmc.executebuiltin('Mute')
            elif command == 'SetVolume':
                xbmc.executebuiltin('SetVolume(%s[,showvolumebar])' % args['Volume'])
            elif command == 'SetRepeatMode':
                xbmc.executebuiltin('xbmc.PlayerControl(%s)' % args['RepeatMode'])
#            elif command == 'SetAudioStreamIndex':
#                self.player.set_audio_subs(args['Index'])
#            elif command == 'SetSubtitleStreamIndex':
#                self.player.set_audio_subs(None, args['Index'])

            self.player.report_playback()
        elif command == 'DisplayMessage':
            self.Service.Utils.dialog("notification", heading=args['Header'], message=args['Text'], icon="{emby}", time=int(self.Service.Utils.Settings.displayMessage) * 1000)
        elif command == 'SendString':
            helper.jsonrpc.JSONRPC('Input.SendText').execute({'text': args['String'], 'done': False})
        elif command == 'GoHome':
            helper.jsonrpc.JSONRPC('GUI.ActivateWindow').execute({'window': "home"})
        elif command == 'Guide':
            helper.jsonrpc.JSONRPC('GUI.ActivateWindow').execute({'window': "tvguide"})
        elif command in ('MoveUp', 'MoveDown', 'MoveRight', 'MoveLeft'):
            actions = {
                'MoveUp': "Input.Up",
                'MoveDown': "Input.Down",
                'MoveRight': "Input.Right",
                'MoveLeft': "Input.Left"
            }
            helper.jsonrpc.JSONRPC(actions[command]).execute(False)
        else:
            builtin = {
                'ToggleFullscreen': 'Action(FullScreen)',
                'ToggleOsdMen': 'Action(OSD)',
                'ToggleContextMen': 'Action(ContextMenu)',
                'Select': 'Action(Select)',
                'Back': 'Action(back)',
                'PageUp': 'Action(PageUp)',
                'NextLetter': 'Action(NextLetter)',
                'GoToSearch': 'VideoLibrary.Search',
                'GoToSettings': 'ActivateWindow(Settings)',
                'PageDown': 'Action(PageDown)',
                'PreviousLetter': 'Action(PrevLetter)',
                'TakeScreenshot': 'TakeScreenshot',
                'ToggleMute': 'Mute',
                'VolumeUp': 'Action(VolumeUp)',
                'VolumeDown': 'Action(VolumeDown)'
            }

            if command in builtin:
                xbmc.executebuiltin(builtin[command])

    def StopServer(self, data):
        self.EmbyServers[data['ServerId']].Online = False

    def Player_OnAVChange(self, *args, **kwargs):
        self.ReportProgressRequested(*args, **kwargs)

    def ReportProgressRequested(self, data):
        if not self.player.Trailer:
            self.player.report_playback()

    def Player_OnStop(self, data):
        for server_id in self.EmbyServers: ######################## WORKAROUND!!!!!!!!!!!
            break

        self.player.OnStop(self.EmbyServers[server_id])

    def Player_OnPlay(self, data):
        for server_id in self.EmbyServers: ######################## WORKAROUND!!!!!!!!!!!
            break

        self.player.OnPlay(data, self.EmbyServers[server_id], self.library[server_id])

    def AddPlaylistItem(self, Position, EmbyID, server_id, Offset):
        Data = database.database.get_kodiID(self.Service.Utils, str(EmbyID))

        if Data: #Requested video is synced to KodiDB. No additional info required
            if Data[0][1] in ("song", "album", "artist"):
                playlistID = 0
                playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
            else:
                playlistID = 1
                playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)

            Pos = self.GetPlaylistPos(Position, playlist, Offset)

            helper.jsonrpc.JSONRPC('Playlist.Insert').execute({'playlistid': playlistID, 'position': Pos, 'item': {'%sid' % Data[0][1]: int(Data[0][0])}})
        else:
            listitems = core.listitem.ListItem(self.Service.Utils)
            item = self.EmbyServers[server_id].API.get_item(EmbyID)
            li = listitems.set(item)
            path = ""

            if item['Type'] == "MusicVideo":
                Type = "musicvideo"
            elif item['Type'] == "Movie":
                Type = "movie"
            elif item['Type'] == "Episode":
                Type = "tvshow"
            elif item['Type'] == "Audio":
                Type = "audio"
            elif item['Type'] == "Video":
                Type = "video"
            elif item['Type'] == "Trailer":
                Type = "trailer"
            elif item['Type'] == "TvChannel":
                Type = "tvchannel"
                path = "http://127.0.0.1:57578/livetv/%s-stream.ts" % item['Id']
            else:
                return

            if not path:
                if 'MediaSources' in item:
                    FilenameURL = self.Service.Utils.PathToFilenameReplaceSpecialCharecters(item['Path'])

                    if len(item['MediaSources'][0]['MediaStreams']) >= 1:
                        path = "http://127.0.0.1:57578/%s/%s-%s-%s-stream-%s" % (Type, item['Id'], item['MediaSources'][0]['Id'], item['MediaSources'][0]['MediaStreams'][0]['BitRate'], FilenameURL)
                    else:
                        path = "http://127.0.0.1:57578/%s/%s-%s-stream-%s" % (Type, item['Id'], item['MediaSources'][0]['Id'], FilenameURL)

            li.setProperty('path', path)

            if Type == "audio":
                playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
            else:
                playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)

            Pos = self.GetPlaylistPos(Position, playlist, Offset)
            self.player.DynamicItem[self.Service.Utils.ReplaceSpecialCharecters(li.getLabel())] = item['Id']
            playlist.add(path, li, index=Pos)

        return playlist

    def GetPlaylistPos(self, Position, playlist, Offset):
        if Position == "current":
            Pos = playlist.getposition()

            if Pos == -1:
                Pos = 0

            Pos = Pos + Offset
        elif Position == "previous":
            Pos = playlist.getposition()

            if Pos == -1:
                Pos = 0
        elif Position == "last":
            Pos = playlist.size()
        else:
            Pos = Position

        return Pos

    #Websocket command from Emby server
    def Play(self, data):
        FirstItem = True
        Offset = 0

        for ID in data['ItemIds']:
            Offset += 1

            if data["PlayCommand"] == "PlayNow":
                playlist = self.AddPlaylistItem("current", ID, data['ServerId'], Offset)
            elif data["PlayCommand"] == "PlayNext":
                playlist = self.AddPlaylistItem("current", ID, data['ServerId'], Offset)

            elif data["PlayCommand"] == "PlayLast":
                playlist = self.AddPlaylistItem("last", ID, data['ServerId'], 0)

            #Play Item
            if data["PlayCommand"] == "PlayNow":
                if "StartIndex" in data:
                    if Offset == int(data["StartIndex"] + 1):
                        if FirstItem:
                            Pos = playlist.getposition()

                            if Pos == -1:
                                Pos = 0

                            self.player.play(item=playlist, startpos=Pos + Offset)

                            if "StartPositionTicks" in data:
                                self.player.seekTime(int(data["StartPositionTicks"]) / 100000)

                            Offset = 0
                            FirstItem = False
                else:
                    if FirstItem:
                        Pos = playlist.getposition()

                        if Pos == -1:
                            Pos = 0

                        self.player.play(item=playlist, startpos=Pos + Offset)

                        if "StartPositionTicks" in data:
                            self.player.seekTime(int(data["StartPositionTicks"]) / 100000)

                        Offset = 0
                        FirstItem = False

    def Playlist_OnClear(self, data):
        self.player.played = {}

    def ServerConnect(self, data):
        self.EmbyServer_Connect()
        xbmc.executebuiltin("Container.Refresh")

    def RemoveServer(self, data):
        self.EmbyServers[data['ServerId']].close()
        self.connect.remove_server(data['ServerId'])
        xbmc.executebuiltin("Container.Refresh")

    def UserDataChanged(self, data):
        if data.get('UserId') != self.EmbyServers[data['ServerId']].auth.get_server_info()['UserId']:
            return

        self.LOG.info("[ UserDataChanged ] %s" % data)
        UpdateData = []

        for ItemData in data['UserDataList']:
            if not ItemData['ItemId'] in self.player.ItemSkipUpdate: #Check EmbyID
                item = database.database.get_Presentationkey(self.Service.Utils, ItemData['ItemId'])

                if item:
                    PresentationKey = item.split("-")

                    if not PresentationKey[0] in self.player.ItemSkipUpdate: #Check PresentationKey
                        UpdateData.append(ItemData)
                    else:
                        self.LOG.info("[ skip update/%s ]" % ItemData['ItemId'])
                else:
                    UpdateData.append(ItemData)
            else:
                self.LOG.info("[ skip update/%s ]" % ItemData['ItemId'])

        if UpdateData:
            self.library[data['ServerId']].userdata(UpdateData)

        if self.player.ItemSkipUpdateReset:
            self.player.ItemSkipUpdateReset = False
            self.player.ItemSkipUpdate = []
            self.LOG.info("[ skip reset ]")

    def LibraryChanged(self, data):
        self.LOG.info("[ LibraryChanged ] %s" % data)
        self.library[data['ServerId']].updated(data['ItemsUpdated'] + data['ItemsAdded'])
        self.library[data['ServerId']].removed(data['ItemsRemoved'])
        self.library[data['ServerId']].delay_verify(data.get('ItemsVerify', []))

    def WebSocketRestarting(self, data):
        self.library[data['ServerId']].get_fast_sync()

    def SyncLibrarySelection(self, data):
        self.library[data['ServerId']].select_libraries("SyncLibrarySelection")

    def RepairLibrarySelection(self, data):
        self.library[data['ServerId']].select_libraries("RepairLibrarySelection")

    def AddLibrarySelection(self, data):
        self.library[data['ServerId']].select_libraries("AddLibrarySelection")

    def RemoveLibrarySelection(self, data):
        self.library[data['ServerId']].select_libraries("RemoveLibrarySelection")

    def ServerUnreachable(self, data):
        if self.Service.ServerReconnectingInProgress(data['ServerId']):
            return

        self.Service.Utils.dialog("notification", heading="{emby}", message=self.Service.Utils.Translate(33146))
        self.EmbyServers[data['ServerId']].Online = False
        self.Service.ServerReconnect(data['ServerId'])

    def ServerShuttingDown(self, data):
        if self.Service.ServerReconnectingInProgress(data['ServerId']):
            return

        self.Service.Utils.dialog("notification", heading="{emby}", message="Enable server shutdown")
        self.EmbyServers[data['ServerId']].Online = False
        self.Service.ServerReconnect(data['ServerId'])

    def UserConfigurationUpdated(self, data):
        self.Service.Views.get_views()

    def UserPolicyUpdated(self, data):
        self.Service.Views.get_views()

    def GUI_OnScreensaverDeactivated(self, data):
        self.LOG.info("--<[ screensaver ]")
        xbmc.sleep(5000)

        if data['ServerId'] in self.library:
            self.library[data['ServerId']].get_fast_sync()

    def PatchMusic(self, data):
        self.library[data['ServerId']].patch_music(data.get('Notification', True))

    def Unauthorized(self, data):
        self.EmbyServers[data['ServerId']].Online = False
        self.Service.Utils.dialog("notification", heading="{emby}", message=self.Service.Utils.Translate(33147))

    def System_OnQuit(self):
        self.Server = None

        for server_id in self.EmbyServers:
            if self.player.Transcoding:
                self.EmbyServers[server_id].API.close_transcode()

        self.Service.ShouldStop = True

    def ServerRestarting(self, data):
        self.Server = None

        if self.Service.ServerReconnectingInProgress(data['ServerId']):
            return

        if self.Service.Utils.Settings.restartMsg:
            self.Service.Utils.dialog("notification", heading="{emby}", message=self.Service.Utils.Translate(33006), icon="{emby}")

        self.EmbyServers[data['ServerId']].Online = False
        self.Service.ServerReconnect(data['ServerId'])

    def WebserviceUpdateInfo(self, server):
        if self.ServerIP:
            if server: #Restart Webservice
                self.Server = server

                if self.WebserviceOnPlayThread:
                    self.WebserviceOnPlayThread.Stop()
                    self.WebserviceOnPlayThread = None

                self.WebserviceOnPlayThread = player.WebserviceOnPlay(self.player, server, self.WebserviceEventIn, self.WebserviceEventOut)
                self.WebserviceOnPlayThread.start()

                if self.WebServiceThread:
                    self.WebServiceThread.stop()
                    self.WebServiceThread.join()
                    self.WebServiceThread = None

                self.WebServiceThread = hooks.webservice.WebService(self.WebserviceEventOut, self.WebserviceEventIn, self.ServerIP, self.ServerToken, self.Service.Utils.Settings.enableCoverArt, self.Service.Utils.Settings.compressArt)
                self.WebServiceThread.start()
        else:
            self.LOG.info("[ WebserviceUpdateInfo -> No Info ]")

    def QuitThreads(self):
        if self.WebserviceOnPlayThread:
            self.WebserviceOnPlayThread.Stop()
            self.WebserviceOnPlayThread = None

        if self.player.ProgressThread:
            self.player.ProgressThread.Stop()
            self.player.ProgressThread = None

        if self.WebServiceThread:
            self.WebServiceThread.stop()
            self.WebServiceThread = None

    def AddServer(self, data):
#        self.connect.setup_manual_server()
#        xbmc.executebuiltin("Container.Refresh")
        self.EmbyServer_Connect()

    def SetServerSSL(self, data):
        self.connect.set_ssl(data['ServerId'])

    def settingschanged(self):
        if self.Service.ShouldStop or self.sleep:
            return

        self.Service.Utils.Settings.InitSettings()

    def Application_OnVolumeChanged(self, data):
        self.player.SETVolume(data['volume'], data['muted'])

    def System_OnWake(self):
        if not self.sleep:
            self.LOG.warning("System.OnSleep was never called, skip System.OnWake")
            return

        self.LOG.info("--<[ sleep ]")
        self.sleep = False
        self.EmbyServer_ReconnectAll()
        self.player.SyncPause = False

    def System_OnSleep(self):
        self.LOG.info("-->[ sleep ]")
        self.player.SyncPause = True
        self.QuitThreads()
        self.EmbyServer_DisconnectAll()
        self.LibraryStopAll()
        self.sleep = True

    def ServerOnline(self, data):
        self.LOG.info("[ Server Online ]")

        if self.EmbyServers[data['ServerId']].Online:
            return

        self.EmbyServers[data['ServerId']].Online = True
        self.ServerIP = self.EmbyServers[data['ServerId']].auth.get_serveraddress()
        self.ServerToken = self.EmbyServers[data['ServerId']].Data['auth.token']
        self.WebserviceUpdateInfo(self.EmbyServers[data['ServerId']])

        if self.Service.Utils.Settings.ReloadSkin:
            xbmc.executebuiltin('ReloadSkin()')
            self.Service.Utils.Settings.set_settings_bool('ReloadSkin', False)

        if self.Service.Utils.Settings.connectMsg:
            users = self.EmbyServers[data['ServerId']].API.get_device()[0]   # ['AdditionalUsers']
            users = users['UserName']
#            users = [user['UserName'] for user in users['AdditionalUsers']]
#            users.insert(0, self.Service.Utils.Settings.username)
#            self.Service.Utils.dialog("notification", heading="{emby}", message="%s %s" % (self.Service.Utils.Translate(33000), ", ".join(users)), icon="{emby}", time=1500, sound=False)
            self.Service.Utils.dialog("notification", heading="{emby}", message="%s %s" % (self.Service.Utils.Translate(33000), self.Service.Utils.StringMod(users)), icon="{emby}", time=1500, sound=False)

    #Mark as watched/unwatched updates
    def VideoLibrary_OnUpdate(self, data):
        for server_id in self.EmbyServers: ######################## WORKAROUND!!!!!!!!!!!  ADD Serverid info in emby.db and query from there
            break

        if 'item' in data:
            if 'playcount' in data:
                kodi_id = data['item']['id']
                media = data['item']['type']
                item = database.database.get_item_complete(self.Service.Utils, kodi_id, media) #read here serverid

                if item:
                    if not item[0] in self.player.ItemSkipUpdate: #Check EmbyID
                        if media == "tvshow": #Search for all items in TVShow and update them
                            PresentationKey = item[10].split("-")
                            items = database.database.get_ItemsByPresentationkey(self.Service.Utils, PresentationKey[0])

                            for item2 in items:
                                self.player.ItemSkipUpdate.append(item2[0])
                                self.EmbyServers[server_id].API.item_played(item2[0], bool(data['playcount']))

                            return

                        self.EmbyServers[server_id].API.item_played(item[0], bool(data['playcount']))

                    else:
                        self.player.ItemSkipUpdate.append(item[0])

            self.player.ItemSkipUpdateReset = True

    #Emby backup
    def Backup(self, data):
        if not self.Service.Utils.Settings.backupPath:
            self.Service.Utils.dialog("notification", heading="{emby}", icon="{emby}", message="No backup path set", sound=False)
            return

        path = self.Service.Utils.Settings.backupPath
        folder_name = "Kodi%s.%s" % (xbmc.getInfoLabel('System.BuildVersion')[:2], xbmc.getInfoLabel('System.Date(dd-mm-yy)'))
        folder_name = self.Service.Utils.dialog("input", heading=self.Service.Utils.Translate(33089), defaultt=folder_name)

        if not folder_name:
            return

        backup = os.path.join(path, folder_name)

        if xbmcvfs.exists(backup + '/'):
            if not self.Service.Utils.dialog("yesno", heading="{emby}", line1=self.Service.Utils.Translate(33090)):
                return backup()

            self.Service.Utils.delete_folder(backup)

        addon_data = self.Service.Utils.translatePath("special://profile/addon_data/plugin.video.emby-next-gen")
        destination_data = os.path.join(backup, "addon_data", "plugin.video.emby-next-gen")
        destination_databases = os.path.join(backup, "Database")

        if not xbmcvfs.mkdirs(path) or not xbmcvfs.mkdirs(destination_databases):
            self.LOG.info("Unable to create all directories")
            self.Service.Utils.dialog("notification", heading="{emby}", icon="{emby}", message=self.Service.Utils.Translate(33165), sound=False)
            return

        self.Service.Utils.copytree(addon_data, destination_data)
        db = self.Service.Utils.translatePath("special://database/")
        _, files = xbmcvfs.listdir(db)

        for Temp in files:
            if 'MyVideos' in Temp:
                xbmcvfs.copy(os.path.join(db, Temp), os.path.join(destination_databases, Temp))
                self.LOG.info("copied %s" % Temp)
            elif 'emby' in Temp:
                xbmcvfs.copy(os.path.join(db, Temp), os.path.join(destination_databases, Temp))
                self.LOG.info("copied %s" % Temp)
            elif 'MyMusic' in Temp:
                xbmcvfs.copy(os.path.join(db, Temp), os.path.join(destination_databases, Temp))
                self.LOG.info("copied %s" % Temp)

        self.LOG.info("backup completed")
        self.Service.Utils.dialog("ok", heading="{emby}", line1="%s %s" % (self.Service.Utils.Translate(33091), backup))

    #Add or remove users from the default server session
    #permanent=True from the add-on settings
    def AddUser(self, data):
        server_id = data['ServerId']
        permanent = True
        session = self.EmbyServers[server_id].API.get_device()
        hidden = None if self.Service.Utils.Settings.addUsersHidden else False
        users = self.EmbyServers[server_id].API.get_users(False, hidden)

        if not users:
            return

        for user in users:
            if user['Id'] == session[0]['UserId']:
                users.remove(user)
                break

        while True:
            session = self.EmbyServers[server_id].API.get_device()
            current = session[0]['AdditionalUsers']

            if permanent:
                perm_users = self.Service.Utils.Settings.Users.split(',') if self.Service.Utils.Settings.Users else []
                current = []

                for user in users:
                    for perm_user in perm_users:

                        if user['Id'] == perm_user:
                            current.append({'UserName': user['Name'], 'UserId': user['Id']})

            result = self.Service.Utils.dialog("select", self.Service.Utils.Translate(33061), [self.Service.Utils.Translate(33062), self.Service.Utils.Translate(33063)] if current else [self.Service.Utils.Translate(33062)])

            if result < 0:
                break

            if not result: # Add user
                eligible = [x for x in users if x['Id'] not in [current_user['UserId'] for current_user in current]]
                resp = self.Service.Utils.dialog("select", self.Service.Utils.Translate(33064), [x['Name'] for x in eligible])

                if resp < 0:
                    break

                user = eligible[resp]

                if permanent:
                    perm_users.append(user['Id'])
                    self.Service.Utils.Settings.set_settings('Users', ','.join(perm_users))

                self.Service.Utils.dialog("notification", heading="{emby}", message="%s %s" % (self.Service.Utils.Translate(33067), user['Name']), icon="{emby}", time=1000, sound=False)
            else: # Remove user
                resp = self.Service.Utils.dialog("select", self.Service.Utils.Translate(33064), [x['UserName'] for x in current])

                if resp < 0:
                    break

                user = current[resp]

                if permanent:
                    perm_users.remove(user['UserId'])
                    self.Service.Utils.Settings.set_settings('Users', ','.join(perm_users))

                self.Service.Utils.dialog("notification", heading="{emby}", message="%s %s" % (self.Service.Utils.Translate(33066), user['UserName']), icon="{emby}", time=1000, sound=False)

#Thread the monitor so that we can do whatever we need without blocking onNotification.
class MonitorWorker(threading.Thread):
    def __init__(self, monitor, sender, method, data):
        self.sender = sender
        self.method = method
        self.data = data
        self.monitor = monitor
        threading.Thread.__init__(self)

    def run(self):
        if self.method == 'System.OnWake':
            self.monitor.System_OnWake()
            return

        if self.method == 'Other.DeleteItem':
            self.monitor.Context.delete_item(True)
            return

        if self.method == 'Other.browse':
            self.data = json.loads(self.data)[0]
            self.monitor.Service.Utils.set_queryIO('emby_event_ack_%s' % self.data['QueryId'])
            self.monitor.Menu.browse(self.data['Handle'], self.data['type'], self.data['id'], self.data['folder'], self.data['name'], self.data['extra'], self.data['ServerId'])
            self.monitor.Service.Utils.set_queryIO('emby_event_%s' % self.data['QueryId'])
            return

        if self.method == 'Other.manage_libraries':
            self.data = json.loads(self.data)[0]
            self.monitor.Service.Utils.set_queryIO('emby_event_ack_%s' % self.data['QueryId'])
            self.monitor.Menu.manage_libraries(self.data['Handle'], self.data['ServerId'])
            self.monitor.Service.Utils.set_queryIO('emby_event_%s' % self.data['QueryId'])
            return

        if self.method == 'Other.nextepisodes':
            self.data = json.loads(self.data)[0]
            self.monitor.Service.Utils.set_queryIO('emby_event_ack_%s' % self.data['QueryId'])
            self.monitor.Menu.get_next_episodes(self.data['Handle'], self.data['libraryname'])
            self.monitor.Service.Utils.set_queryIO('emby_event_%s' % self.data['QueryId'])
            return

        if self.method == 'Other.listing':
            self.data = json.loads(self.data)[0]
            self.monitor.Service.Utils.set_queryIO('emby_event_ack_%s' % self.data['QueryId'])
            self.monitor.Menu.listing(self.data['Handle'])
            self.monitor.Service.Utils.set_queryIO('emby_event_%s' % self.data['QueryId'])
            return

        if self.method == 'Other.restartservice':
            self.monitor.Service.Utils.Settings.emby_restart = True
            return

        if self.method == 'Other.reset_device_id':
            self.monitor.Service.Utils.reset_device_id()
            return

        if self.method == 'Other.context':
            self.monitor.Context.select_menu()
            return

        if self.method == 'System.OnSleep':
            self.monitor.System_OnSleep()
            return

        if self.method == 'System.OnQuit':
            self.monitor.System_OnQuit()
            return

        if self.method == 'settingschanged':
            self.monitor.settingschanged()
            return

        if self.method == 'Other.DatabaseReset':
            self.monitor.DatabaseReset()
            return

        if self.method == 'Other.TextureCache':
            self.monitor.TextureCache()
            return

        if self.sender == 'plugin.video.emby-next-gen':
            self.method = self.method.split('.')[1]

            if self.method not in ('ReportProgressRequested', 'Play', 'Playstate', 'GeneralCommand', 'StopServer', 'RegisterServer', 'ServerOnline', 'ServerConnect', 'AddUser', 'AddServer', 'RemoveServer', 'SetServerSSL', 'UserDataChanged', 'LibraryChanged', 'WebSocketRestarting', 'SyncLibrarySelection', 'RepairLibrarySelection', 'AddLibrarySelection', 'RemoveLibrarySelection', 'GUI.OnScreensaverDeactivated', 'PatchMusic', 'UserConfigurationUpdated', 'UserPolicyUpdated', 'ServerUnreachable', 'ServerShuttingDown', 'ServerRestarting', 'Unauthorized', 'SyncThemes', 'Backup'):
                return

            self.data = json.loads(self.data)[0]
        elif self.sender.startswith('upnextprovider'):
            self.method = self.method.split('.')[1]

            if self.method != 'plugin.video.emby-next-gen_play_action':
                return

            self.method = "Play"
            self.data = json.loads(self.data)
        else:
            if self.method not in ('Player.OnPlay', 'Player.OnStop', 'VideoLibrary.OnUpdate', 'Player.OnAVChange', 'Playlist.OnClear', 'Application.OnVolumeChanged'):
                return

            self.data = json.loads(self.data)

        self.data['MonitorMethod'] = self.method
        func = getattr(self.monitor, self.method.replace('.', '_'))
        func(self.data)
