# -*- coding: utf-8 -*-
"""Parser for Extensible Storage Engine (ESE) database files (EDB)."""

import pyesedb

from plaso import dependencies
from plaso.lib import specification
from plaso.parsers import interface
from plaso.parsers import manager
from plaso.parsers import plugins


dependencies.CheckModuleVersion(u'pyesedb')


class ESEDBCache(plugins.BasePluginCache):
  """A cache storing query results for ESEDB plugins."""

  def StoreDictInCache(self, attribute_name, dict_object):
    """Store a dict object in cache.

    Args:
      attribute_name (str): name of the attribute.
      dict_object (dict): dictionary.
    """
    setattr(self, attribute_name, dict_object)


class ESEDBParser(interface.FileObjectParser):
  """Parses Extensible Storage Engine (ESE) database files (EDB)."""

  _INITIAL_FILE_OFFSET = None

  NAME = u'esedb'
  DESCRIPTION = u'Parser for Extensible Storage Engine (ESE) database files.'

  _plugin_classes = {}

  def _GetTableNames(self, database):
    """Retrieves the table names in a database.

    Args:
      database (pyesedb.file): ESE database.

    Returns:
      list[str]: table names.
    """
    table_names = []
    for esedb_table in database.tables:
      table_names.append(esedb_table.name)

    return table_names

  @classmethod
  def GetFormatSpecification(cls):
    """Retrieves the format specification.

    Returns:
      FormatSpecification: format specification.
    """
    format_specification = specification.FormatSpecification(cls.NAME)
    format_specification.AddNewSignature(b'\xef\xcd\xab\x89', offset=4)
    return format_specification

  def ParseFileObject(self, parser_mediator, file_object, **kwargs):
    """Parses an ESE database file-like object.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      file_object (dfvfs.FileIO): file-like object.
    """
    esedb_file = pyesedb.file()

    try:
      esedb_file.open_file_object(file_object)
    except IOError as exception:
      parser_mediator.ProduceExtractionError(
          u'unable to open file with error: {0:s}'.format(exception))
      return

    # Compare the list of available plugin objects.
    cache = ESEDBCache()
    try:
      table_names = frozenset(self._GetTableNames(esedb_file))

      for plugin in self._plugin_objects:
        if parser_mediator.abort:
          break

        if plugin.required_tables.difference(table_names):
          continue

        try:
          plugin.UpdateChainAndProcess(
              parser_mediator, cache=cache, database=esedb_file)

        except Exception as exception:  # pylint: disable=broad-except
          parser_mediator.ProduceExtractionError((
              u'plugin: {0:s} unable to parse ESE database with error: '
              u'{1:s}').format(plugin.NAME, exception))

    finally:
      # TODO: explicitly clean up cache.

      esedb_file.close()


manager.ParsersManager.RegisterParser(ESEDBParser)
