# -*- coding: utf-8 -*-
"""Minimal Kodi module stubs so wcs/library2 runs offline (docs/plan/milionar-library/120 §2)."""
import os
import sys
import tempfile
import types

ADDON_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
LOG = []
PROFILE = None


def install():
    """Fake xbmc/xbmcvfs/xbmcaddon/xbmcgui; special://profile maps to a temp directory. Returns its path."""
    global PROFILE
    if PROFILE is not None:      # one set of fake modules per process, shared by every test module
        return PROFILE
    if ADDON_DIR not in sys.path:
        sys.path.insert(0, ADDON_DIR)
    PROFILE = tempfile.mkdtemp(prefix='milionar-lib-')

    xbmc = types.ModuleType('xbmc')
    xbmc.LOGDEBUG, xbmc.LOGINFO, xbmc.LOGWARNING, xbmc.LOGERROR = 0, 1, 2, 3
    xbmc.log = lambda msg, level=1: LOG.append((level, msg))
    xbmc.getCondVisibility = lambda cond: False
    xbmc.executebuiltin = lambda *a, **k: None
    xbmc.sleep = lambda ms: None
    xbmc.getInfoLabel = lambda label: ''
    xbmc.ISO_639_1, xbmc.ISO_639_2, xbmc.ENGLISH_NAME = 0, 1, 2
    xbmc.getSkinDir = lambda: 'skin.milionar'
    xbmc.getLanguage = lambda format=2, region=False: 'English'
    xbmc.getLocalizedString = lambda string_id: {20366: '* All seasons'}.get(string_id, '')
    xbmc.RPC_CALLS = []
    xbmc.RPC_REPLIES = {}   # method -> result dict (default {'result': 'OK'})

    def execute_rpc(request):
        import json
        call = json.loads(request)
        xbmc.RPC_CALLS.append(call)
        return json.dumps({'id': call.get('id'), 'jsonrpc': '2.0',
                           'result': xbmc.RPC_REPLIES.get(call['method'], 'OK')})
    xbmc.executeJSONRPC = execute_rpc

    class Player:
        played = []                       # what play() got (playlists)
        playing = False

        def play(self, item=None, *args, **kwargs):
            Player.played.append(item)

        def isPlaying(self):
            return Player.playing

        def getTime(self):
            return 0.0

        def seekTime(self, seconds):
            pass

    class PlayList:
        def __init__(self, kind=0):
            self.items = []

        def clear(self):
            self.items = []

        def add(self, url, listitem=None, *args, **kwargs):
            self.items.append((url, listitem))

        def size(self):
            return len(self.items)

        def getposition(self):
            return 0
    xbmc.PlayList, xbmc.PLAYLIST_VIDEO = PlayList, 1

    class Monitor:
        def abortRequested(self):
            return False

        def waitForAbort(self, timeout=0):
            return False
    xbmc.Player, xbmc.Monitor = Player, Monitor
    sys.modules['xbmc'] = xbmc

    def translate(path):
        if path.startswith('special://profile/'):
            return os.path.join(PROFILE, path[len('special://profile/'):])
        if path.startswith('special://home/'):
            return os.path.join(PROFILE, 'home', path[len('special://home/'):])
        return path
    xbmcvfs = types.ModuleType('xbmcvfs')
    xbmcvfs.translatePath = translate

    def real(path):
        return translate(path)
    xbmcvfs.exists = lambda path: os.path.exists(real(path))
    xbmcvfs.mkdir = lambda path: os.makedirs(real(path), exist_ok=True) or True
    xbmcvfs.mkdirs = xbmcvfs.mkdir
    xbmcvfs.delete = lambda path: (os.path.exists(real(path)) and os.unlink(real(path))) is not False

    def rename(source, target):
        os.replace(real(source), real(target))
        return True
    xbmcvfs.rename = rename

    def copy(source, target):
        import shutil
        shutil.copyfile(real(source), real(target))
        return True
    xbmcvfs.copy = copy

    def listdir(path):
        entries = sorted(os.listdir(real(path)))
        return ([e for e in entries if os.path.isdir(os.path.join(real(path), e))],
                [e for e in entries if os.path.isfile(os.path.join(real(path), e))])
    xbmcvfs.listdir = listdir

    class File:
        """read() -> str unless opened binary; write() takes str or bytes; usable as a context manager."""

        def __init__(self, path, mode='r'):
            self.binary = 'b' in mode
            self.handle = open(real(path), mode.replace('b', '') + 'b')

        def read(self, size=-1):
            data = self.handle.read(size)
            return data if self.binary else data.decode('utf-8')

        def readBytes(self, size=-1):
            return bytearray(self.handle.read(size))

        def write(self, data):
            self.handle.write(data if isinstance(data, (bytes, bytearray)) else data.encode('utf-8'))
            return True

        def size(self):
            return os.fstat(self.handle.fileno()).st_size

        def close(self):
            self.handle.close()

        def __enter__(self):
            return self

        def __exit__(self, *exc):
            self.close()
    xbmcvfs.File = File

    class Stat:
        def __init__(self, path):
            self.info = os.stat(real(path))

        def st_size(self):
            return self.info.st_size

        def st_mtime(self):
            return self.info.st_mtime
    xbmcvfs.Stat = Stat
    sys.modules['xbmcvfs'] = xbmcvfs

    xbmcaddon = types.ModuleType('xbmcaddon')

    class Addon:
        settings = {}

        def __init__(self, addon_id=None):
            pass

        def getSetting(self, key):
            return Addon.settings.get(key, '')

        def setSetting(self, key, value):
            Addon.settings[key] = str(value)

        strings = {}

        def getLocalizedString(self, string_id):
            return Addon.strings.get(string_id, '')

        def getAddonInfo(self, key):
            return {'path': ADDON_DIR, 'profile': PROFILE + os.sep, 'id': 'plugin.video.milionar',
                    'version': '0.0.0'}.get(key, '')
    xbmcaddon.Addon = Addon
    sys.modules['xbmcaddon'] = xbmcaddon

    xbmcgui = types.ModuleType('xbmcgui')

    class Window:
        props = {}

        def __init__(self, window_id=0):
            pass

        def setProperty(self, key, value):
            Window.props[key] = value

        def getProperty(self, key):
            return Window.props.get(key, '')

        def clearProperty(self, key):
            Window.props.pop(key, None)
    xbmcgui.Window = Window

    class ListControl:
        def __init__(self):
            self.items, self.selected = [], 0

        def reset(self):
            self.items, self.selected = [], 0

        def addItems(self, items):
            self.items.extend(items)

        def selectItem(self, index):
            self.selected = index

        def getSelectedPosition(self):
            return self.selected

    class WindowXMLDialog(Window):
        def __init__(self, *args, **kwargs):
            self.controls, self.closed = {}, False

        def getControl(self, control_id):
            return self.controls.setdefault(control_id, ListControl())

        def setFocusId(self, control_id):
            self.focus = control_id

        def close(self):
            self.closed = True

    xbmcgui.WindowXMLDialog = WindowXMLDialog
    xbmcgui.NOTIFICATION_INFO = 'info'
    xbmcgui.NOTIFICATIONS = []

    class Dialog:
        def notification(self, heading, message, icon='', time=0):
            xbmcgui.NOTIFICATIONS.append((heading, message))

        def select(self, heading, labels, *args, **kwargs):
            return -1

        def contextmenu(self, labels):
            return -1

        def yesno(self, heading, message, *args, **kwargs):
            return False
    xbmcgui.Dialog = Dialog

    class VideoTag:
        """Records every setter call as tag.values[name-without-set]; setResumePoint -> values['ResumePoint']."""

        def __init__(self):
            self.values = {}

        def __getattr__(self, name):
            if not name.startswith('set'):
                raise AttributeError(name)
            return lambda *args: self.values.__setitem__(name[3:], args if len(args) != 1 else args[0])

    class ListItem:
        def __init__(self, label='', label2='', path='', offscreen=False):
            self.label, self.path, self.art, self.props, self.tag = label, path, {}, {}, VideoTag()
            self.context = []

        def getPath(self):
            return self.path

        def setPath(self, path):
            self.path = path

        def getLabel(self):
            return self.label

        def setArt(self, art):
            self.art.update(art)

        def setProperty(self, key, value):
            self.props[key] = value

        def getProperty(self, key):
            return self.props.get(key, '')

        def getVideoInfoTag(self):
            return self.tag

        def setLabel(self, label):
            self.label = label

        def setInfo(self, kind, info):
            self.info = dict(info)

        def addContextMenuItems(self, items, replaceItems=False):
            self.context.extend(items)

        def setIsFolder(self, value):
            self.props['_folder'] = value
    xbmcgui.ListItem = ListItem
    sys.modules['xbmcgui'] = xbmcgui

    xbmc.Actor = lambda name='', role='', order=0, thumbnail='': {'name': name, 'role': role, 'order': order, 'thumb': thumbnail}

    xbmcplugin = types.ModuleType('xbmcplugin')
    xbmcplugin.DIRECTORY = []        # [(handle, [(path, item, is_folder)...], content, sort_methods, ended_kwargs)]
    xbmcplugin.RESOLVED = []
    xbmcplugin.CONTENT, xbmcplugin.SORTS, xbmcplugin.CATEGORY = {}, {}, {}
    for number, name in enumerate(('SORT_METHOD_TITLE_IGNORE_THE', 'SORT_METHOD_VIDEO_YEAR', 'SORT_METHOD_DATEADDED',
                                   'SORT_METHOD_VIDEO_RATING', 'SORT_METHOD_LASTPLAYED', 'SORT_METHOD_EPISODE',
                                   'SORT_METHOD_UNSORTED', 'SORT_METHOD_LABEL')):
        setattr(xbmcplugin, name, number + 1)
    xbmcplugin.ITEMS = {}
    xbmcplugin.ENDED = {}
    xbmcplugin.addDirectoryItems = lambda handle, items, total=0: xbmcplugin.ITEMS.setdefault(handle, []).extend(items)
    xbmcplugin.addDirectoryItem = lambda handle, url, item, isFolder=False, totalItems=0: \
        xbmcplugin.ITEMS.setdefault(handle, []).append((url, item, isFolder))
    xbmcplugin.setContent = lambda handle, content: xbmcplugin.CONTENT.__setitem__(handle, content)
    xbmcplugin.setPluginCategory = lambda handle, text: xbmcplugin.CATEGORY.__setitem__(handle, text)
    xbmcplugin.addSortMethod = lambda handle, method, label2Mask='': xbmcplugin.SORTS.setdefault(handle, []).append(method)
    xbmcplugin.endOfDirectory = lambda handle, succeeded=True, updateListing=False, cacheToDisc=True: \
        xbmcplugin.ENDED.__setitem__(handle, {'succeeded': succeeded, 'updateListing': updateListing, 'cacheToDisc': cacheToDisc})
    xbmcplugin.setResolvedUrl = lambda handle, succeeded, item: xbmcplugin.RESOLVED.append((handle, succeeded))
    sys.modules['xbmcplugin'] = xbmcplugin
    return PROFILE
