"""The CLI arguments helper manager objects."""

from plaso.lib import errors


class ArgumentHelperManager:
  """Class that implements the CLI argument helper manager."""

  _helper_classes = {}

  # Pylint 1.9.3 is confused by the format of the argument_group docstring.
  # pylint: disable=missing-param-doc,missing-type-doc
  @classmethod
  def AddCommandLineArguments(
      cls, argument_group, category=None, names=None):
    """Adds command line arguments to a configuration object.

    Args:
      argument_group (argparse._ArgumentGroup|argparse.ArgumentParser):
          argparse group.
      category (Optional[str]): category of helpers to apply to
          the group, such as storage, output, where None will apply the
          arguments to all helpers. The category can be used to add arguments
          to a specific group of registered helpers.
      names (Optional[list[str]]): names of argument helpers to apply,
          where None will apply the arguments to all helpers.
    """
    # Process the helper classes in alphabetical order this is needed to
    # keep the argument order consistent.
    for helper_name, helper_class in sorted(cls._helper_classes.items()):
      if ((category and helper_class.CATEGORY != category) or
          (names and helper_name not in names)):
        continue

      helper_class.AddArguments(argument_group)

  @classmethod
  def DeregisterHelper(cls, helper_class):
    """Deregisters a helper class.

    The helper classes are identified based on their lower case name.

    Args:
      helper_class (type): class object of the argument helper.

    Raises:
      KeyError: if helper class is not set for the corresponding name.
    """
    helper_name = helper_class.NAME.lower()
    if helper_name not in cls._helper_classes:
      raise KeyError(f'Helper class not set for name: {helper_class.NAME:s}.')

    del cls._helper_classes[helper_name]

  @classmethod
  def ParseOptions(cls, options, config_object, category=None, names=None):
    """Parses and validates arguments using the appropriate helpers.

    Args:
      options (argparse.Namespace): parser options.
      config_object (object): object to be configured by an argument helper.
      category (Optional[str]): category of helpers to apply to
          the group, such as storage, output, where None will apply the
          arguments to all helpers. The category can be used to add arguments
          to a specific group of registered helpers.
      names (Optional[list[str]]): names of argument helpers to apply,
          where None will apply the arguments to all helpers.
    """
    for helper_name, helper_class in cls._helper_classes.items():
      if ((category and helper_class.CATEGORY != category) or
          (names and helper_name not in names)):
        continue

      try:
        helper_class.ParseOptions(options, config_object)
      except errors.BadConfigObject:
        pass

  @classmethod
  def RegisterHelper(cls, helper_class):
    """Registers a helper class.

    The helper classes are identified based on their lower case name.

    Args:
      helper_class (type): class object of the argument helper.

    Raises:
      KeyError: if helper class is already set for the corresponding name.
    """
    helper_name = helper_class.NAME.lower()
    if helper_name in cls._helper_classes:
      raise KeyError(
          f'Helper class already set for name: {helper_class.NAME:s}.')

    cls._helper_classes[helper_name] = helper_class

  @classmethod
  def RegisterHelpers(cls, helper_classes):
    """Registers helper classes.

    The helper classes are identified based on their lower case name.

    Args:
      helper_classes (list[type]): class objects of the argument helpers.

    Raises:
      KeyError: if helper class is already set for the corresponding name.
    """
    for helper_class in helper_classes:
      cls.RegisterHelper(helper_class)
