#!/usr/bin/python
import sys
import xml.dom.minidom
import re

# table_type is a synonym to storage_engine
OPTIONS_TO_IGNORE = ["default-table-type", "new"]

sdoc = xml.dom.minidom.parse("structure.xml")

tabs = {}
tabs_list = []
variables = {}

#===============================================================================
# Create structure first
def parse_configuration(sec):
    for node in sec.childNodes:
        if xml.dom.Node.ELEMENT_NODE == node.nodeType:
            if node.tagName == "tab":
                tab = (node.getAttribute("caption"), node.getAttribute("description"), [], {})
                groups = tab[2]
                groups_map = tab[3]
                tabs[node.getAttribute("caption")] = tab
                tabs_list.append(tab)
                for group in node.childNodes:
                    if xml.dom.Node.ELEMENT_NODE == group.nodeType:
                        if group.tagName == "group":
                            grp = []
                            groups.append((group.getAttribute('caption'), grp))
                            groups_map[group.getAttribute('caption')] = grp

for sec in sdoc.documentElement.getElementsByTagName('section'):
    if sec.getAttribute("class") == "mycnf":
        parse_configuration(sec)

misc_grp = []
misc_tab = ('Misc', 'Uncategorized', [('Misc', misc_grp)], {'Misc':misc_grp})
tabs['Misc'] = misc_tab
tabs_list.append(misc_tab)

#-------------------------------------------------------------------------------
def get_group(collection, group):
    coll = None
    grp = None
    try:
        coll = tabs[collection]
        grp = coll[3][group]
    except:
        print collection, group
        raise
    return grp

#===============================================================================
# read controls/options
doc = xml.dom.minidom.parse('mysqld_new.xml')

#-------------------------------------------------------------------------------
def parse_ui_section(name, sec):
    uis = {}

    if sec.getAttribute('class') == "mycnf":
        uis['class'] = "mycnf"
        uis['collection'] = sec.getAttribute('collection')
        uis['group'] = sec.getAttribute('group')
        for node in sec.childNodes:
            if xml.dom.Node.ELEMENT_NODE == node.nodeType:
                if node.tagName == "caption":
                    uis['caption'] = node.getAttribute('value')
                elif node.tagName == "description":
                    uis['description'] = node.getAttribute('value')
                elif node.tagName == "type":
                    utype = node.getAttribute('value')
                    unitcontrol = node.getAttribute('unitcontrol')
                    if unitcontrol != "":
                        uis['unitcontrol'] = unitcontrol
                    udefault = node.getAttribute('default')
                    uis['default'] = udefault
                    uis['type'] = utype

                    for n in node.getElementsByTagName('value'):
                        if n.nodeType == xml.dom.Node.ELEMENT_NODE:
                            for i in range(len(n.attributes)):
                                attr = n.attributes.item(i)
                                uis[attr.name] = attr.value

                    if utype == 'dropdownbox':
                        style = node.getAttribute('style')
                        if style == 'edit':
                            uis['type'] = 'dropdownboxentry'
                        choices = []
                        pysrc = node.getElementsByTagName('pysource')
                        if len(pysrc) > 0:
                            srcid = pysrc[0].getAttribute('sourceid')
                            uis['choices'] = srcid
                        else:
                            for n in node.childNodes:
                                if xml.dom.Node.ELEMENT_NODE == n.nodeType:
                                    if n.tagName == 'choice':
                                        choices.append((n.getAttribute('caption'), n.getAttribute('value')))
                            uis['choices'] = choices
                    #else:
                    #  sys.stderr.write("Fix script: unhandled overriden type " + utype + "\n")
                    #  uis = None

    return uis

#-------------------------------------------------------------------------------
def parse_ui(name, ui_node):
    ui = {}
    for node in ui_node.childNodes:
        if xml.dom.Node.ELEMENT_NODE == node.nodeType:
            if node.tagName == "section":
                uis = parse_ui_section(name, node)
                ui[uis['class']] = uis

    return ui

#-------------------------------------------------------------------------------
def add_variable(var, oclass, section, scope, versions, description):
    #if scope == 'session':
    #  return

    # Prepare description
    description = description.replace('\n', '')
    while True:
        new_description = description.replace('  ', ' ')
        if new_description == description:
            break
        description = new_description

    description = description.replace('\'', '\\\'')
    res = re.search('<shortdescription>(.+)</shortdescription>', description)
    if res is not None:
        description = res.groups()[0].strip(' ')

    # Get class
    if oclass not in variables:
        variables[oclass] = {}
    cls = variables[oclass]

    if section not in cls:
        cls[section] = []

    sec = cls[section]

    values = vars()
    names  = ('var', 'versions', 'description')
    lines = []
    for name in names:
        value = values[name]
        if value is not None:
            if type(value) is not tuple:
                lines.append(name + "='" + str(value) + "'")
            else:
                lines.append(name + "=" + str(value))
        else:
            lines.append(name + "=None")
    sec.append("Option(" + ' ,'.join(lines) + ")")

