//
// 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 and it 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.
//
// Implementation: Simon Chamberland
// Project Manager: Froduald Kabanza
//

#ifndef _BITSET_FACTS_CONTAINER_H
#define _BITSET_FACTS_CONTAINER_H

#include "FactsContainer.h"

// Can't use std::bitset since the number of bits is not known at compile-time
#include <boost/dynamic_bitset.hpp>
#include <assert.h>

namespace planning
{
  /// <summary>
  /// A bitset facts container holds facts in a fixed bitset.
  /// Since each atomic formula application is assigned a unique formula ID, it is possible to
  /// store all possible facts in a bitset (indexed by their ID). This approach may be more
  /// memory-expensive than other methods but it allows for constant-time retrieval of
  /// predicates' value.
  /// </summary>
  class BitSetFactsContainer : public FactsContainer
  {
  public:
    /// <summary>
    /// Creates a new bitset facts container of the specified length.
    /// </summary>
    /// <param name="interval">The interval of predicates ID whose value are stored
    /// in this new bitset facts container.</param>
    BitSetFactsContainer(IntegerInterval interval);

    BitSetFactsContainer(const BitSetFactsContainer &other);

    /// <summary>
    /// Copies this facts container.
    /// </summary>
    /// <returns>A copy of this facts container.</returns>
    virtual FactsContainer* copy() const;

    /// <summary>
    /// Checks whether the specified described atomic formula holds in this world.
    /// The value (a bit) is retrieved from the bitset.
    /// </summary>
    /// <param name="formulaID">A formula ID.</param>
    /// <returns>True, false, or unknown.</returns>
    virtual bool isSet(FormulaId id) const;

    /// <summary>
    /// Sets the specified atomic formula to true.
    /// This function updates the appropriate bit.
    /// </summary>
    /// <param name="formulaID">A formula ID.</param>
    virtual void set(FormulaId id);

    /// <summary>
    /// Sets the specified atomic formula to false.
    /// This function updates the appropriate bit.
    /// </summary>
    /// <param name="formulaID">A formula ID.</param>
    virtual void unset(FormulaId id);

    virtual size_t getHashCode() const;

    virtual int compareTo(const FactsContainer& other) const;

  private:
    boost::dynamic_bitset<> m_facts;

    int m_offset;

    size_t m_hashCode;

    //#region Private Fields

    ///// <summary>
    ///// The bitset containing all facts.
    ///// </summary>
    //private BitSet m_facts;

    ///// <summary>
    ///// The hash code of this facts container.
    ///// </summary>
    //private int m_hashCode;

    ///// <summary>
    ///// The offset (in a lookup table) corresponding to the predicate at bit 0 of this bitset.
    ///// </summary>
    //private int m_offset;

    //#endregion

    //#region Properties

    ///// <summary>
    ///// The interval of predicates ID whose value are stored in this bitset.
    ///// </summary>
    //public IntegerInterval DefinitionInterval
    //{
    //  get
    //  {
    //    return new IntegerInterval(m_offset, m_facts.Count);
    //  }
    //}

    //#endregion

    //#endregion

    //#region Object Interface Overrides

    ///// <summary>
    ///// Returns whether this facts container is equal to another object.
    ///// Two bitset facts containers are equal if they hold the same facts.
    ///// </summary>
    ///// <param name="obj">The other object to test for equality.</param>
    ///// <returns>Whether this facts container is equal to the other object.</returns>
    //public override bool Equals(object obj)
    //{
    //  if (obj == this)
    //  {
    //    return true;
    //  }
    //  else
    //  {
    //    BitSetFactsContainer other = (BitSetFactsContainer)obj;
    //    System.Diagnostics.Debug.Assert(this.m_offset == other.m_offset);

    //    return this.m_facts.Equals(other.m_facts);
    //  }
    //}

    ///// <summary>
    ///// Returns the hash code of this facts container.
    ///// </summary>
    ///// <returns>The hash code of this facts container.</returns>
    //public override int GetHashCode()
    //{
    //  return m_hashCode;
    //}

    //#endregion

    //#region IComparable<FactsContainer> interface

    ///// <summary>
    ///// Compares this facts container with another facts container.
    ///// Comparison is done first on the hashcode, then on the internal bitsets. 
    ///// </summary>
    ///// <param name="other">The other facts container to compare this facts container to.</param>
    ///// <returns>An integer representing the total order relation between the two facts containers.
    ///// </returns>
    //public override int CompareTo(FactsContainer other)
    //{
    //  int value = this.GetHashCode().CompareTo(other.GetHashCode());
    //  if (value != 0)
    //    return value;

    //  // We do not compare worlds of different implementations... yet.

    //  BitSetFactsContainer otherCnt = (BitSetFactsContainer)other;

    //  System.Diagnostics.Debug.Assert(this.m_offset == otherCnt.m_offset);

    //  value = this.m_offset.CompareTo(otherCnt.m_offset);
    //  if (value != 0)
    //    return value;

    //  return this.m_facts.CompareTo(otherCnt.m_facts);
    //}

    //#endregion
  };
}

#endif