"""Helper object to identify configuration parameters so we can easily overwrite
them via command line magic or via the steering file

To add additional arguments create either a member variable or a property to a
subclass of the ConfigHelper. To add additional arguments to the add_argument
call for the parser object create an additional member::

  self.member = [1,2]
  self._member_EXTRA = {'help': 'description for parameter',
                        'nargs': '+',
                       }

"""

from __future__ import absolute_import, unicode_literals

import ddsix as six


class ConfigHelper(object):
  """Base class for configuration helper"""

  def __init__(self):
    pass

  def getOptions(self):
    finalVars = {}

    # get all direct members not starting with underscore
    allVars = vars(self)
    for var, val in six.iteritems(allVars):
      if not var.startswith('_'):
        extraArgumentsName = "_%s_EXTRA" % var
        options = getattr(self, extraArgumentsName) if hasattr(self, extraArgumentsName) else None
        finalVars[var] = {'default': val}
        if options:
          finalVars[var].update(options)

    # now get things defined with @property
    props = [(p, getattr(type(self), p)) for p in dir(type(self)) if isinstance(getattr(type(self), p), property)]
    for propName, prop in props:
      optName = "_%s_EXTRA" % propName
      doc = prop.__doc__
      options = getattr(self, optName) if hasattr(self, optName) else None
      finalVars[propName] = {'default': getattr(self, propName)}
      if doc:
        finalVars[propName]['help'] = doc
      if options:
        finalVars[propName].update(options)

    return finalVars

  def __repr__(self):
    return self.printOptions()

  def printOptions(self):
    """print all parameters"""
    options = []
    for opt, val in six.iteritems(self.getOptions()):
      options.append("\n\t'%s': '%s'" % (opt, val['default']))
    return "".join(options)

  def setOption(self, name, val):
    """ set the attribute name to val """
    setattr(self, name, val)

  @staticmethod
  def makeList(stringVal, sep=" "):
    """returns a list from a string separated by sep"""
    if not stringVal:
      return []
    if isinstance(stringVal, list):
      return stringVal
    else:
      return stringVal.split(sep)

  @staticmethod
  def makeSet(stringVal, sep=" "):
    """returns a set from a string separated by sep"""
    if not stringVal:
      return set()
    if isinstance(stringVal, (list, set, tuple)):
      return set(stringVal)
    else:
      return set(stringVal.split(sep))

  @staticmethod
  def makeString(container):
    """Return a string that can be parsed by dd4hep into a vector."""
    if not container:
      return ""
    if isinstance(container, set):
      return '{%s}' % ','.join([str(s) for s in container])

  @staticmethod
  def makeTuple(val):
    """ returns a tuple of the string, separators are space or comma """
    myTuple = None
    if isinstance(val, tuple):
      myTuple = val
    if isinstance(val, list):
      myTuple = tuple(val)
    if isinstance(val, six.string_types):
      sep = ',' if ',' in val else ' '
      myTuple = tuple([_.strip("(), ") for _ in val.split(sep)])
    if myTuple is None:
      raise RuntimeError("Cannot parse input value %s" % val)
    return myTuple

  @staticmethod
  def makeBool(val):
    """check if val is a bool or a string of true/false, otherwise raise exception"""
    if isinstance(val, bool):
      return val
    elif isinstance(val, six.string_types):
      if val.lower() == 'true':
        return True
      elif val.lower() == 'false':
        return False
    raise RuntimeError(val)

  @staticmethod
  def addAllHelper(ddsim, parser):
    """all configHelper objects to commandline args"""
    for name, obj in six.iteritems(vars(ddsim)):
      if isinstance(obj, ConfigHelper):
        for var, optionsDict in six.iteritems(obj.getOptions()):
          optionsDict['action'] = 'store_true' if var.startswith("enable") else optionsDict.get('action', 'store')
          parser.add_argument("--%s.%s" % (name, var),
                              dest="%s.%s" % (name, var),
                              **optionsDict
                              )