//
// 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 this file was inspired in part by the PDDL4J library:
// http://www.math-info.univ-paris5.fr/~pellier/software/software.php 
//
// Implementation: Daniel Castonguay
// Project Manager: Froduald Kabanza
//

#include "stdafx.h"
#include "UntilExp.h"
#include "../Composite/TemporalAndExp.h"
#include "../Composite/TemporalOrExp.h"
#include "../../../World/IDurativeClosedWorld.h"

using namespace planning;

UntilExp::UntilExp(const ITemporalExp* exp1, const ITemporalExp* exp2)
  : m_exp1(exp1), m_exp2(exp2)
{
  m_exp1->acquire(this);
  m_exp2->acquire(this);

  INITIALIZE_HASH_CODE();
}

UntilExp::~UntilExp()
{
  m_exp1->release(this);
  m_exp2->release(this);
}

ProgressionValue UntilExp::progress(const IDurativeClosedWorld* world, const Bindings* bindings) const
{
  //return this.Exp2.Progress(world, bindings) || (this.Exp1.Progress(world, bindings) && new ProgressionValue(this, ProgressionValue.NoTimestamp));
  //return m_exp2->progress(world, bindings) || (m_exp1->progress(world, bindings) && this);

  ProgressionValue suffix2 = m_exp2->progress(world, bindings);
  if (suffix2.isTrue())
    return suffix2;
  else if (suffix2.isFalse())
  {
    ProgressionValue suffix1 = m_exp1->progress(world, bindings);
    if (suffix1.isTrue())
      return ProgressionValue(this->apply(bindings));
    else if (suffix1.isFalse())
      return suffix1;
    else
      return ProgressionValue(new TemporalAndExp(suffix1.getExp(), this->apply(bindings)));
  }
  else
  {
    ProgressionValue suffix1 = m_exp1->progress(world, bindings);
    if (suffix1.isTrue())
      return ProgressionValue(new TemporalOrExp(suffix2.getExp(), this->apply(bindings)));
    else if (suffix1.isFalse())
      return suffix2;
    else
      return ProgressionValue(new TemporalOrExp(suffix2.getExp(), new TemporalAndExp(suffix1.getExp(), this->apply(bindings))));
  }
}

bool UntilExp::evaluateIdle(const IDurativeClosedWorld* world, const Bindings* bindings) const
{
  return m_exp2->evaluateIdle(world, bindings);
}

const IExp* UntilExp::_internalApply(const Bindings* bindings) const
{
  return new UntilExp(m_exp1->apply(bindings), m_exp2->apply(bindings));
}

void UntilExp::_output(std::ostream& out) const
{
  out << "(until " << *m_exp1 << " " << *m_exp2 << ")";
}

int UntilExp::_internalCompareTo(const IExp& other) const
{
  const UntilExp& exp = dynamic_cast<const UntilExp&>(other);

  COMPARE(m_exp1, exp.m_exp1);

  COMPARE(m_exp2, exp.m_exp2);

  return 0;
}
