//
// Copyright (c) 2009 Froduald Kabanza and the Université de Sherbrooke.
// Use of this software is permitted for non-commercial research purposes, and
// it may be copied or applied only for that use. All copies must include this
// copyright message.
// 
// This is a research prototype that has not gone through intensive tests and
// is delivered as is. It may still contain bugs. Froduald Kabanza and the
// Université de Sherbrooke disclaim any responsibility for damage that may be
// caused by using it.
//
// Please note that some files were inspired in part by the PDDL4J library:
// http://www.math-info.univ-paris5.fr/~pellier/software/software.php 
//
// Implementation: Simon Chamberland
// Project Manager: Froduald Kabanza
//

#include "stdafx.h"
#include "DurativeGroundAction.h"
#include "../Exp/UnaryFunctions.h"

using namespace planning;

template <class Expression1, class Expression2>
class PairApplyBindings : public std::unary_function<std::pair<const Expression1*, const Expression2*>,
                                                     std::pair<const Expression1*, const Expression2*>>
{
public:
  PairApplyBindings(const Bindings* bindings)
    : m_bindings(bindings)
  {}

  std::pair<const Expression1*, const Expression2*> operator()(const std::pair<const Expression1*, const Expression2*>& p) const
  {
    return std::make_pair(p.first->apply(m_bindings), 
                          p.second->apply(m_bindings));
  }

private:
  const Bindings* m_bindings;
};

DurativeGroundAction::DurativeGroundAction(const DurativeAction* durativeAction, const Bindings* bindings)
  : m_arguments(boost::transform_iterator<LookupVariableBinding, DurativeAction::argument_iterator>(
                  durativeAction->argumentsBegin(), bindings), 
                boost::transform_iterator<LookupVariableBinding, DurativeAction::argument_iterator>(
                  durativeAction->argumentsEnd(), bindings)), 
    m_durativeAction(durativeAction),
    m_precondition(durativeAction->getPrecondition()->apply(bindings)),
    m_startEffect(durativeAction->getStartEffect()->apply(bindings)),
    m_delayedEffects(boost::transform_iterator<PairApplyBindings<INumericExp, IEffect>, DurativeAction::delayed_effect_iterator>(
                        durativeAction->delayedEffectsBegin(), bindings), 
                     boost::transform_iterator<PairApplyBindings<INumericExp, IEffect>, DurativeAction::delayed_effect_iterator>(
                        durativeAction->delayedEffectsEnd(), bindings))
{
  m_precondition->acquire(this);
  m_startEffect->acquire(this);

  for (std::vector<std::pair<const INumericExp*, const IEffect*>>::const_iterator it = m_delayedEffects.begin();
       it != m_delayedEffects.end();
       ++it)
  {
    it->first->acquire(this);
    it->second->acquire(this);
  }
}

DurativeGroundAction::~DurativeGroundAction()
{
  m_precondition->release(this);
  m_startEffect->release(this);

  for (std::vector<std::pair<const INumericExp*, const IEffect*>>::const_iterator it = m_delayedEffects.begin();
       it != m_delayedEffects.end();
       ++it)
  {
    it->first->release(this);
    it->second->release(this);
  }
}

bool DurativeGroundAction::isApplicable(const IClosedWorld* world) const
{
  return m_precondition->evaluate(world, Bindings::emptyBindings());
}

const IEffect* DurativeGroundAction::getStartEffect() const
{
  return m_startEffect;
}

void DurativeGroundAction::getDelayedEffects(const IClosedWorld* evaluationWorld, 
                                             std::vector<std::pair<double, const IEffect*>>& delayedEffects) const
{
  delayedEffects.resize(m_delayedEffects.size());
  std::vector<std::pair<double, const IEffect*>>::iterator itEffect = delayedEffects.begin();
  for (std::vector<std::pair<const INumericExp*, const IEffect*>>::const_iterator it = m_delayedEffects.begin();
       it != m_delayedEffects.end();
       ++it)
  {
    itEffect->first = it->first->evaluate(evaluationWorld, Bindings::emptyBindings());
    itEffect->second = it->second;
  }
}

void DurativeGroundAction::_output(std::ostream& out) const
{
  out << "(";
  out << m_durativeAction->getName();
  for (object_iterator it = m_arguments.begin();
       it != m_arguments.end();
       ++it)
  {
    out << " " << **it;
  }
  out << ")";
}