"""This file contains a class for managing digest hashers for Plaso."""


class HashersManager:
  """Class that implements the hashers manager."""

  _hasher_classes = {}

  @classmethod
  def DeregisterHasher(cls, hasher_class):
    """Deregisters a hasher class.

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

    Args:
      hasher_class (type): class object of the hasher.

    Raises:
      KeyError: if hasher class is not set for the corresponding name.
    """
    hasher_name = hasher_class.NAME.lower()
    if hasher_name not in cls._hasher_classes:
      raise KeyError(f'hasher class not set for name: {hasher_class.NAME:s}.')

    del cls._hasher_classes[hasher_name]

  @classmethod
  def GetHasherNamesFromString(cls, hasher_names_string):
    """Retrieves a list of a hasher names from a comma separated string.

    Takes a string of comma separated hasher names transforms it to a list of
    hasher names.

    Args:
      hasher_names_string (str): comma separated names of hashers to enable,
          the string 'all' to enable all hashers or 'none' to disable all
          hashers.

    Returns:
      list[str]: names of valid hashers from the string, or an empty list if no
          valid names are found.
    """
    hasher_names = []

    if not hasher_names_string or hasher_names_string.strip() == 'none':
      return hasher_names

    if hasher_names_string.strip() == 'all':
      return cls.GetHasherNames()

    for hasher_name in hasher_names_string.split(','):
      hasher_name = hasher_name.strip()
      if not hasher_name:
        continue

      hasher_name = hasher_name.lower()
      if hasher_name in cls._hasher_classes:
        hasher_names.append(hasher_name)

    return hasher_names

  @classmethod
  def GetHasherNames(cls):
    """Retrieves the names of all loaded hashers.

    Returns:
      list[str]: hasher names.
    """
    return cls._hasher_classes.keys()

  @classmethod
  def GetHashersInformation(cls):
    """Retrieves the hashers information.

    Returns:
      list[tuple]: containing:

          str: hasher name.
          str: hasher description.
    """
    hashers_information = []
    for _, hasher_class in cls.GetHasherClasses():
      description = getattr(hasher_class, 'DESCRIPTION', '')
      hashers_information.append((hasher_class.NAME, description))

    return hashers_information

  @classmethod
  def GetHasher(cls, hasher_name):
    """Retrieves an instance of a specific hasher.

    Args:
      hasher_name (str): the name of the hasher to retrieve.

    Returns:
      BaseHasher: hasher.

    Raises:
      KeyError: if hasher class is not set for the corresponding name.
    """
    hasher_name = hasher_name.lower()
    if hasher_name not in cls._hasher_classes:
      raise KeyError(f'hasher class not set for name: {hasher_name:s}.')

    hasher_class = cls._hasher_classes[hasher_name]
    return hasher_class()

  @classmethod
  def GetHashers(cls, hasher_names):
    """Retrieves instances for all the specified hashers.

    Args:
      hasher_names (list[str]): names of the hashers to retrieve.

    Returns:
      list[BaseHasher]: hashers.
    """
    hashers = []
    for hasher_name, hasher_class in cls._hasher_classes.items():
      if hasher_name in hasher_names:
        hashers.append(hasher_class())

    return hashers

  @classmethod
  def GetHasherClasses(cls, hasher_names=None):
    """Retrieves the registered hashers.

    Args:
      hasher_names (list[str]): names of the hashers to retrieve.

    Yields:
        tuple: containing:

         str: parser name
         type: next hasher class.
    """
    for hasher_name, hasher_class in cls._hasher_classes.items():
      if not hasher_names or hasher_name in hasher_names:
        yield hasher_name, hasher_class

  @classmethod
  def RegisterHasher(cls, hasher_class):
    """Registers a hasher class.

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

    Args:
      hasher_class (type): class object of the hasher.

    Raises:
      KeyError: if hasher class is already set for the corresponding name.
    """
    hasher_name = hasher_class.NAME.lower()
    if hasher_name in cls._hasher_classes:
      raise KeyError(
          f'hasher class already set for name: {hasher_class.NAME:s}.')

    cls._hasher_classes[hasher_name] = hasher_class
