import hashlib
import os
import xml.etree.ElementTree as ET
import xml.dom.minidom as minidom

import xbmc
import xbmcgui
import xbmcvfs
import xbmcaddon

ADSFNAME = "advancedsettings.xml"
SRCFNAME = "settings.src.xml"
PLGFNAME = "settings.xml"

plg_settings = None
adv_settings = None

def log(msg):
    xbmc.log(msg, level=xbmc.LOGINFO)

def __init__():


def unlock(addon):
    path = addon.getAddonInfo('path')
    ads_path = xbmcvfs.translatePath("special://userdata")
    data_path = xbmcvfs.translatePath(addon.getAddonInfo('profile'))
    ads_file = os.path.join(ads_path, ADSFNAME)
    sts_file = os.path.join(data_path, PLGFNAME)
    plg_file = os.path.join(os.path.join(path, "resources"), PLGFNAME)

    log('plg_file=' + str(plg_file))

    srcfname = os.path.join(os.path.join(path, "resources"), SRCFNAME)
    xbmcvfs.copy(srcfname, plg_file)

    plg_settings = _load_xml_from_file(plg_file)
    try:
        adv_settings = _load_xml_from_file(ads_file)
    except ET.ParseError:
        xbmcgui.Dialog().notification(addon.getAddonInfo("name"),
                                      "%s %s" % (language(30800), ADSFNAME),
                                      xbmcgui.NOTIFICATION_ERROR, 5000)

    _load(plg_settings)
    md5 = _get_file_hash(sts_file)
    addon.openSettings()
    if md5 != _get_file_hash(sts_file) and xbmcgui.Dialog().yesno(addon.getAddonInfo("name"),
                                                                            language(30801)):
        _save()
        xbmcgui.Dialog().notification(addon.getAddonInfo("name"),
                                      language(30802),
                                      xbmcgui.NOTIFICATION_INFO, 5000)
    xbmcvfs.delete(plg_file)

def _load(plg_settings):

    for cat in plg_settings.findall("category"):
        for s in cat.findall("setting[@id]"):
            setting_id = s.attrib['id']
            addon.setSetting(setting_id, _read_adv_setting_value(cat, s))

def _save():
    if adv_settings is None:
        adv_settings = ET.fromstring("<advancedsettings version='1.0' />")

    for cat in plg_settings.findall("category"):
        for s in cat.findall("setting[@id]"):
            setting_id = s.attrib['id']
            value = addon.getSetting(setting_id)

            _save_adv_setting_value(cat, s, value)

        adv_cat = adv_settings.find(cat.attrib['id'])
        if not (adv_cat is None) and len(adv_cat) == 0:
            adv_settings.remove(adv_cat)

    _backup_file(ads_file)
    _save_pretty_xml(adv_settings, ads_file)

def _save_adv_setting_value( cat, s, value):
    xbmc.log("Category %s, setting %s" % (cat.attrib['id'], s.attrib['id']), xbmc.LOGDEBUG)
    default = s.attrib.get('default', "")

    section_tag = cat.attrib['id']
    section = adv_settings if _is_root_cat(cat) else adv_settings.find(section_tag)
    if section is None:
        section = ET.SubElement(adv_settings, section_tag)

    setting_tag = s.attrib['id'].partition("#")[0]
    setting = _lookup_element(section, setting_tag)

    if default == value:
        if not (setting is None):
            if "#" in s.attrib['id']:
                attrib = s.attrib['id'].partition("#")[2]
                if attrib in setting.attrib:
                    setting.attrib.pop(attrib)
                if setting.text == "":
                    _remove_element(section, setting_tag)
            else:
                if len(setting.attrib) == 0:
                    _remove_element(section, setting_tag)
        return

    if setting is None:
        setting = _create_element(section, setting_tag)

    _write_setting_value(setting, s, _encode_value(value, s))

def _read_adv_setting_value( cat, s):
    xbmc.log("Category %s, setting %s" % (cat.attrib['id'], s.attrib['id']), xbmc.LOGDEBUG)
    if adv_settings is None:
        xbmc.log("%s not found" % ADSFNAME, xbmc.LOGDEBUG)
        return s.attrib.get('default', "")

    section_tag = cat.attrib['id']
    section = adv_settings if _is_root_cat(cat) else adv_settings.find(section_tag)
    if section is None:
        xbmc.log("Section %s not found" % section_tag, xbmc.LOGDEBUG)
        return s.attrib.get('default', "")

    setting = _lookup_element(section, s.attrib['id'].partition("#")[0])
    if not (setting is None):
        return _decode_value(_read_setting_value(setting, s), s)
    else:
        xbmc.log("Setting %s not found" % s.attrib['id'], xbmc.LOGDEBUG)
        return s.attrib.get('default', "")

