/**
   @file reversible_pmap.hpp
   @brief wrapper for a reversible property map
 */

#ifndef REVERSIBLE_PMAP_HPP
#define REVERSIBLE_PMAP_HPP

#include <boost/property_map.hpp>
//#include <boost/lambda/bind.hpp>
#include <boost/bind.hpp>

#include <utils/action_queue.hpp>

namespace boost {

  template <typename PA>
  struct reversible_property_map {
    
    typedef PA Base;
    typedef property_traits<PA> Traits;

    // take definitions from base property map
    typedef typename Traits::key_type   key_type;
    typedef typename Traits::value_type value_type; 
    typedef typename Traits::reference  reference;
    typedef typename Traits::category   category;

    reversible_property_map(PA pmap, action_queue& aq)
      : m_pmap(pmap), m_aq(aq) {}

    reference operator[](const key_type k) const {
      return m_pmap[k];
    }

    void undo_put(const key_type k, const value_type& val) {
      put(m_pmap, k, val);
    }

    // private:
    PA m_pmap;
    action_queue& m_aq;
  };

  /* temporary objects are too expensive to generate 
  namespace detail {
    // this helper object should actually not be necessary!
    template<typename PA>
    struct undo_put {
      typedef PA                                  Property;
      typedef typename boost::property_traits<PA> Traits;
      typedef typename Traits::key_type           key_type;
      typedef typename Traits::value_type         value_type;
      undo_put(Property p, const key_type k, const value_type& val) : _p(p), _k(k), _v(val) {}
      void operator()() { put(_p, _k, _v); }
    private:
      Property _p;
      const key_type _k;
      const value_type _v;
    };
  }
  */

  template<typename PA>
  void
  put(reversible_property_map<PA>& rpa,
      const typename reversible_property_map<PA>::key_type k,
      const typename reversible_property_map<PA>::value_type& v)
  {
    // save old value into undo queue
    typename reversible_property_map<PA>::value_type
      oval = get(rpa.m_pmap, k);
    // using this lambda expression works, but produces segfaults if
    // used with optimizations of gcc :(
    //typedef detail::undo_put<PA> undo_functor;
    //rpa.m_aq.register_action(undo_functor(rpa.m_pmap, k, oval));
    //typedef boost::function<void ()> action_t;
    rpa.m_aq.register_action( bind(&reversible_property_map<PA>::undo_put, boost::ref(rpa), k, oval) );
    // ok, now acutally do the put
    put(rpa.m_pmap, k, v);
  }

  template<typename PA>
  inline const typename reversible_property_map<PA>::reference
  get(reversible_property_map<PA>& rpa,
      const typename reversible_property_map<PA>::key_type k)
  {
    return static_cast<const reversible_property_map<PA>&>(rpa)[k];
  }

  // convenience function
  template<typename PA>
  reversible_property_map<PA>
  make_reversible_pmap(PA p, action_queue& aq)
  {
    return reversible_property_map<PA>(p, aq);
  }
}

#endif

