//
// 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 "RootFormula.h"
#include "../Term/Object.h"

#include <functional>
#include <assert.h>

using namespace planning;

boost::hash<std::string> RootFormula::s_stringHasher;

RootFormula::~RootFormula()
{
  memory::release(this, m_arguments.begin(), m_arguments.end());
  releaseMembers();

  delete[] m_objectBuffer;
  delete[] m_termBuffer;
}

void RootFormula::releaseMembers()
{
  for (MapIncompleteApplications::const_iterator it = m_incompleteGroundApplications.begin();
       it != m_incompleteGroundApplications.end();
       ++it)
  {
    it->second->release(this);
  }
  m_incompleteGroundApplications.clear();

  for (std::vector<FormulaApplication*>::iterator it = m_applications.begin();
       it != m_applications.end();
       ++it)
  {
    if (*it != NULL)
    {
      (*it)->release(this);
      (*it) = NULL;
    }
  }
}

const std::string& RootFormula::getName() const
{
  return m_name;
}

size_t RootFormula::getOffset() const
{
  assert(m_offset != std::numeric_limits<size_t>::max());

  return m_offset;
}

FormulaApplication* RootFormula::getGroundFormulaApplication(const Object** begin, const Object** end) const
{
  if (isPreprocessed())
  {
    // TODO: possible to optimize by precalculating arguments id

    size_t id = getArgumentsId(begin, end);
    FormulaApplication*& appl = m_applications[id];
    if (appl == 0)
    {
      appl = instantiate(begin, end);
      appl->setArgumentsId(id);
      appl->acquire(this);
    }
    return appl;
  }
  else
  {
    std::vector<const Object*> objects(begin, end);
    std::pair<MapIncompleteApplications::iterator, bool> p = 
      m_incompleteGroundApplications.insert(std::make_pair(objects, (FormulaApplication*)NULL));
    if (p.second)
    {
      p.first->second = instantiate(begin, end);
      p.first->second->acquire(this);
    }
    return p.first->second;
  }
}

void RootFormula::preprocess(size_t offset)
{
  m_offset = offset;
  m_applications.resize(this->getDomainCardinality());

  for (MapIncompleteApplications::const_iterator it = m_incompleteGroundApplications.begin();
       it != m_incompleteGroundApplications.end();
       ++it)
  {
    // No need to acquire application since it already is

    FormulaApplication* application = it->second;

    const size_t argumentsId = getArgumentsId(it->first.begin(), it->first.end());
    application->setArgumentsId(argumentsId);

    assert(m_applications[argumentsId] == NULL);
    m_applications[argumentsId] = application;
  }
  m_incompleteGroundApplications.clear();
}

bool RootFormula::isPreprocessed() const
{
  return m_offset != std::numeric_limits<size_t>::max();
}

const std::vector<ObjectParameterVariable*>& RootFormula::getArguments() const
{
  return m_arguments;
}

size_t RootFormula::getArity() const
{
  return m_arguments.size();
}

void RootFormula::_output(std::ostream& out) const
{
  out << "(" << m_name;
  for (const_iterator it = m_arguments.begin();
       it != m_arguments.end();
       ++it)
  {
    out << " " << **it;
  }
  out << ")";
}

size_t RootFormula::getDomainCardinality() const
{
  size_t cardinality = 1;
  for (const_iterator it = m_arguments.begin();
       it != m_arguments.end();
       ++it)
  {
    cardinality *= (*it)->getType()->getDomainCardinality();
  }
  return cardinality;
}

size_t RootFormula::getHashCode() const
{
  return m_hashCode;
  // return static_cast<size_t>(this); ?
}

int RootFormula::compareTo(const RootFormula& other) const
{
  COMPARE_PTR(this, &other);

  return 0;
}
