#ifndef BOOST_SERIALIZATION_EXPORT_HPP
#define BOOST_SERIALIZATION_EXPORT_HPP

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// export.hpp: set traits of classes to be serialized

// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . 
// Use, modification and distribution is subject to 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)

//  See http://www.boost.org for updates, documentation, and revision history.

// implementation of class export functionality.  This is an alternative to
// "forward declaration" method to provoke instantiation of derived classes
// that are to be serialized through pointers.

#include <dkutil/boost/config.hpp>

#include <dkutil/boost/static_assert.hpp>
#include <dkutil/boost/preprocessor/stringize.hpp>
#include <dkutil/boost/mpl/apply_if.hpp>
#include <dkutil/boost/mpl/or.hpp>
#include <dkutil/boost/mpl/empty.hpp>
#include <dkutil/boost/mpl/front.hpp>
#include <dkutil/boost/mpl/pop_front.hpp>
#include <dkutil/boost/mpl/void.hpp>
#include <dkutil/boost/mpl/identity.hpp>

#include <dkutil/boost/serialization/force_include.hpp>
#include <dkutil/boost/serialization/extended_type_info.hpp>
#include <dkutil/boost/serialization/type_info_implementation.hpp>

#include <dkutil/boost/archive/detail/known_archive_types.hpp>

namespace boost {

namespace archive {
namespace detail {

// forward template declarations
class basic_pointer_iserializer;
template<class Archive, class T>
const basic_pointer_iserializer &
instantiate_pointer_iserializer(Archive * ar, T *);

class basic_pointer_oserializer;
template<class Archive, class T>
const basic_pointer_oserializer &
instantiate_pointer_oserializer(Archive * ar, T *);

namespace export_impl
{
    struct nothing{
        static void instantiate(){}
    };

    template<class Archive, class T>
    struct archive {
        struct i {
            static void invoke(){
        		instantiate_pointer_iserializer(
        			static_cast<Archive *>(NULL),
        			static_cast<T *>(NULL)
        		);
        	}
        };
        struct o {
            static void invoke(){
        		instantiate_pointer_oserializer(
        			static_cast<Archive *>(NULL),
        			static_cast<T *>(NULL)
        		);
        	}
        };
        static void instantiate(){
        	BOOST_STATIC_ASSERT(
        		Archive::is_loading::value || Archive::is_saving::value
        	);
            mpl::apply_if<
                BOOST_DEDUCED_TYPENAME Archive::is_saving,
                mpl::identity<o>,
            // else
            mpl::apply_if<
        		BOOST_DEDUCED_TYPENAME Archive::is_loading,
                mpl::identity<i>,
            // else
                mpl::identity<nothing>
            > >::type::invoke();
        }
    };

    template<class ASeq, class T>
    struct for_each_archive {
    private:
        typedef BOOST_DEDUCED_TYPENAME mpl::pop_front<ASeq>::type tail;
        typedef BOOST_DEDUCED_TYPENAME mpl::front<ASeq>::type head;
    public:
        static void instantiate(){
            archive<head, T>::instantiate();
            mpl::apply_if<
                mpl::empty<tail>,
                mpl::identity<nothing>,
                mpl::identity<for_each_archive<tail, T> >
            >::type::instantiate();
        }
    };

} // namespace export_impl

template<class T, class ASeq>
struct export_generator {
    export_generator(){
        export_impl::for_each_archive<ASeq, T>::instantiate();
    }
    static const export_generator instance;
};

template<class T, class ASeq>
const export_generator<T, ASeq> 
    export_generator<T, ASeq>::instance;

// instantiation of this template creates a static object.
template<class T, class ASeq>
struct guid_initializer {
    struct empty {
        static void key_register(const char *key){}
    };
    struct non_empty {
        typedef BOOST_DEDUCED_TYPENAME boost::serialization::type_info_implementation<T>::type eti_type;
        static void key_register(const char *key){
        	boost::serialization::extended_type_info * eti = eti_type::get_instance();
        	eti->key_register(key);
        }
    };
    static const guid_initializer instance;
    guid_initializer(const char *key){
        typedef BOOST_DEDUCED_TYPENAME mpl::apply_if<
        	mpl::empty<ASeq>,
        	mpl::identity<empty>,
        	mpl::identity<non_empty>
        >::type typex;
        typex::key_register(key);
    }
};

template<class T, class ASeq>
const guid_initializer<T, ASeq> guid_initializer<T, ASeq>::instance;

template<class T, class ASeq>
BOOST_FORCE_INCLUDE const guid_initializer<T, ASeq> & 
boost_template_instantiate(T &, ASeq &){
    return guid_initializer<T, ASeq>::instance;
}

} // namespace detail
} // namespace archive
} // namespace boost

