#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

'''Interface for all gatherers.
'''


import os.path
import types

from grit import clique
from grit import util


class GathererBase(object):
  '''Interface for all gatherer implementations.  Subclasses must implement
  all methods that raise NotImplemented.'''

  def __init__(self, rc_file, extkey=None, encoding='cp1252', is_skeleton=False):
    '''Initializes the gatherer object's attributes, but does not attempt to
    read the input file.

    Args:
      rc_file: The 'file' attribute of the <structure> node (usually the
               relative path to the source file).
      extkey: e.g. 'ID_MY_DIALOG'
      encoding: e.g. 'utf-8'
      is_skeleton: Indicates whether this gatherer is a skeleton gatherer, in
                   which case we should not do some types of processing on the
                   translateable bits.
    '''
    self.rc_file = rc_file
    self.extkey = extkey
    self.encoding = encoding
    # A default uberclique that is local to this object.  Users can override
    # this with the uberclique they are using.
    self.uberclique = clique.UberClique()
    # Indicates whether this gatherer is a skeleton gatherer, in which case
    # we should not do some types of processing on the translateable bits.
    self.is_skeleton = is_skeleton
    # Stores the grd node on which this gatherer is running. This allows
    # evaluating expressions.
    self.grd_node = None

  def SetAttributes(self, attrs):
    '''Sets node attributes used by the gatherer.

    By default, this does nothing.  If special handling is desired, it should be
    overridden by the child gatherer.

    Args:
      attrs: The mapping of node attributes.
    '''
    pass

  def SetDefines(self, defines):
    '''Sets global defines used by the gatherer.

    By default, this does nothing.  If special handling is desired, it should be
    overridden by the child gatherer.

    Args:
      defines: The mapping of define values.
    '''
    pass

  def SetGrdNode(self, node):
    '''Sets the grd node on which this gatherer is running.
    '''
    self.grd_node = node

  def SetUberClique(self, uberclique):
    '''Overrides the default uberclique so that cliques created by this object
    become part of the uberclique supplied by the user.
    '''
    self.uberclique = uberclique

  def Parse(self):
    '''Reads and parses the contents of what is being gathered.'''
    raise NotImplementedError()

  def GetData(self, lang, encoding):
    '''Returns the data to be added to the DataPack for this node or None if
    this node does not add a DataPack entry.
    '''
    return None

  def GetText(self):
    '''Returns the text of what is being gathered.'''
    raise NotImplementedError()

  def GetTextualIds(self):
    '''Returns the mnemonic IDs that need to be defined for the resource
    being gathered to compile correctly.'''
    return []

  def GetCliques(self):
    '''Returns the MessageClique objects for all translateable portions.'''
    return []

  def GetInputPath(self):
    return self.rc_file

  def GetHtmlResourceFilenames(self):
    """Returns a set of all filenames inlined by this gatherer."""
    return []

  def Translate(self, lang, pseudo_if_not_available=True,
                skeleton_gatherer=None, fallback_to_english=False):
    '''Returns the resource being gathered, with translateable portions filled
    with the translation for language 'lang'.

    If pseudo_if_not_available is true, a pseudotranslation will be used for any
    message that doesn't have a real translation available.

    If no translation is available and pseudo_if_not_available is false,
    fallback_to_english controls the behavior.  If it is false, throw an error.
    If it is true, use the English version of the message as its own
    "translation".

    If skeleton_gatherer is specified, the translation will use the nontranslateable
    parts from the gatherer 'skeleton_gatherer', which must be of the same type
    as 'self'.

    If fallback_to_english

    Args:
      lang: 'en'
      pseudo_if_not_available: True | False
      skeleton_gatherer: other_gatherer
      fallback_to_english: True | False

    Return:
      e.g. 'ID_THIS_SECTION TYPE\n...BEGIN\n  "Translated message"\n......\nEND'

    Raises:
      grit.exception.NotReady() if used before Parse() has been successfully
      called.
      grit.exception.NoSuchTranslation() if 'pseudo_if_not_available' and
      fallback_to_english are both false and there is no translation for the
      requested language.
    '''
    raise NotImplementedError()

  def SubstituteMessages(self, substituter):
    '''Applies substitutions to all messages in the gatherer.

    Args:
      substituter: a grit.util.Substituter object.
    '''
    pass

  def SetFilenameExpansionFunction(self, fn):
    '''Sets a function for rewriting filenames before gathering.'''
    pass

  # TODO(benrg): Move this elsewhere, since it isn't part of the interface.
  def _LoadInputFile(self):
    '''A convenience function for subclasses that loads the contents of the
    input file.
    '''
    if isinstance(self.rc_file, types.StringTypes):
      path = self.GetInputPath()
      # Hack: some unit tests supply an absolute path and no root node.
      if not os.path.isabs(path):
        path = self.grd_node.ToRealPath(path)
      return util.ReadFile(path, self.encoding)
    else:
      return self.rc_file.read()