def _lookup_element( parent, path):
    if parent is None:
        return None
    pathelem = path.split("/")
    if len(pathelem) == 1:
        if "$" in path:
            name_index = path.split("$")
            elements = parent.findall(name_index[0])
            index = int(name_index[1])
            if index < len(elements):
                return elements[index]
            else:
                return None
        else:
            return parent.find(path)
    else:
        return _lookup_element(parent.find(pathelem[0]), "/".join(pathelem[1:]))

def _create_element( parent, path):

    pathelem = path.split("/")
    if len(pathelem) == 1:
        if "$" in path:
            name_index = path.split("$")
            xbmc.log("Create element %s.%s" % (parent.tag, name_index[0]))
            return ET.SubElement(parent, name_index[0])
        else:
            return ET.SubElement(parent, path)
    else:
        subparent = parent.find(pathelem[0])
        if subparent is None:
            subparent = ET.SubElement(parent, pathelem[0])
        return _create_element(subparent, "/".join(pathelem[1:]))

def _remove_element( rootparent, path):
    elem = _lookup_element(rootparent, path)
    if elem is None:
        return
    pathelem = path.split("/")
    l = len(pathelem)
    if l == 1:
        rootparent.remove(elem)
    else:
        parentpath = "/".join(pathelem[0:l - 1])
        parent = _lookup_element(rootparent, parentpath)
        if "$" in pathelem[l - 1]:
            name_index = pathelem[l - 1].split("$")
            index = int(name_index[1])
            if index < len(parent):
                del parent[index:]
        else:
            parent.remove(elem)
        if len(parent) == 0 and len(parent.attrib) == 0:
            _remove_element(rootparent, parentpath)


def _get_file_hash(filename):
    if xbmcvfs.exists(filename):
        with open(filename, 'r+') as f:
            return hashlib.md5(f.read().encode('utf-8')).hexdigest()
    else:
        return ""


def _is_root_cat(cat):
    return cat.attrib.get('root', "false") == "true"


def _decode_value(value, s):
    if s.attrib['type'] == "enum":
        enummap = s.find("enummap[@value='%s']" % value)
        if enummap is None:
            return value
        else:
            return enummap.attrib['key']
    else:
        return value


def _encode_value(value, s):
    if s.attrib['type'] == "enum":
        enummap = s.find("enummap[@key='%s']" % value)
        if enummap is None:
            return value
        else:
            return enummap.attrib['value']
    else:
        return value


def _read_setting_value(setting, s):
    if "#" in s.attrib['id']:
        idc = s.attrib['id'].split("#")
        xbmc.log("Setting attribute %s of setting  %s" % (idc[1], s.attrib['id']))
        if idc[1] in setting.attrib:
            return setting.attrib[idc[1]]
        else:
            return s.attrib.get('default', "")
    else:
        return setting.text


def _write_setting_value(setting, s, value):
    if "#" in s.attrib['id']:
        idc = s.attrib['id'].split("#")
        xbmc.log("Setting attribute %s of setting  %s" % (idc[1], s.attrib['id']))
        setting.set(idc[1], value)
    else:
        setting.text = value


def _set_child_text(element, child_tag, value):
    child = element.find(child_tag)
    if not (child is None):
        child.text = value


def _load_xml_from_file(filename):
    if xbmcvfs.exists(filename):
        tree = ET.parse(filename)
        return tree.getroot()
    else:
        return None


def _save_pretty_xml(element, output_xml):
    xml_string = minidom.parseString(ET.tostring(element)).toprettyxml()
    xml_string = os.linesep.join(
        [s for s in xml_string.splitlines() if s.strip()])
    with open(output_xml, "w") as file_out:
        file_out.write(xml_string)


def _backup_file(fpath):
    if xbmcvfs.exists(fpath):
        fpathbak = "%s.bak" % fpath
        if xbmcvfs.exists(fpathbak):
            xbmcvfs.delete(fpathbak)
        return xbmcvfs.copy(fpath, fpathbak)
    else:
        return False
