# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################

class MovementGeneratorMixin(object):
  """
  This class provides a generic implementation of IMovementGenerator
  which can be used together the Rule mixin class bellow. It does not
  have any pretention to provide more than that.

  TODO:
    - _getInputMovementList is still not well defined. Should input
      be an amount (_getInputAmountList) or a movement? This
      requires careful thiking.
  """
  # Default values
  _applied_rule = None
  _rule = None
  _trade_phase_list = None
  _explanation = None

  def __init__(self, applied_rule, explanation=None, rule=None, trade_phase_list=None):
    self._trade_phase_list = trade_phase_list # XXX-JPS Why a list ?
    self._applied_rule = applied_rule
    if rule is None and applied_rule is not None:
      self._rule = applied_rule.getSpecialiseValue()
    else:
      self._rule = rule # for rule specific stuff
    if explanation is None:
      self._explanation = applied_rule
    else:
      # A good example of explicit explanation can be getRootExplanationLineValue
      # since different lines could have different dates
      # such an explicit root explanation only works if
      # indexing of simulation has already happened
      self._explanation = explanation
    # XXX-JPS handle delay_mode

  # Implementation of IMovementGenerator
  def getGeneratedMovementList(self, movement_list=None, rounding=False):
    """
    Returns a list of movements generated by that rule.

    movement_list - optional IMovementList which can be passed explicitely

    rounding - boolean argument, which controls if rounding shall be applied on
               generated movements or not

    NOTE:
      - implement rounding appropriately (True or False seems
        simplistic)
    """
    # Results will be appended to result
    result = []
    # Build a list of movement and business path
    input_movement_list = self._getInputMovementList(
                            movement_list=movement_list, rounding=rounding)
    for input_movement in input_movement_list:
      # Merge movement and business path properties (core implementation)
      # Lookup Business Process through composition (NOT UNION)
      business_process = input_movement.asComposedDocument()
      explanation = self._applied_rule # We use applied rule as local explanation
      trade_phase = self._getTradePhaseList(input_movement, business_process) # XXX-JPS not convenient to handle
      update_property_dict = self._getUpdatePropertyDict(input_movement)
      generated_movement_list = business_process.getTradePhaseMovementList(
        explanation,
        input_movement,
        trade_phase=trade_phase,
        delay_mode=None,
        update_property_dict=update_property_dict)
      result.extend(self._updateGeneratedMovementList(input_movement, generated_movement_list))

    # And return list of generated movements
    return result

  def _getUpdatePropertyDict(self, input_movement):
    # XXX Wouldn't it better to return {} or {'delivery': None} ?
    #     Below code is mainly for root applied rules.
    #     Other movement generators usually want to reset delivery.
    return {'delivery': input_movement.getRelativeUrl()}

  def _updateGeneratedMovementList(self, input_movement, generated_movement_list):
    return generated_movement_list

  def _getTradePhaseList(self, input_movement, business_process): # XXX-JPS WEIRD
    if self._trade_phase_list:
      return self._trade_phase_list
    if self._rule is not None:
      trade_phase_list = self._rule.getTradePhaseList()
      if trade_phase_list:
        return trade_phase_list
    return input_movement.getTradePhaseList() or \
      business_process.getTradePhaseList()

  def _getInputMovementList(self, movement_list=None, rounding=None): #XXX-JPS should it be amount or movement ?
    raise NotImplementedError
    # Default implementation takes amounts ?
    # Use TradeModelRuleMovementGenerator._getInputMovementList as default implementation
    # and potentially use trade phase for that.... as a way to filter out