#-------------------------------------------------------------------------------
def parse_versions(versions_node):
    vers = []
    for node in versions_node.childNodes:
        if xml.dom.Node.ELEMENT_NODE == node.nodeType:
            if node.tagName == "manual" or node.tagName == 'introduced':
                version_str = node.getAttribute('version')
                version = None
                try:
                    tokens = re.match("([0-9]+)\.([0-9]+)\.([0-9]+)|([0-9]+)\.([0-9]+)", version_str).groups()
                    if tokens[0] is not None:
                        version = (int(tokens[0]), int(tokens[1]), int(tokens[2]))
                    else:
                        version = (int(tokens[3]), int(tokens[4]))
                    vers.append(version)
                except ValueError, e:
                    print "ERROR! incorrect version attribute value '" + version_str + "', ", type(version_str)

    vers.sort()
    vers.append((0,0))
    res = []
    skip = False
    for i in range(0, len(vers)-1):

        if vers[i][0:2] == vers[i+1][0:2]:
            res.append(max(vers[i], vers[i+1]))
            skip = True
        else:
            if not skip:
                res.append(vers[i])
            else:
                skip = False

    return tuple(res)


#-------------------------------------------------------------------------------
def parse_opt_values(name, node):
    opt={}

    for i in range(len(node.attributes)):
        attr = node.attributes.item(i)
        aname = attr.name
        if aname == 'vartype':
            aname = 'type'
        avalue = attr.value
        if avalue == 'enumeration':
            avalue = 'dropdownbox'
        opt[aname] = avalue


    optype = None
    if 'type' in opt:
        optype = opt['type']

    if optype == 'numeric' or optype == 'string' or optype == 'boolean' or optype == 'filename':
        for n in node.childNodes:
            if xml.dom.Node.ELEMENT_NODE == n.nodeType:
                for i in range(len(n.attributes)):
                    attr = n.attributes.item(i)
                    opt[attr.name] = attr.value
    elif optype == 'dropdownbox':
        choices = []
        for n in node.childNodes:
            if xml.dom.Node.ELEMENT_NODE == n.nodeType:
                for i in range(len(n.attributes)):
                    attr = n.attributes.item(i)
                    if attr.name == 'value':
                        choices.append((attr.value, attr.value))
                    elif attr.name == 'default':
                        opt['default'] = attr.value
        opt['choices'] = choices
    else:
        sys.stderr.write("Unhandled " + optype + " in " + name + "\n")
        opt = None

    return opt


#-------------------------------------------------------------------------------
for option in doc.documentElement.getElementsByTagName('mysqloption'):
    name = ""
    optype = None
    ui = None
    versions = None
    variable_name = None
    varcls = None
    varscope = None
    shortdesc = ""

    optid = option.getAttribute('id')
    optsection = option.getAttribute('section')

    for node in option.getElementsByTagName('shortdescription'):
        shortdesc += node.firstChild.data.encode('ascii')

    is_opt = False
    is_var = False
    for node in option.getElementsByTagName('optype'):
        if node.getAttribute('class') == 'mycnf':
            name = node.getAttribute('format') # Try to pick name from optype tag's attribute
            split_name = name.split("=")
            if len(split_name) > 0:
                name = split_name[0].strip(" \r\t\n")
            is_opt = True

    for node in option.getElementsByTagName('vartype'):
        varcls = node.getAttribute('class')
        varscope = node.getAttribute('scope')
        nm = node.getAttribute('format') # Try to pick name from optype tag's attribute
        nm = nm.strip(' \r\n\t')
        if nm != '' or nm is not None:
            variable_name = nm
        is_var = True

    if name == "":
        name = optid

    if variable_name is None or variable_name == '':
        variable_name = optid

    if name in OPTIONS_TO_IGNORE:
        continue

    for node in option.childNodes:
        if xml.dom.Node.ELEMENT_NODE == node.nodeType:
            if node.tagName == "name":
                nm = node.firstChild.data.encode('ascii')
                if name == "":
                    name = nm
                if variable_name == "":
                    variable_name = nm
            elif node.tagName == "values":
                if optype is None:
                    optype = parse_opt_values(name, node)
            elif node.tagName == "versions":
                versions = parse_versions(node)
            elif node.tagName == "ui":
                ui = parse_ui(name, node)

    if is_var:
        add_variable(variable_name, varcls, optsection, varscope, versions, shortdesc)

