//
// 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 "AndExp.h"
#include "../UnaryFunctions.h"

#include <assert.h>

using namespace planning;

const std::string AndExp::s_image = "and";

/// <summary>
/// An instance of ContextData stores a world and a list of worlds satisfying a 
/// given element of the conjunctive formula.
/// </summary>
struct ContextData
{
  // TODO: acquire/release mechanism for partial worlds

  ContextData()
    : world(NULL)
  {
  }

  ~ContextData()
  {
    if (world != NULL)
      delete world;

    for (ILogicalExp::PartialWorldSet::iterator it = satisfyingWorlds.begin();
         it != satisfyingWorlds.end();
         ++it)
    {
      delete *it;
    }
  }

  /// <summary>
  /// The world.
  /// </summary>
  PartialWorld* world;
  /// <summary>
  /// The list of worlds satisfying some logical expression.
  /// </summary>
  ILogicalExp::PartialWorldSet satisfyingWorlds;

  ILogicalExp::PartialWorldSet::const_iterator worldIterator;
};

/// <summary>
/// Evaluates this logical expression in the specified closed world.
/// </summary>
/// <param name="world">The evaluation world.</param>
/// <param name="bindings">A set of variable bindings.</param>
/// <returns>True, false, or undefined.</returns>
bool AndExp::evaluate(const IClosedWorld* world, const Bindings* bindings) const
{
  for (const_iterator it = m_elements.begin(); 
       it != m_elements.end();
       ++it)
  {
    if (!(*it)->evaluate(world, bindings))
      return false;
  }
  return true;
}

/// <summary>
/// Enumerates all the worlds within which this logical expression evaluates to true.
/// This method is used to support the goal modality expressions.
/// </summary>
/// <returns>All the worlds satisfying this logical expression.</returns>
void AndExp::enumerateSatisfyingWorlds(PartialWorldSet& worlds) const
{
  assert(worlds.size() == 0);

  //HashSet<PartialWorld> worlds = new HashSet<PartialWorld>();

  //if (this.m_expressions.Count == 0)
  //{
  //  worlds.Add(currentContext);
  //  return worlds;
  //}

  if (m_elements.size() == 0)
  {
    worlds.insert(new PartialWorld());
  }
  else
  {
    const size_t nbElements = m_elements.size();

    //ContextData[] contexts = new ContextData[this.m_expressions.Count];
    //PartialWorld currentContext = new PartialWorld();

    //for (int i = 0; i < this.m_expressions.Count; ++i)
    //{
    //  IEnumerator<PartialWorld> enumerator = ((ILogicalExp)this.m_expressions[i]).EnumerateAllSatisfyingWorlds().GetEnumerator();
    //  contexts[i] = new ContextData(null, enumerator);
    //}

    ContextData* contexts = new ContextData[nbElements];
    {
      size_t i = 0;
      for (const_iterator it = m_elements.begin(); it != m_elements.end(); ++it)
      {
        (*it)->enumerateSatisfyingWorlds(contexts[i].satisfyingWorlds);
        contexts[i].worldIterator = contexts[i].satisfyingWorlds.begin();
        ++i;
      }
    }

    //contexts[0].World = new PartialWorld();
    //int currentIndex = 0;
    //int lastIndex = this.m_expressions.Count - 1;

    contexts[0].world = new PartialWorld();
    size_t currentIndex = 0;
    const size_t lastIndex = nbElements - 1;

    //while (currentIndex >= 0)
    //{
    while (true)
    {
    //  ContextData data = contexts[currentIndex];
    //  if (currentIndex == lastIndex)
    //  {
    //    while (data.AllSatisfyingWorlds.MoveNext())
    //    {
    //      PartialWorld newContext = data.World.Join(data.AllSatisfyingWorlds.Current);
    //      if (newContext != null)
    //        worlds.Add(newContext);
    //    }
    //    --currentIndex;
    //  }
      ContextData& data = contexts[currentIndex];
      if (currentIndex == lastIndex)
      {
        for (;
             data.worldIterator != data.satisfyingWorlds.end();
             ++data.worldIterator)
        {
          PartialWorld* newWorld = data.world->join(**data.worldIterator);
          if (newWorld != NULL)
          {
            std::pair<PartialWorldSet::iterator, bool> p = worlds.insert(newWorld);
            if (!p.second)
              delete newWorld;
          }
        }
        --currentIndex;
      }

    //  else
    //  {
    //    PartialWorld newContext = null;
    //    while (newContext == null && data.AllSatisfyingWorlds.MoveNext())
    //    {
    //      newContext = data.World.Join(data.AllSatisfyingWorlds.Current);
    //    }
    //    if (newContext == null)
    //    {
    //      if (--currentIndex < 0)
    //        break;
    //    }
    //    else
    //    {
    //      ++currentIndex;
    //      contexts[currentIndex].World = newContext;
    //      contexts[currentIndex].AllSatisfyingWorlds.Reset();
    //    }
    //  }
    //}
      else
      {
        PartialWorld* newWorld = NULL;
        for (;
             data.worldIterator != data.satisfyingWorlds.end() && (newWorld == NULL);
             ++data.worldIterator)
        {
          newWorld = data.world->join(**data.worldIterator);
        }
        if (newWorld == NULL)
        {
          if (currentIndex == 0)
            break;
          else
            --currentIndex;
        }
        else
        {
          ++currentIndex;
          contexts[currentIndex].world = newWorld;
          contexts[currentIndex].worldIterator = contexts[currentIndex].satisfyingWorlds.begin();
        }
      }

    }
    delete[] contexts;
  }
}

const IExp* AndExp::_internalApply(const Bindings* bindings) const
{
  return new AndExp(m_elements.size(),
                    boost::transform_iterator<ApplyBindings<ILogicalExp>, const_iterator>(m_elements.begin(), bindings), 
                    boost::transform_iterator<ApplyBindings<ILogicalExp>, const_iterator>(m_elements.end(), bindings));
}