# limitations in new settings format
#
# type="enum" + values="$HOURS" not supported
# type="fileenum" not supported
# type="labelenum" + lvalues saves the label id instead of translated label string
# type="sep" and type="lsep" can't use visible or enable conditions
# conditions can not use infobools
# labels need to be string id's. $LOCALIZE[xxx] and $ADDON[my.addon.id xxx] are not supported

import os
import json
import string
import random
import xml.etree.ElementTree as ET
from xml.dom import minidom
import xbmcgui
import xbmcaddon
import xbmcvfs
from defs import *

ADDON = xbmcaddon.Addon()
ADDONID = ADDON.getAddonInfo('id')
ADDONPATH = ADDON.getAddonInfo('path')
ADDONPROFILE = ADDON.getAddonInfo('profile')
ADDONNAME = ADDON.getAddonInfo('name')
LANGUAGE = ADDON.getLocalizedString


def log(txt):
    message = '%s: %s' % (ADDONID, txt)
    xbmc.log(msg=message, level=xbmc.LOGERROR)

class Main:
    def __init__(self):
        self.issues = False
        # get the addon
        path = self.getAddon()
        if not path:
            return
        # get the old settings.xml file
        settingsxml = os.path.join(path, 'resources', 'settings.xml')
        if not settingsxml or not os.path.exists(settingsxml):
            log('Error: no settings.xml file found')
            xbmcgui.Dialog().ok(ADDONNAME, LANGUAGE(30002))
            return
        # update the settings
        newsettings = self.updateSettings(settingsxml)
        # check if we have data to save
        if not newsettings:
            xbmcgui.Dialog().ok(ADDONNAME, LANGUAGE(30002))
            return
        # save the new settings.xml file
        self.saveSettings(newsettings, settingsxml)
        if self.issues:
            xbmcgui.Dialog().ok(ADDONNAME, LANGUAGE(30003))
        else:
            xbmcgui.Dialog().ok(ADDONNAME, LANGUAGE(30001))

    def getAddon(self):
        # get all installed addons
        json_query = xbmc.executeJSONRPC('{"jsonrpc":"2.0", "method":"Addons.GetAddons", "params":{"properties":["enabled"]}, "id":1}')
        json_response = json.loads(json_query)
        addons = []
        for item in json_response['result']['addons']:
             if item['enabled']:
                 addons.append(item['addonid'])
        addonlist = sorted(addons)
        # select an addon
        dialog = xbmcgui.Dialog()
        addon = dialog.select(LANGUAGE(30000), addonlist)
        if addon != -1:
            self.id = xbmcaddon.Addon(addonlist[addon]).getAddonInfo('id')
            self.path = xbmcaddon.Addon(addonlist[addon]).getAddonInfo('path')
            return self.path

    def updateSettings(self, settingsxml):
        # parse the old settings.xml file
        data = ET.parse(settingsxml).getroot()
        # test if this is an old style settings file
        if 'version' in data.attrib:
            log('Warning: your settings have already been converted')
            self.issues = True
            return
        # create the root settings node
        newroot = ET.Element('settings')
        newroot.set('version', '1')
        # create a section subnode in the settings node
        newsection = ET.SubElement(newroot, 'section')
        newsection.set('id', self.id)
        # parse the old settings.xml file
        data = ET.parse(settingsxml).getroot()
        valid = data.find('category')
        if not valid:
            log('Error: your old settings file is not valid, no category section found')
            self.issues = True
            return
        # iter over all categories
        for cat in data.iter('category'):
            newgroupid = 1
            # create a category subnode in the section node
            if 'label' in cat.attrib:
                catlabel = cat.get('label')
                if catlabel.startswith('$LOCALIZE['):
                    catlabel = catlabel.lstrip('$LOCALIZE[').rstrip(']')
                elif catlabel.startswith('$ADDON['):
                    catlabel = catlabel.split(' ')[1].rstrip(']')
            else:
                catlabel = '571' # Default
            newcategory = ET.SubElement(newsection, 'category')
            catid = xbmcaddon.Addon(self.id).getLocalizedString(int(catlabel))
            if not catid:
                catid = xbmc.getLocalizedString(int(catlabel))
            # an invalid string id was used in the original settings.xml file, this should usually not happen
            if not catid:
                catid = 'Default'
            newcategory.set('id', catid.lower())
            newcategory.set('label', catlabel)
            newcategory.set('help', '')
            # check if the first setting is either sep or lsep
            firstattrib = cat.find('setting').attrib
            if firstattrib['type'] != 'sep' and firstattrib['type'] != 'lsep':
                # create the first group subnode in this category node
                newgroup = ET.SubElement(newcategory, 'group')
                newgroup.set('id', str(newgroupid))
                newgroupid += 1
            catsettings = []
            for setting in cat.iter('setting'):
                catsettings.append(setting)
            count = 0
            # iter over all settings in this category
            for setting in cat.iter('setting'):
                attributes = setting.attrib
                # check if the setting has a type
                if not 'type' in attributes:
                    attributes['type'] = ''
                # check if the type is valid
                if attributes['type'] not in TYPES:
                    log('Error: setting %s has an invalid type: %s' % (attributes, attributes['type']))
                    count += 1
                    continue
                # check if all attributes are valid
                for attrib, val in attributes.items():
                    if attrib not in ATTRIBS:
                        log('Warning: setting %s has an invalid attribute: %s' % (attributes, attrib))
                # special setting, start a new group subnode in this category node
                if attributes['type'] == 'sep':
                    newgroup = ET.SubElement(newcategory, 'group')
                    newgroup.set('id', str(newgroupid))
                    newgroupid += 1
                # special setting, start a new group subnode with a label in this category node
                elif attributes['type'] == 'lsep':
                    newgroup = ET.SubElement(newcategory, 'group')
                    newgroup.set('id', str(newgroupid))
                    grouplabel = attributes['label']
                    if grouplabel.startswith('$LOCALIZE['):
                        grouplabel = grouplabel.lstrip('$LOCALIZE[').rstrip(']')
                    elif grouplabel.startswith('$ADDON['):
                        grouplabel = grouplabel.split(' ')[1].rstrip(']')
                    newgroup.set('label', grouplabel)
                    newgroupid += 1
                # create a setting subnode in this group node
                else:
                    newgroup = self.parseSetting(newgroup, setting, attributes, catsettings, count)
                count += 1
        return newroot

    def parseSetting(self, newgroup, setting, attributes, catsettings, count):
        # create a setting subnode in this group node
        newsetting = ET.SubElement(newgroup, 'setting')
        # add attribute id
        if 'id' in attributes:
            newsetting.set('id', attributes['id'])
        else:
            randomstring = ''.join(random.SystemRandom().choice(string.ascii_lowercase) for _ in range(32))
            newsetting.set('id', randomstring)
            log('Warning: random "id" generated for setting %s' % attributes)
            self.issues = True
        # add attribute type
        if attributes['type'] == 'bool':
            newsetting.set('type', 'boolean')
        elif attributes['type'] == 'text':
             newsetting.set('type', 'string')
        elif attributes['type'] == 'number':
            newsetting.set('type', 'integer')
        elif attributes['type'] == 'ipaddress':
            newsetting.set('type', 'string')
        elif attributes['type'] == 'date':
            newsetting.set('type', 'date')
        elif attributes['type'] == 'time':
            newsetting.set('type', 'time')
        elif attributes['type'] == 'slider':
            if 'option' in attributes and attributes['option'] == 'float':
                newsetting.set('type', 'number')
            else:
                newsetting.set('type', 'integer')
        elif attributes['type'] == 'action':
            newsetting.set('type', 'action')
        elif attributes['type'] == 'addon':
            if 'multiselect' in attributes and attributes['multiselect'] == 'true':
                newsetting.set('type', 'list[addon]')
            else:
                newsetting.set('type', 'addon')
        elif attributes['type'] in ('file', 'audio', 'video', 'image', 'executable', 'folder'):
            newsetting.set('type', 'path')
        # fileenum settings are not supported
        elif attributes['type'] == 'fileenum':
            log('Error: fileenum settings are not supported, setting %s has been converted to a file browser' % attributes)
            self.issues = True
            # convert to a file or folder browser
            newsetting.set('type', 'path')
        elif attributes['type'] == 'rangeofnum':
            newsetting.set('type', 'number')
        elif attributes['type'] == 'select':
            if 'lvalues' in attributes:
                newsetting.set('type', 'integer')
            else:
                newsetting.set('type', 'string')
        elif attributes['type'] == 'enum':
            newsetting.set('type', 'integer')
        elif attributes['type'] == 'labelenum':
            newsetting.set('type', 'string')
        elif attributes['type'] == '':
            newsetting.set('type', 'string')
        # add attribute label
        if 'label' in attributes:
            settinglabel = attributes['label']
            if settinglabel.startswith('$LOCALIZE['):
                settinglabel = settinglabel.lstrip('$LOCALIZE[').rstrip(']')
            elif settinglabel.startswith('$ADDON['):
                settinglabel = settinglabel.split(' ')[1].rstrip(']')
            newsetting.set('label', settinglabel)
        # add attribute help
        newsetting.set('help', '')
        # add attribute parent
        if 'subsetting' in attributes and attributes['subsetting'] == 'true':
            i = count - 1
            # some settings do not have an id
            settingid = ''
            while i >= 0 :
                attribs = catsettings[i].attrib
                if not 'subsetting' in attribs:
                    if 'id' in attribs:
                        settingid = catsettings[i].attrib['id']
                    break
                i -= 1
            newsetting.set('parent', settingid)
        # add subnodes
        newsetting = self.addNodes(newsetting, attributes, catsettings, count)
        # add additional constraints and the control node
        newsetting = self.settingConvert(newsetting, attributes)
        return newgroup

    def addNodes(self, newsetting, attributes, catsettings, count):
        # create a level subnode in this setting node
        newlevel = ET.SubElement(newsetting, 'level')
        # set level to basic
        newlevel.text = '0'
        # create a default subnode in this setting node, except for action type settings without a value
        if (attributes['type'] != 'action') or (attributes['type'] == 'action' and 'default' in attributes):
            newdefault = ET.SubElement(newsetting, 'default')
            # add default value
            if 'default' in attributes and attributes['default'] != '':
                newdefault.text = attributes['default']
            # settings of type integer must have a default value
            elif attributes['type'] == 'number' or attributes['type'] == 'enum' or (attributes['type'] == 'select' and 'lvalues' in attributes):
                newdefault.text = '0'
                # set the default attribute to avoid the creation of an allowempty node
                attributes['default'] = 0
            # fileenum settings use the 'values' attribute
            elif attributes['type'] == 'fileenum':
                path = self.path
                if 'values' in attributes:
                    path = path + attributes['values']
                newdefault.text = path
                # set the default attribute to avoid the creation of an allowempty node
                if not 'default' in attributes or attributes['default'] == '':
                    attributes['default'] = path
        # add data subnode for actions
        elif attributes['type'] == 'action' and not 'default' in attributes:
            newdefault = ET.SubElement(newsetting, 'data')
            action = ''
            if 'action' in attributes:
                action = attributes['action'].replace('$CWD', self.path).replace('$ID', self.id)
            newdefault.text = action
        # add constraints subnode
        newsetting = self.addConstraints(newsetting, attributes)
        # add dependency subnode
        newsetting = self.addDependency(newsetting, attributes, catsettings, count)
        # settings without type are hidden
        if attributes['type'] == '':
            newvisible = ET.SubElement(newsetting, 'visible')
            newvisible.text = 'false'
        return newsetting

    def addConstraints(self, newsetting, attributes):
        if attributes['type'] == 'slider':
            if 'range' in attributes:
                # create a constraints subnode in this setting node
                newconstraints = ET.SubElement(newsetting, 'constraints')
                values = attributes['range'].split(',')
                if len(values) == 2:
                    # create a minimum subnode in this constraints node
                    newminimum = ET.SubElement(newconstraints, 'minimum')
                    # create a maximum subnode in this constraints node
                    newmaximum = ET.SubElement(newconstraints, 'maximum')
                    newminimum.text = str(values[0])
                    newmaximum.text = str(values[1])
                elif len(values) == 3:
                    # create a minimum subnode in this constraints node
                    newminimum = ET.SubElement(newconstraints, 'minimum')
                    # create a step subnode in this constraints node
                    newstep = ET.SubElement(newconstraints, 'step')
                    # create a maximum subnode in this constraints node
                    newmaximum = ET.SubElement(newconstraints, 'maximum')
                    newminimum.text = str(values[0])
                    newstep.text = str(values[1])
                    newmaximum.text = str(values[2])
        elif attributes['type'] == 'addon':
            # parse addontype attribute
            if 'addontype' in attributes:
                # create a constraints subnode in this setting node
                newconstraints = ET.SubElement(newsetting, 'constraints')
                # create a addontype subnode in this constraints node
                newaddontype = ET.SubElement(newconstraints, 'addontype')
                newaddontype.text = attributes['addontype']
        elif attributes['type'] in ('fileenum', 'file', 'audio', 'video', 'image', 'executable', 'folder'):
            if 'source' in attributes:
                # create a constraints subnode in this setting node
                newconstraints = ET.SubElement(newsetting, 'constraints')
                # create a sources subnode in this constraints node
                newsources = ET.SubElement(newconstraints, 'sources')
                # create a source subnode in this sources node
                newsource = ET.SubElement(newsources, 'source')
                newsource.text = attributes['source']
            # writeable is enabled by default in newstyle, it was disabled by default in oldstyle
            if 'option' in attributes and attributes['option'] == 'writeable':
                pass
            else:
                if newsetting.find('constraints') == None:
                    # create a constraints subnode in this setting node
                    newconstraints = ET.SubElement(newsetting, 'constraints')
                else:
                    newconstraints = newsetting.find('constraints')
                # create a writeable subnode in this constraints node
                newwriteable = ET.SubElement(newconstraints, 'writable')
                newwriteable.text = 'false'
            if 'mask' in attributes or attributes['type'] in ('audio', 'video', 'image', 'executable'):
                if newsetting.find('constraints') == None:
                    # create a constraints subnode in this setting node
                    newconstraints = ET.SubElement(newsetting, 'constraints')
                else:
                    newconstraints = newsetting.find('constraints')
                # create a masking subnode in this constraints node
                newmasking = ET.SubElement(newconstraints, 'masking')
                # set masking value
                if 'mask' in attributes:
                    mask = attributes['mask'].replace('$AUDIO', 'audio').replace('$VIDEO', 'video').replace('$IMAGE', 'image').replace('$EXECUTABLE', 'executable')
                elif attributes['type'] == 'audio':
                    mask = 'audio'
                elif attributes['type'] == 'video':
                    mask = 'video'
                elif attributes['type'] == 'image':
                    mask = 'image'
                elif attributes['type'] == 'executable':
                    mask = 'executable'
                newmasking.text = mask
        elif attributes['type'] == 'rangeofnum':
            # create a constraints subnode in this setting node
            newconstraints = ET.SubElement(newsetting, 'constraints')
            # create a minimum subnode in this constraints node
            newminimum = ET.SubElement(newconstraints, 'minimum')
            newminimum.text = attributes['rangestart']
            # create a maximum subnode in this constraints node
            newmaximum = ET.SubElement(newconstraints, 'maximum')
            newmaximum.text = attributes['rangeend']
            # create a step subnode in this constraints node
            newstep = ET.SubElement(newconstraints, 'step')
            if 'elements' in attributes:
                newstep.text = str(int((attributes['rangeend'] - attributes['rangestart']) / (attributes['elements'] - 1)))
            else:
                newstep.text = '2'
        elif attributes['type'] == 'select':
            # create a constraints subnode in this setting node
            newconstraints = ET.SubElement(newsetting, 'constraints')
            # create a options subnode in this constraints node
            newoptions = ET.SubElement(newconstraints, 'options')
            if 'lvalues' in attributes:
                values = attributes['lvalues'].split('|')
                count = 0
                for value in values:
                    # create a option subnode in this options node
                    newoption = ET.SubElement(newoptions, 'option')
                    # add attribute label
                    newoption.set('label', value)
                    newoption.text = str(count)
                    count += 1
            elif 'values' in attributes:
                values = attributes['values'].split('|')
                count = 0
                for value in values:
                    # create a option subnode in this options node
                    newoption = ET.SubElement(newoptions, 'option')
                    newoption.text = value
                    count += 1
        elif attributes['type'] == 'enum':
            # create a constraints subnode in this setting node
            newconstraints = ET.SubElement(newsetting, 'constraints')
            # create a options subnode in this constraints node
            newoptions = ET.SubElement(newconstraints, 'options')
            if 'lvalues' in attributes:
                values = attributes['lvalues'].split('|')
                count = 0
                for value in values:
                    # create a option subnode in this options node
                    newoption = ET.SubElement(newoptions, 'option')
                    # add attribute label
                    newoption.set('label', value)
                    newoption.text = str(count)
                    count += 1
            elif 'values' in attributes and attributes['values'] == '$HOURS':
                log('Error: values="$HOURS" is not implemented in Kodi for setting %s' % attributes)
                self.issues = True
            elif 'values' in attributes:
                values = attributes['values'].split('|')
                count = 0
                for value in values:
                    # create a option subnode in this options node
                    newoption = ET.SubElement(newoptions, 'option')
                    # add attribute label
                    newoption.set('label', value)
                    newoption.text = str(count)
                    count += 1
        elif attributes['type'] == 'labelenum':
            # create a constraints subnode in this setting node
            newconstraints = ET.SubElement(newsetting, 'constraints')
            # create a options subnode in this constraints node
            newoptions = ET.SubElement(newconstraints, 'options')
            # parse the entries attribute
            if 'sort' in attributes and attributes['sort'] == 'yes':
                newoptions.set('sort', 'ascending')
            # $HOURS is not supported
            if 'values' in attributes and attributes['values'] == '$HOURS':
                log('Error: values="$HOURS" is not implemented in Kodi for setting %s' % attributes)
                self.issues = True
            # parse the entries attribute
            elif 'values' in attributes:
                values = attributes['values'].split('|')
                for value in values:
                    # create a option subnode in this options node
                    newoption = ET.SubElement(newoptions, 'option')
                    # add attribute label
                    newoption.set('label', value)
                    newoption.text = value
            elif 'lvalues' in attributes:
                log('Warning: the selected value of %s is saved as the label id instead of translated label now. to fix: use xbmc.getLocalizedString(id) in your addon code' % attributes)
                self.issues = True
                values = attributes['lvalues'].split('|')
                for value in values:
                    # create a option subnode in this options node
                    newoption = ET.SubElement(newoptions, 'option')
                    # add attribute label
                    newoption.set('label', value)
                    newoption.text = value
        # if there's no defalt value we need to set allowempty
        if not 'default' in attributes or attributes['default'] == '':
            if newsetting.find('constraints') == None:
                # create a constraints subnode in this setting node
                newconstraints = ET.SubElement(newsetting, 'constraints')
            else:
                newconstraints = newsetting.find('constraints')
            # create a allowempty subnode in this constraints node
            newallowempty = ET.SubElement(newconstraints, 'allowempty')
            newallowempty.text = 'true'
        return newsetting

    def addDependency(self, newsetting, attributes, catsettings, count):
        if 'enable' in attributes or 'visible' in attributes:
            newdependencies = ET.SubElement(newsetting, 'dependencies')
            # create a dependency subnode in this dependencies node
            if 'enable' in attributes:
                newdependency = ET.SubElement(newdependencies, 'dependency')
                # add type attribute
                newdependency.set('type', 'enable')
                newdependency = self.parseCondition(newdependency, attributes['enable'], catsettings, count)
            # create a dependency subnode in this dependencies node
            if 'visible' in attributes:
                newdependency = ET.SubElement(newdependencies, 'dependency')
                # add type attribute
                newdependency.set('type', 'visible')
                newdependency = self.parseCondition(newdependency, attributes['visible'], catsettings, count)
        return newsetting

    def parseCondition(self, newdependency, condition, catsettings, count):
        hasand = False
        hasor = False
        # check if we have multiple conditions
        if '+' in condition:
            values = condition.split('+')
            hasand = True
        elif '|' in condition:
            values = condition.split('|')
            hasor = True
        else:
            values = []
            values.append(condition)
        # add and subnode in dependency node
        if hasand:
            newdependency = ET.SubElement(newdependency, 'and')
        # add or subnode in dependency node
        elif hasor:
            newdependency = ET.SubElement(newdependency, 'or')
        for item in values:
            if item.startswith('eq(') or item.startswith('!eq(') or item.startswith('lt(') or item.startswith('!lt(') or item.startswith('gt(') or item.startswith('!gt('):
                # add condition subnode in dependency node
                newcondition = ET.SubElement(newdependency, 'condition')
                # set operator attribute
                if item.startswith('eq'):
                    operator = 'is'
                if item.startswith('!eq'):
                    operator = '!is'
                if item.startswith('lt'):
                    operator = 'lt'
                if item.startswith('!lt'):
                    operator = '!lt'
                if item.startswith('gt'):
                    operator = 'gt'
                if item.startswith('!gt'):
                    operator = '!gt'
                newcondition.set('operator', operator)
                # split the condition values
                temp = item.split('(')[1]
                data = temp.split(')')[0]
                parts = data.split(',')
                # get setting id
                attributes = catsettings[count + int(parts[0])].attrib
                if 'id' in attributes:
                    setting = catsettings[count + int(parts[0])].attrib['id']
                else:
                    # some settings do not have an id
                    setting = ''
                # set setting attribute
                newcondition.set('setting', setting)
                # set condition value
                newcondition.text = parts[1]
            else:
                # add condition subnode in dependency node
                newcondition = ET.SubElement(newdependency, 'condition')
                # set on property
                newcondition.set('on', 'property')
                # set name property
                newcondition.set('name', 'InfoBool')
                # set value
                newcondition.text = item
        return newdependency

    def settingConvert(self, newsetting, attributes):
        if attributes['type'] == 'bool':
            newsetting = self.convertBool(newsetting, attributes)
        elif attributes['type'] == 'text':
            newsetting = self.convertText(newsetting, attributes)
        elif attributes['type'] == 'number':
            newsetting = self.convertNumber(newsetting, attributes)
        elif attributes['type'] == 'ipaddress':
            newsetting = self.convertIpaddress(newsetting, attributes)
        elif attributes['type'] == 'date':
            newsetting = self.convertDate(newsetting, attributes)
        elif attributes['type'] == 'time':
            newsetting = self.convertTime(newsetting, attributes)
        elif attributes['type'] == 'slider':
            newsetting = self.convertSlider(newsetting, attributes)
        elif attributes['type'] == 'action':
            newsetting = self.convertAction(newsetting, attributes)
        elif attributes['type'] == 'addon':
            newsetting = self.convertAddon(newsetting, attributes)
        elif attributes['type'] in ('file', 'audio', 'video', 'image', 'executable', 'folder', 'fileenum'):
            newsetting = self.convertFile(newsetting, attributes)
        elif attributes['type'] == 'rangeofnum':
            newsetting = self.convertRangeofnum(newsetting, attributes)
        elif attributes['type'] == 'select':
            newsetting = self.convertSelect(newsetting, attributes)
        elif attributes['type'] == 'enum':
            newsetting = self.convertEnum(newsetting, attributes)
        elif attributes['type'] == 'labelenum':
            newsetting = self.convertLabelenum(newsetting, attributes)
        elif attributes['type'] == '':
            newsetting = self.convertHidden(newsetting, attributes)
        return newsetting

    def convertBool(self, newsetting, attributes):
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # add attribute type
        newcontrol.set('type', 'toggle')
        return newsetting

    def convertText(self, newsetting, attributes):
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # add attribute type
        newcontrol.set('type', 'edit')
        # add attribute format
        newcontrol.set('format', 'string')
        # create a heading subnode in this control node
        newheading = ET.SubElement(newcontrol, 'heading')
        if 'label' in attributes:
            newheading.text = attributes['label']
        if 'option' in attributes:
            if attributes['option'] == 'hidden':
                # add a hidden subnode to the control node
                newhidden = ET.SubElement(newcontrol, 'hidden')
                newhidden.text = 'true'
            elif attributes['option'] == 'urlencoded':
                # change setting attribute type
                newsetting.set('type', 'urlencodedstring')
                # change control attribute format
                newcontrol.set('format', 'urlencoded')
        return newsetting

    def convertNumber(self, newsetting, attributes):
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # add attribute type
        newcontrol.set('type', 'edit')
        # add attribute format
        newcontrol.set('format', 'integer')
        # create a heading subnode in this control node
        newheading = ET.SubElement(newcontrol, 'heading')
        if 'label' in attributes:
            newheading.text = attributes['label']
        return newsetting

    def convertIpaddress(self, newsetting, attributes):
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # add attribute type
        newcontrol.set('type', 'edit')
        # add attribute format
        newcontrol.set('format', 'ip')
        # create a heading subnode in this control node
        newheading = ET.SubElement(newcontrol, 'heading')
        if 'label' in attributes:
            newheading.text = attributes['label']
        return newsetting

    def convertDate(self, newsetting, attributes):
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # add attribute type
        newcontrol.set('type', 'button')
        # add attribute format
        newcontrol.set('format', 'date')
        # create a heading subnode in this control node
        newheading = ET.SubElement(newcontrol, 'heading')
        if 'label' in attributes:
            newheading.text = attributes['label']
        return newsetting

    def convertTime(self, newsetting, attributes):
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # add attribute type
        newcontrol.set('type', 'button')
        # add attribute format
        newcontrol.set('format', 'time')
        # create a heading subnode in this control node
        newheading = ET.SubElement(newcontrol, 'heading')
        if 'label' in attributes:
            newheading.text = attributes['label']
        return newsetting

    def convertSlider(self, newsetting, attributes):
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # add attribute type
        newcontrol.set('type', 'slider')
        # add attribute format
        if 'option' in attributes:
            if attributes['option'] == 'float':
                newcontrol.set('format', 'number')
            elif attributes['option'] == 'int':
                newcontrol.set('format', 'integer')
            elif attributes['option'] == 'percent':
                newcontrol.set('format', 'percentage')
        else:
            newcontrol.set('format', 'integer')
        # create a popup subnode in this control node
        newpopup = ET.SubElement(newcontrol, 'popup')
        newpopup.text = 'false'
        return newsetting

    def convertAction(self, newsetting, attributes):
        # parse action attribute
        action = ''
        if 'action' in attributes:
            action = attributes['action'].replace('$CWD', self.path).replace('$ID', self.id)
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # add attribute type
        newcontrol.set('type', 'button')
        # check if we need to display a value
        if 'default' in attributes:
            # add action to button control
            newdefault = ET.SubElement(newcontrol, 'data')
            newdefault.text = action
            # add attribute format
            newcontrol.set('format', 'string')
        else:
            # add attribute format
            newcontrol.set('format', 'action')
        # parse option attribute
        if 'option' in attributes:
            if attributes['option'] == 'close':
                newclose = ET.SubElement(newcontrol, 'close')
                newclose.text = 'true'
        return newsetting

    def convertAddon(self, newsetting, attributes):
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # add attribute type
        newcontrol.set('type', 'button')
        # add attribute format
        newcontrol.set('format', 'addon')
        # create a heading subnode in this control node
        newheading = ET.SubElement(newcontrol, 'heading')
        if 'label' in attributes:
            newheading.text = attributes['label']
        # create a show subnode in this control node
        newshow = ET.SubElement(newcontrol, 'show')
        # add attribute type
        newshow.set('more', 'true')
        # add attribute format
        newshow.set('details', 'true')
        newshow.text = 'installed'
        # create a multiselect subnode in this control node
        if 'multiselect' in attributes and attributes['multiselect'] == 'true':
            newmultiselect = ET.SubElement(newcontrol, 'multiselect')
            newmultiselect.text = 'true'
        return newsetting

    def convertFile(self, newsetting, attributes):
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # convert fileenum to file or folder browser
        if attributes['type'] == 'fileenum':
            if 'mask' in attributes and attributes['mask'] == '/':
                attributes['type'] = 'folder'
                del attributes['mask']
            else:
                attributes['type'] = 'file'
        # add attribute type
        newcontrol.set('type', 'button')
        # add attribute format
        if attributes['type'] == 'folder':
            newcontrol.set('format', 'path')
        elif attributes['type'] == 'image':
            newcontrol.set('format', 'image')
        else:
            newcontrol.set('format', 'file')
        # create a heading subnode in this control node
        newheading = ET.SubElement(newcontrol, 'heading')
        if 'label' in attributes:
            newheading.text = attributes['label']
        return newsetting

    def convertRangeofnum(self, newsetting, attributes):
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # add attribute type
        newcontrol.set('type', 'spinner')
        # add attribute format
        newcontrol.set('format', 'string')
        # create a formatlabel subnode in this control node
        newformatlabel = ET.SubElement(newcontrol, 'formatlabel')
        if 'valueformat' in attributes:
            newformatlabel.text = attributes['valueformat']
        else:
            newformatlabel.text = '-1'
        return newsetting

    def convertSelect(self, newsetting, attributes):
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # add attribute type
        newcontrol.set('type', 'list')
        # add attribute format
        newcontrol.set('format', 'string')
        # create a heading subnode in this control node
        newheading = ET.SubElement(newcontrol, 'heading')
        if 'label' in attributes:
            newheading.text = attributes['label']
        return newsetting

    def convertEnum(self, newsetting, attributes):
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # add attribute type
        newcontrol.set('type', 'spinner')
        # add attribute format
        newcontrol.set('format', 'string')
        return newsetting

    def convertLabelenum(self, newsetting, attributes):
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # add attribute type
        newcontrol.set('type', 'spinner')
        # add attribute format
        newcontrol.set('format', 'string')
        return newsetting

    def convertHidden(self, newsetting, attributes):
        # create a control subnode in this setting node
        newcontrol = ET.SubElement(newsetting, 'control')
        # add attribute type
        newcontrol.set('type', 'edit')
        # add attribute format
        newcontrol.set('format', 'string')
        # create a heading subnode in this control node
        newheading = ET.SubElement(newcontrol, 'heading')
        if 'label' in attributes:
            newheading.text = attributes['label']
        return newsetting

    def saveSettings(self, newsettings, settingsxml):
        # prettify xml structure
        root = ET.tostring(newsettings)
        data = minidom.parseString(root)
        tree = data.toprettyxml(indent='\t')
        xml = tree.replace('\t\n', '').replace('\n\n', '\n')
        # write to file
        f = xbmcvfs.File(settingsxml, 'w')
        result = f.write(xml)
        f.close()


if (__name__ == '__main__'):
    Main()
