//
// 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
//

#ifndef _EXP_H
#define _EXP_H

#include "Bindings.h"
#include "../Utils/Utils.h"
#include "../../Utils/ReferenceCounted.h"

#include <string>
#include <fstream>
#include <list>
#include <iostream>

// define INIT_HASH_CODE to initialize hash code during construction

#ifdef INIT_HASH_CODE
#define INITIALIZE_HASH_CODE() _setHashCode(_calculateHashCode())
#define CALCULATE_HASH_CODE(DerivedClass) DerivedClass::getHashCode()
#else
#define INITIALIZE_HASH_CODE()
#define CALCULATE_HASH_CODE(DerivedClass) DerivedClass::_calculateHashCode()
#endif

namespace planning
{
  /// <summary>
  /// This interface is implemented by all expressions of the PDDL langage.
  /// </summary>
  class IExp : public ReferenceCounted
  {   
  public:
    IExp()
#if defined(_DEBUG) || !defined(INIT_HASH_CODE)
      : m_hashCodeInitialized(false)
#endif
    {
      // m_hashCode must be initialized in derived classes!
      // (cannot call virtual methods here; and 
      // requiring the actual hash code as argument is troublesome)
    }

    virtual ~IExp()
    {
    }

    const IExp* apply(const Bindings* bindings) const
    {
      if (bindings->empty())
        return this;

      //
      // VC++ 2008 does not support mixing virtual inheritance and overriden virtual
      // functions with covariant return type. Hence the overloaded "apply" functions
      // are not virtual, but the "_internalApply" functions are.
      //
      return _internalApply(bindings);
    }

    int compareTo(const IExp& other) const
    {
      if (this == &other)
        return 0;

#ifdef INIT_HASH_CODE
      COMPARE(getHashCode(), other.getHashCode());
#endif

      COMPARE(typeid(*this), typeid(other));

      return _internalCompareTo(other);
    }

    size_t getHashCode() const
    {
#ifdef INIT_HASH_CODE
#ifdef _DEBUG
      // If this assertion fails, make sure the hash code is calculated in the constructor,
      // or before the actual getHashCode() call!
      assert(m_hashCodeInitialized);
#endif
#else
      if (!m_hashCodeInitialized)
      {
        _setHashCode(_calculateHashCode());
      }
#endif
      return m_hashCode;
    }

  protected:
    virtual const IExp* _internalApply(const Bindings* bindings) const = 0;

    virtual int _internalCompareTo(const IExp& other) const = 0;

    virtual size_t _calculateHashCode() const = 0;

    void _setHashCode(size_t hashCode) const
    {
      // _setHashCode() could take no parameter, but in this case I'm not sure it could be called
      // during construction (or would it crash on a pure virtual function call?)
#if defined(_DEBUG) || !defined(INIT_HASH_CODE)
      m_hashCodeInitialized = true;
#endif
      m_hashCode = hashCode;
    }

  private:
    mutable size_t m_hashCode;
#if defined(_DEBUG) || !defined(INIT_HASH_CODE)
    mutable bool m_hashCodeInitialized;
#endif

    ///// <summary>
    ///// Substitutes all occurrences of the variables that occur in this
    ///// expression by their corresponding bindings.
    ///// </summary>
    ///// <param name="bindings">A set of variable bindings.</param>
    ///// <returns>A substituted copy of this expression.</returns>
    //virtual IExp* apply(const Bindings *bindings) const = 0;

    ///// <summary>
    ///// Returns true if the expression is ground, i.e. it does not contain any variables.
    ///// </summary>
    ///// <returns>Whether the expression is ground.</returns>
    //bool IsGround();

    ///// <summary>
    ///// Returns the free variables in this expression.
    ///// </summary>
    ///// <returns>The free variables in this expression.</returns>
    //HashSet<Variable> GetFreeVariables();

    ///// <summary>
    ///// Returns a typed string representation of this expression.
    ///// </summary>
    ///// <returns>A typed string representation of this expression.</returns>
    //string ToTypedString();

  };

  //template <class InputIterator, class OutputIterator>
  //void IExp::applyBindings(const Bindings* bindings, InputIterator first, InputIterator last, 
  //                         OutputIterator output)
  //{
  //  for (; first != last; ++first)
  //  {
  //    *output++ = (*first)->apply(bindings);
  //  }
  //}
}

#endif