#ifndef EMBED_TYPE_HPP
#define EMBED_TYPE_HPP

#include <boost/mpl/transform.hpp>
#include <boost/mpl/placeholders.hpp>

#include <boost/generate_hierarchy.hpp> // my sibling proposal

namespace boost {

  /*
    
  File: embed_type.hpp
  Author: David Bergman, based on Beman Daves' 'identifier' proposal.
  Date: 2006-07-25
  
  These templates and classes embed a (primitive or not) type, and with
  a set of policies decides what aspects of that type should be exposed
  in the embedding.
  
  */
  
  /*
    
  The various policies that we use in the final embedding. The policies
  are either implemented or not, depending on a template flag. They constitute
  an inheritance chain of types.
  
  We use the proposed generate_chain meta construct to generate
  the actual chain of policies.
  
  So, all policies are MPL meta function classes generating MPL meta function classes,
  i.e., meta meta function classes. The first application is to get the particular
  policy node (with a hole) from the two types  
  and the second application is to get the hierarchy up to that point.
  
  The template parameter D signifies the embedding type.
  
  In order to create your own embedding policies you "just" have to
  to create a meta meta function class or - easier - a teriary
  template and use the utility 'to_mmf'.

  */

  // We first need a utility that creates the MPL meta meta function class
  // - or curried meta function class -
  // from a plain template taking three parameters.

  template <template <typename T, typename D, typename Base> class Policy>
  struct policy : quote_raw3<Policy> {};

  //
  // Now the various policies already implemented
  // 

  // Order
  template <typename T, typename D, typename C>
  struct embed_order : C {
    bool operator<(const D& rhs) const { return this->value_ref() < rhs.value_ref(); }
    bool operator<=(const D& rhs) const { return this->value_ref() <= rhs.value_ref(); }
    bool operator>(const D& rhs) const { return this->value_ref() > rhs.value_ref(); }
    bool operator>=(const D& rhs) const { return this->value_ref() >= rhs.value_ref(); }
  };
  
  // Equivalence
  template <typename T, typename D, typename C>
  struct embed_equivalence : C {
    bool operator==(const D& rhs) const { return this->value_ref() == rhs.value_ref(); }
    bool operator!=(const D& rhs) const { return this->value_ref() != rhs.value_ref(); }
  };
  
  // Assignability from source type
  template <typename T, typename D, typename C>
  struct embed_assignability : C {
    D& operator=(const T& t) { this->value_ref(t); return *this; }
  };
  
  // Conversion to source
  template <typename T, typename D, typename C>
  struct embed_conversion : C {
    operator T() const { return this->value_ref(); }
  };

  // Arithmetics (+, -, +=, -= at least...)
  template <typename T, typename D, typename C>
  struct embed_arithmetics : C {
    D operator+(const D& rhs) const { return D(this->value_ref() + rhs.value_ref()); }
    D operator-(const D& rhs) const { return D(this->value_ref() - rhs.value_ref()); }
    D& operator+=(const D& rhs) { this->value_ref() += rhs.value_ref();
    return static_cast<D&>(*this); }
    D& operator-=(const D& rhs) { this->value_ref() -= rhs.value_ref();
    return static_cast<D&>(*this); }
  };

  
  /*
    
  The structure-holding type
  
  */
  
  template <typename T>
  class embed_structure {
  protected:
    embed_structure() {};
    embed_structure(const T& m_value) : m_value(m_value) {}
  public:
    T value() const { return m_value; }
  protected:
    void value_ref(const T& value) { m_value = value; }
    T& value_ref() { return m_value; }
    const T& value_ref() const { return m_value; }
  private:
    T m_value;
  };
  
  /*
    
  The (conglomerate) embedding itself, which expects a policy sequence.
  If no such policy list is provided, "all" structures are preserved
  (and embedded)
  
  */
  
  namespace {

    // TODO: This helper is ugly, please use MPL functions instead
    // to achieve this simple flip of parameters!
    template <typename T, typename D>
    struct create_policy_node {
      template <typename Policy>
      struct apply {
	typedef mpl::bind<Policy, T, D, mpl::_1> type;
      };
    };

    // Map a sequence of policy meta function classes expecting
    // T and D to meta function classes ready to be chained together
    template <typename PolicySeq, typename T, typename D>
    struct create_policy_nodes :
      mpl::transform<PolicySeq, create_policy_node<T, D> >::type {};

  } // namespace anonymous
  
  // All policies enabled, i.e., creating an isomorphism
  
  typedef mpl::vector<policy<embed_order>, policy<embed_equivalence>,
		      policy<embed_conversion>, policy<embed_assignability>,
		      policy<embed_arithmetics> >
  embed_policy_all;

  template<typename T, typename D,
	   typename PolicySeq = embed_policy_all>
  class embed_type :
    // We just have to apply the given policy meta meta functions
    public generate_chain<create_policy_nodes<PolicySeq, T, D>,
			  embed_structure<T> > {
  protected:
    embed_type() {}
    embed_type(const T& value) { this->value_ref(value); };
  };
  
} // namespace boost
  
#endif

