import collections
import os
import re
import sys
import time
import urllib
import xbmc
import xbmcaddon
from datetime import datetime

oldpython = sys.version_info < (2, 7)
if oldpython:
    import simplejson as json
else:
    import json

try:
    datetime.strptime('2112-04-01', '%Y-%m-%d')
except TypeError:
    pass

_log_level_tag_lookup = {
    xbmc.LOGDEBUG: 'D',
    xbmc.LOGINFO: 'I'
}

_log_scrub_strings = {}

ADDONID = 'script.artwork.beef'

thumbnailimages = ('image://video@',)
remoteimages = ('http',)
embeddedimages = ('image://video_', 'image://music')
notimagefiles = remoteimages + thumbnailimages + embeddedimages

_main_addon = None
def get_main_addon():
    global _main_addon
    if not _main_addon:
        _main_addon = Addon()
    return _main_addon

_watch_addon = None
def is_addon_watched():
    global _watch_addon
    if _watch_addon is None:
        if not get_conditional('System.HasAddon(script.module.devhelper)'):
            _watch_addon = False
        else:
            devhelper = Addon('script.module.devhelper')
            if devhelper.get_setting('watchalladdons'):
                _watch_addon = True
            else:
                _watch_addon = ADDONID in devhelper.get_setting('watchaddons_list')

    return _watch_addon

_kodiversion = None
def get_kodi_version():
    global _kodiversion
    if _kodiversion is None:
        _kodiversion = int(get_infolabel('System.BuildVersion').split('.')[0])
    return _kodiversion

def localize(messageid):
    if isinstance(messageid, basestring):
        result = messageid
    elif messageid >= 32000 and messageid < 33000:
        result = get_main_addon().getLocalizedString(messageid)
    else:
        result = xbmc.getLocalizedString(messageid)
    return result.encode('utf-8') if isinstance(result, unicode) else result

def get_conditional(conditional):
    return xbmc.getCondVisibility(conditional)

def get_infolabel(infolabel):
    return xbmc.getInfoLabel(infolabel)

def execute_builtin(builtin_command):
    xbmc.executebuiltin(builtin_command)

def datetime_now():
    try:
        return datetime.now()
    except ImportError:
        xbmc.sleep(50)
        return datetime_now()

def datetime_strptime(date_string, format_string):
    try:
        return datetime.strptime(date_string, format_string)
    except TypeError:
        try:
            return datetime(*(time.strptime(date_string, format_string)[0:6]))
        except ImportError:
            xbmc.sleep(50)
            return datetime_strptime(date_string, format_string)

def execute_jsonrpc(jsonrpc_command):
    if isinstance(jsonrpc_command, dict):
        try:
            jsonrpc_command = json.dumps(jsonrpc_command)
        except UnicodeDecodeError:
            jsonrpc_command = json.dumps(jsonrpc_command, ensure_ascii=False)

    json_result = xbmc.executeJSONRPC(jsonrpc_command)
    try:
        return json.loads(json_result, cls=UTF8JSONDecoder)
    except UnicodeDecodeError:
        return json.loads(json_result.decode('utf-8', 'replace'), cls=UTF8JSONDecoder)

def log(message, level=xbmc.LOGDEBUG, tag=None):
    if is_addon_watched() and level < xbmc.LOGNOTICE:
        # Messages from this add-on are being watched, elevate to NOTICE so Kodi logs it
        level_tag = _log_level_tag_lookup[level] + ': ' if level in _log_level_tag_lookup else ''
        level = xbmc.LOGNOTICE
    else:
        level_tag = ''

    if isinstance(message, (dict, list)) and len(message) > 300:
        message = str(message)
    elif isinstance(message, unicode):
        message = message.encode('utf-8')
    elif not isinstance(message, str):
        message = json.dumps(message, cls=UTF8PrettyJSONEncoder)

    addontag = ADDONID if not tag else ADDONID + ':' + tag
    file_message = '%s[%s] %s' % (level_tag, addontag, scrub_message(message))
    xbmc.log(file_message, level)

def scrub_message(message):
    for string in _log_scrub_strings.values():
        message = message.replace(string, "XXXXX")
    return message

def set_log_scrubstring(key, string):
    if string and len(string) > 4:
        _log_scrub_strings[key] = string
    elif key in _log_scrub_strings:
        del _log_scrub_strings[key]

def get_language(language_format=xbmc.ENGLISH_NAME, region=False):
    language = xbmc.getLanguage(language_format, region)
    if not language or region and language.startswith('-'):
        engname = xbmc.getLanguage(xbmc.ENGLISH_NAME)
        regiontag = language
        if ' (' in engname:
            language = engname[:engname.rfind(' (')]
        if language_format != xbmc.ENGLISH_NAME:
            language = xbmc.convertLanguage(language, language_format) + regiontag
    return language

def unquoteimage(imagestring):
    # extracted thumbnail images need to keep their 'image://' encoding
    if imagestring.startswith('image://') and not imagestring.startswith(('image://video', 'image://music')):
        return urllib.unquote(imagestring[8:-1])
    return imagestring

