#!/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')
