//  (C) Copyright Jeremy Siek 2004 

//  Distributed under the Boost Software License, Version 1.0. (See

//  accompanying file LICENSE_1_0.txt or copy at

//  http://www.boost.org/LICENSE_1_0.txt)



#ifndef BOOST_DETAIL_PROPERTY_HPP

#define BOOST_DETAIL_PROPERTY_HPP



#include <utility> // for std::pair

#include <boost/type_traits/same_traits.hpp> // for is_same



namespace boost {



  namespace detail {



    template <class PropertyTag1, class PropertyTag2>

    struct same_property {

      enum { value = is_same<PropertyTag1,PropertyTag2>::value };

    };



    struct error_property_not_found { };



    template <int TagMatched>

    struct property_value_dispatch {

      template <class PropertyTag, class T, class Tag>

      inline static T& get_value(PropertyTag& p, T*, Tag) {

        return p.m_value; 

      }

      template <class PropertyTag, class T, class Tag>

      inline static const T& const_get_value(const PropertyTag& p, T*, Tag) {

        return p.m_value; 

      }

    };



    template <class PropertyList>

    struct property_value_end {

      template <class T> struct result { typedef T type; };



      template <class T, class Tag>

      inline static T& get_value(PropertyList& p, T* t, Tag tag) {

        typedef typename PropertyList::next_type Next;

        typedef typename Next::tag_type Next_tag;

        enum { match = same_property<Next_tag,Tag>::value };

        return property_value_dispatch<match>

          ::get_value(static_cast<Next&>(p), t, tag);

      }

      template <class T, class Tag>

      inline static const T& const_get_value(const PropertyList& p, T* t, Tag tag) {

        typedef typename PropertyList::next_type Next;

        typedef typename Next::tag_type Next_tag;

        enum { match = same_property<Next_tag,Tag>::value };

        return property_value_dispatch<match>

          ::const_get_value(static_cast<const Next&>(p), t, tag);

      }

    };

    template <>

    struct property_value_end<no_property> {

      template <class T> struct result { 

        typedef detail::error_property_not_found type; 

      };



      // Stop the recursion and return error

      template <class T, class Tag>

      inline static detail::error_property_not_found&

      get_value(no_property&, T*, Tag) {

        static error_property_not_found s_prop_not_found;

        return s_prop_not_found;

      }

      template <class T, class Tag>

      inline static const detail::error_property_not_found&

      const_get_value(const no_property&, T*, Tag) {

        static error_property_not_found s_prop_not_found;

        return s_prop_not_found;

      }

    };



    template <>

    struct property_value_dispatch<0> {

      template <class PropertyList, class T, class Tag>

      inline static typename property_value_end<PropertyList>::template result<T>::type&

      get_value(PropertyList& p, T* t, Tag tag) {

        return property_value_end<PropertyList>::get_value(p, t, tag);

      }

      template <class PropertyList, class T, class Tag>

      inline static const typename property_value_end<PropertyList>::template result<T>::type&

      const_get_value(const PropertyList& p, T* t, Tag tag) {

        return property_value_end<PropertyList>::const_get_value(p, t, tag);

      }

    };



    template <class PropertyList>

    struct build_property_tag_value_alist

    {

      typedef typename PropertyList::next_type NextProperty;

      typedef typename PropertyList::value_type Value;

      typedef typename PropertyList::tag_type Tag;

      typedef typename build_property_tag_value_alist<NextProperty>::type Next;

      typedef std::pair< std::pair<Tag,Value>, Next> type;

    };

    template <>

    struct build_property_tag_value_alist<no_property>

    {

      typedef no_property type;

    };



#if !defined BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION

    template <class TagValueAList, class Tag>

    struct extract_value {

      typedef error_property_not_found type;

    };

    template <class Value, class Tag1, class Tag2, class Rest>

    struct extract_value< std::pair<std::pair<Tag1,Value>,Rest>, Tag2> {

      typedef typename extract_value<Rest,Tag2>::type type;

    };

    template <class Value, class Tag, class Rest>

    struct extract_value< std::pair<std::pair<Tag,Value>,Rest>, Tag> {

      typedef Value type;

    };

#else

    // VC++ workaround:

    // The main idea here is to replace partial specialization with

    // nested template member classes. Of course there is the

    // further complication that the outer class of the nested

    // template class cannot itself be a template class.

    // Hence the need for the ev_selector. -JGS



    struct recursive_extract;

    struct end_extract;



    template <class TagValueAList>

    struct ev_selector { typedef recursive_extract type; };

    template <>

    struct ev_selector<no_property> { typedef end_extract type; };



    struct recursive_extract {

      template <class TagValueAList, class Tag1>

      struct bind_ {

        typedef typename TagValueAList::first_type AListFirst;

        typedef typename AListFirst::first_type Tag2;

        typedef typename AListFirst::second_type Value;

        enum { match = same_property<Tag1,Tag2>::value };

        typedef typename TagValueAList::second_type Next;

        typedef typename ev_selector<Next>::type Extractor;

        typedef typename boost::ct_if< match, Value, 

          typename Extractor::template bind_<Next,Tag1>::type

        >::type type;

      };

    };

    struct end_extract {

      template <class AList, class Tag1>

      struct bind_ {

        typedef error_property_not_found type;

      };

    };

#endif //!defined BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION



  } // namespace detail 

} // namespace boost



#endif // BOOST_DETAIL_PROPERTY_HPP