def quoteimage(imagestring):
    if imagestring.startswith('image://'):
        return imagestring
    # Kodi goes lowercase and doesn't encode some chars
    result = 'image://{0}/'.format(urllib.quote(imagestring, '()!'))
    result = re.sub(r'%[0-9A-F]{2}', lambda mo: mo.group().lower(), result)
    return result

def unquotearchive(filepath):
    # DEPRECATED: Krypton and below need this, Leia changed archive support to be a standard file path
    if not filepath or not filepath.startswith(('rar://', 'zip://')):
        return filepath
    result = filepath[6:].split('/', 1)[0]
    return urllib.unquote(result)

def get_command(*first_arg_keys):
    command = {}
    start = len(first_arg_keys) if first_arg_keys else 1
    for x in range(start, len(sys.argv)):
        arg = sys.argv[x].split("=")
        command[arg[0].strip().lower()] = arg[1].strip() if len(arg) > 1 else True

    if first_arg_keys:
        for i, argkey in enumerate(first_arg_keys, 1):
            if len(sys.argv) <= i:
                break
            command[argkey] = sys.argv[i]
    return command

def get_busydialog():
    return DialogBusy()

class Addon(xbmcaddon.Addon):
    def __init__(self, *args, **kwargs):
        super(Addon, self).__init__()
        self.addonid = self.getAddonInfo('id')
        self.version = self.getAddonInfo('version')
        self.path = self.getAddonInfo('path')
        self.datapath = self.getAddonInfo('profile') # WARN: This can change if Kodi profile changes
        self.resourcespath = os.path.join(xbmc.translatePath(self.path).decode('utf-8'), u'resources')
        if not os.path.isdir(self.resourcespath):
            self.resourcespath = None

    def get_setting(self, settingid):
        result = self.getSetting(settingid)
        if result == 'true':
            result = True
        elif result == 'false':
            result = False
        elif settingid.endswith('_list'):
            result = [addon.strip() for addon in result.split('|')]
            if len(result) == 1 and not result[0]:
                result = []
        return result

    def set_setting(self, settingid, value):
        if settingid.endswith('_list') and not isinstance(value, basestring) \
        and isinstance(value, collections.Sequence):
            value = '|'.join(value)
        elif isinstance(value, bool):
            value = 'true' if value else 'false'
        elif not isinstance(value, basestring):
            value = str(value)
        self.setSetting(settingid, value)

class ObjectJSONEncoder(json.JSONEncoder):
    # Will still flop on circular objects
    def __init__(self, *args, **kwargs):
        kwargs['skipkeys'] = True
        super(ObjectJSONEncoder, self).__init__(*args, **kwargs)

    def default(self, obj):
        # Called for objects that aren't directly JSON serializable
        if isinstance(obj, collections.Mapping):
            return dict((key, obj[key]) for key in obj.keys())
        if isinstance(obj, collections.Sequence):
            return list(obj)
        if callable(obj):
            return str(obj)
        try:
            result = dict(obj.__dict__)
            result['* objecttype'] = str(type(obj))
            return result
        except AttributeError:
            pass # obj has no __dict__ attribute
        result = {'* dir': dir(obj)}
        result['* objecttype'] = str(type(obj))
        return result

class UTF8PrettyJSONEncoder(ObjectJSONEncoder):
    def __init__(self, *args, **kwargs):
        kwargs['ensure_ascii'] = False
        kwargs['indent'] = 2
        kwargs['separators'] = (',', ': ')
        super(UTF8PrettyJSONEncoder, self).__init__(*args, **kwargs)

    def iterencode(self, obj, _one_shot=False):
        for result in super(UTF8PrettyJSONEncoder, self).iterencode(obj, _one_shot):
            if isinstance(result, unicode):
                result = result.encode('utf-8')
            yield result

class UTF8JSONDecoder(json.JSONDecoder):
    def raw_decode(self, s, idx=0):
        args = (s,) if oldpython else (s, idx)
        result, end = super(UTF8JSONDecoder, self).raw_decode(*args)
        result = self._json_unicode_to_str(result)
        return result, end

    def _json_unicode_to_str(self, jsoninput):
        if isinstance(jsoninput, dict):
            return dict((self._json_unicode_to_str(key), self._json_unicode_to_str(value)) for key, value in jsoninput.iteritems())
        elif isinstance(jsoninput, list):
            return [self._json_unicode_to_str(item) for item in jsoninput]
        elif isinstance(jsoninput, unicode):
            return jsoninput.encode('utf-8')
        else:
            return jsoninput

class DialogBusy(object):
    def __init__(self):
        self.visible = False
        window = 'busydialognocancel' if get_kodi_version() >= 18 else 'busydialog'
        self._activate = 'ActivateWindow({0})'.format(window)
        self._close = 'Dialog.Close({0})'.format(window)

    def create(self):
        xbmc.executebuiltin(self._activate)
        self.visible = True

    def close(self):
        xbmc.executebuiltin(self._close)
        self.visible = False

    def __del__(self):
        if self.visible:
            try:
                xbmc.executebuiltin(self._close)
            except AttributeError:
                pass