// if no archive headers have been included this is a no op
// this is to permit BOOST_EXPORT etc to be included in a 
// file declaration header
#if ! defined(BOOST_ARCHIVE_EXPORT)
    #define BOOST_CLASS_EXPORT_GUID_ARCHIVE_LIST(T, K, ASEQ)

// in my view either of these should be ok 
// but this one fails with gcc
#elif defined(BOOST_MSVC) || defined(__BORLANDC__)
#define BOOST_CLASS_EXPORT_GUID_ARCHIVE_LIST(T, K, ASEQ)         \
    namespace boost { namespace archive { namespace detail {     \
    template<>                                                   \
    const export_generator<T, ASEQ >                             \
        export_generator<T, ASEQ >::instance;                    \
    template<>                                                   \
    const guid_initializer<T, ASEQ>                              \
        guid_initializer<T, ASEQ>::instance(K);                  \
    template                                                     \
    BOOST_FORCE_INCLUDE const guid_initializer<T, ASEQ> &        \
    boost_template_instantiate(T &, ASEQ &);                     \
    } } }                                                        \
    /**/
#else
// and this one fails with VC and BORLAND
#define BOOST_CLASS_EXPORT_GUID_ARCHIVE_LIST(T, K, ASEQ)         \
    namespace boost { namespace archive { namespace detail {     \
    template                                                     \
    const export_generator<T, ASEQ >                             \
        export_generator<T, ASEQ >::instance;                    \
    template                                                     \
    const guid_initializer<T, ASEQ >                             \
	guid_initializer<T, ASEQ >::instance(K) ;                    \
    } } }                                                        \
    /**/
#endif

// check for unnecessary export.  T isn't polymorphic so there is no 
// need to export it.
#define BOOST_CLASS_EXPORT_CHECK(T)                              \
    BOOST_STATIC_WARNING(                                        \
        boost::serialization::type_info_implementation<T>        \
        	::type::is_polymorphic::value                        \
    );                                                           \
    /**/

// the default list of archives types for which code id generated
#define BOOST_CLASS_EXPORT_GUID(T, K)                            \
    BOOST_CLASS_EXPORT_GUID_ARCHIVE_LIST(                        \
        T,                                                       \
        K,                                                       \
        boost::archive::detail::known_archive_types<false>::type \
    )                                                            \
    /**/

// the default exportable class identifier is the class name
#define BOOST_CLASS_EXPORT_ARCHIVE_LIST(T, ASEQ)   \
    BOOST_CLASS_EXPORT_GUID_ARCHIVE_LIST(T, BOOST_PP_STRINGIZE(T), A)

// the default exportable class identifier is the class name
// the default list of archives types for which code id generated
// are the originally included with this serialization system
#define BOOST_CLASS_EXPORT(T)                                    \
    BOOST_CLASS_EXPORT_GUID_ARCHIVE_LIST(                        \
        T,                                                       \
        BOOST_PP_STRINGIZE(T),                                   \
        boost::archive::detail::known_archive_types<false>::type \
    )
    /**/

#endif // BOOST_SERIALIZATION_EXPORT_HPP