#  if is_opt and optype is None and ui is None:
#    print name
    if is_opt:
        if optype is None:
            optype = {}

        if 'type' not in optype:
            optype['type'] = 'string'

        optype['name'] = name
        optype['versions'] = versions

        if ui is not None:
            uiopt = ui['mycnf']
            for k,v in uiopt.iteritems():
                if 'collection' != k and 'class' != k and 'group' != k and 'platform' != k:
                    optype[k] = v

            grp = get_group(uiopt['collection'], uiopt['group'])
            grp.append(optype)
        else:
            optype['caption'] = name
            optype['description'] = " "

            grp = get_group('Misc', 'Misc')
            grp.append(optype)


#===============================================================================
ind = "  "
tab_text = []

f = open('opts.py', 'w')

def build_group(name, group, ind):
    grp = "{'caption' : \"" + name + "\",\n"
    grp += ind + "'controls':\n" + ind + "  (\n"
    ctrls = []
    single = len(group) == 1
    for optype in group:
        ctrl = ind + "    {\n"
        lines = []

        for k,v in optype.iteritems():
            if k != 'platform':
                if type(v) is list:
                    if optype['type'] == 'dropdownbox' or optype['type'] == 'dropdownboxentry':
                        line = ind + "      'items' : {"
                        items = []
                        for caption, value in v:
                            items.append("\"" + caption + "\":\"" + value + "\"")
                        line += ", ".join(items) + "}"
                        lines.append(line)
                else:
                    if type(v) is not tuple:
                        lines.append(ind + "      '" + k + "' : \"" + str(v) + "\"")
                    else:
                        lines.append(ind + "      '" + k + "' : " + str(v))

        ctrl += ",\n".join(lines)
        ctrl += "\n"
        ctrl += ind + "    }"
        ctrls.append(ctrl)
    grp += (",\n").join(ctrls)
    grp += "\n"
    if single:
        grp += ind + "   ,\n"
    grp += ind + "  ) #End of controls list in group " + name + "\n"
    grp += ind + "} #End of group " + name + "\n"
    return grp

f.write("opts_list = {\n")

for (pos, (tab_name,tab_desc, groups, groups_map)) in enumerate(tabs_list):
    text = ind + "'" + tab_name + "' : { '"
    indent = " "*len(text)
    text += "description' : \"" + tab_desc + "\",\n"
    text += indent + "  'position' : " + str(pos+1) + ",\n"
    group_text_list = []
    single_group = len(groups) == 1
    for group in groups:
        group_text_list.append(build_group(group[0], group[1], indent + "               "))
    text += indent + "  'groups':(  "
    text += (indent + "             ,").join(group_text_list)
    if single_group:
        text += indent + "             ,"
    text += indent + "           ) # end of groups in tab " + tab_name + "\n"
    text += indent + "} # end of tab " + tab_name +"\n"
    tab_text.append(text)

f.write((ind + ",").join(tab_text) + "\n")
f.write("} #End of opts_list\n")
f.close()

# Write out variables
f = open('wb_admin_variable_list.py', 'w')

def generate_Option_class(fields):
    cls = 'class Option:\n'
    cls += '  def __init__(self, ' + ', '.join(fields) + "):\n"
    for field in fields:
        cls += '    self.' + field + " = " + field + "\n"
    cls += '\n'
    cls += '  def __repr__(self):\n'
    cls += '    s = []\n'
    cls += '    for attr in dir(self):\n'
    cls += '      if attr[0] == \'_\':\n'
    cls += '        continue\n'
    cls += '      else:\n'
    cls += '        s.append("\'" + attr + "\':" + str(getattr(self, attr)))\n'
    cls += '    return \'(\' + (\',\'.join(s)) + \')\'\n'
    cls += '\n\n\n'
    return cls

f.write(generate_Option_class(('var', 'versions', 'description')))

f.write('vars_list = {\n')
classes = []
for cn, cv in variables.iteritems():
    cls = "'" + cn + "' : {\n"
    sections = []
    for sn, sv in cv.iteritems():
        if sn is None or sn == '':
            sn = "General"
        sn = sn[0].upper() + sn[1:]
        section =  "'" + sn + "' : (\n"
        sv.sort()
        vs = []
        for v in sv:
            vs.append(v + "\n")
        section += '        '
        section += '       ,'.join(vs)
        if len(vs) == 1:
            section += "        ,"
        section += "     ) # End of section " + sn + "\n"
        sections.append(section)
    cls += "     " + ("     ,".join(sections))
    cls += "   } # End of class " + cn + "\n"
    classes.append(cls)
f.write(" ,".join(classes))
f.write("\n")
f.write('} # End of \n')
