//
//
// The following code is based on
// .../include/boost/serialization/shared_ptr.hpp
// of boost 1.32 and modified to allow smartptr<>s to be
// read/written by boost::serialization
//
// This is the LICENSE_1_0.txt for boost.
//
// Boost Software License - Version 1.0 - August 17th, 2003

// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:

// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
#ifndef _ALREADY_INCLUDED_FERRIS_LOKI_BOOST_EXTENSIONS_H_

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

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// shared_ptr.hpp: serialization for boost shared pointer

// (C) Copyright 2004 Robert Ramey and Martin Ecker
// 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.

#include <map>

#include <boost/config.hpp>
#include <boost/mpl/integral_c.hpp>
#include <boost/mpl/integral_c_tag.hpp>

#include <boost/detail/workaround.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/throw_exception.hpp>

#include <boost/archive/archive_exception.hpp>

#include <boost/serialization/split_free.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/tracking.hpp>
#include <boost/static_assert.hpp>

#include <boost/serialization/extended_type_info.hpp>
#include <boost/serialization/type_info_implementation.hpp>
#include <boost/serialization/void_cast.hpp>

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// shared_ptr serialization traits
// version 1 to distinguish from boost 1.32 version. Note: we can only do this
// for a template when the compiler supports partial template specialization

#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION

namespace boost {
    namespace serialization{
        template< class T,
                  template <class> class OP,
                  class CP,
                  template <class> class KP,
                  template <class> class SP >
        struct version< ::Loki::SmartPtr<T,OP, CP, KP, SP> > {  \
            typedef mpl::integral_c_tag tag;
            typedef mpl::int_<1> type;
            BOOST_STATIC_CONSTANT(unsigned int, value = type::value);
        };
        // don't track shared pointers
        template< class T,
                  template <class> class OP,
                  class CP,
                  template <class> class KP,
                  template <class> class SP >
        struct tracking_level< ::Loki::SmartPtr<T,OP, CP, KP, SP> > { 
            typedef mpl::integral_c_tag tag;
            typedef mpl::int_< ::boost::serialization::track_never> type;
            BOOST_STATIC_CONSTANT(int, value = type::value);
        };
    }}
    #define BOOST_SERIALIZATION_SHARED_PTR(T)
#else
#error foo
//     // define macro to let users of these compilers do this
//     #define BOOST_SERIALIZATION_SHARED_PTR(T)                         \
//     BOOST_CLASS_VERSION(                                              \
//         ::boost::shared_ptr< T >,                                     \
//         1                                                             \
//     )                                                                 \
//     BOOST_CLASS_TRACKING(                                             \
//         ::boost::shared_ptr< T >,                                     \
//         ::boost::serialization::track_never                           \
//     )                                                                 \
//     /**/
#endif

namespace boost {
namespace serialization{
namespace detail {

struct null_deleter {
    void operator()(void const *) const {}
};

// template<class T, class U> shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r)
// {
//     return shared_ptr<T>(r, detail::static_cast_tag());
// }
    
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// a common class for holding various types of shared pointers
class loki_smartptr_helper {
    typedef Loki::SmartPtr<void*,
                           Loki::RefCounted,
                           Loki::DisallowConversion,
                           FerrisLoki::FerrisExSmartPointerChecker,
                           Loki::DefaultSPStorage > NullSmartPtr_t;
    typedef std::map<void*, NullSmartPtr_t > collection_type;
//     typedef shared_ptr<void> NullSmartPtr_t;
//     typedef std::map<void*, NullSmartPtr_t > collection_type;
    typedef collection_type::const_iterator iterator_type;
    // list of shared_pointers create accessable by raw pointer. This
    // is used to "match up" shared pointers loaded at diferent
    // points in the archive
    collection_type m_pointers;
    // return a void pointer to the most derived type
    template<class T>
    void * object_identifier(T * t) const {
        const extended_type_info * true_type 
            = type_info_implementation<T>::type::get_derived_extended_type_info(*t);
        // note:if this exception is thrown, be sure that derived pointer
        // is either regsitered or exported.
        if(NULL == true_type)
            boost::throw_exception(
                boost::archive::archive_exception(
                    boost::archive::archive_exception::unregistered_class
                )
            );
        const boost::serialization::extended_type_info * this_type
            = boost::serialization::type_info_implementation<T>::type::get_instance();
        void * vp = void_downcast(*true_type, *this_type, t);
        return vp;
    }
public:
    template< class T,
              template <class> class OP,
              class CP,
              template <class> class KP,
              template <class> class SP >
    void reset( Loki::SmartPtr<T,OP, CP, KP, SP> & s, T * r){
        if(NULL == r){
            s = 0;
            return;
        }
        // get pointer to the most derived object.  This is effectively
        // the object identifer
        void * od = object_identifier(r);

        iterator_type it = m_pointers.find(od);

//        std::cerr << "reset() r:" << (void*)r << std::endl;
        
        if(it == m_pointers.end()){

//            std::cerr << "     adding r:" << (void*)r << std::endl;
            s = r;

//             void* vp = GetImpl(s);
//             NullSmartPtr_t np;
//             np.reset( vp );
//             m_pointers.insert(collection_type::value_type(od, np ));
            
            void** doouble_void_ptr = (void**)(GetImpl(s));
            NullSmartPtr_t doouble_void_smartptr = doouble_void_ptr;
            doouble_void_smartptr.Clone( 0 );
            m_pointers.insert(collection_type::value_type(od, doouble_void_smartptr ));
        }
        else{

//             T* tmp = static_pointer_cast<T>( (*it).second );
//             s = tmp;
            
            
            void* vp = (void*)GetImpl((*it).second);
            Loki::SmartPtr<T,OP, CP, KP, SP> tmp( (T*)vp );
            s = tmp;
        }
    }
    virtual ~loki_smartptr_helper(){}
};

} // namespace detail

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// utility function for creating/getting a helper - could be useful in general
// but shared_ptr is the only class (so far that needs it) and I don't have a
// convenient header to place it into.
template<class Archive, class T>
T &
get_helper(Archive & ar){
    extended_type_info * eti = type_info_implementation<T>::type::get_instance();
    shared_ptr<void> sph;
    ar.lookup_helper(eti, sph);
    if(NULL == sph.get()){
        sph = shared_ptr<T>(new T);
        ar.insert_helper(eti, sph);
    }
    return * static_cast<T *>(sph.get());
}

#if 0
template<class Archive, class H>
H &
get_helper(Archive & ar){
    shared_ptr<H> sph;
    ar.lookup_helper(sph);
    if(NULL == sph.get()){
        sph = shared_ptr<H>(new H);
        ar.insert_helper(sph);
    }
    return * static_cast<T *>(sph.get());
}
#endif

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// serialization for shared_ptr

template<class Archive,
         class T,
         template <class> class OP,
         class CP,
         template <class> class KP,
         template <class> class SP >
inline void save(
    Archive & ar,
    const Loki::SmartPtr<T,OP, CP, KP, SP> &t,
    const unsigned int /* file_version */
){
    // The most common cause of trapping here would be serializing
    // something like shared_ptr<int>.  This occurs because int
    // is never tracked by default.  Wrap int in a trackable type
    BOOST_STATIC_ASSERT((tracking_level<T>::value != track_never));
    const T * t_ptr = GetImpl( t );
    ar << boost::serialization::make_nvp("px", t_ptr);
}
    
template<class Archive,
         class T,
         template <class> class OP,
         class CP,
         template <class> class KP,
         template <class> class SP >
inline void load(
    Archive & ar,
    Loki::SmartPtr<T,OP, CP, KP, SP> &t,
    const unsigned int file_version
){
    // The most common cause of trapping here would be serializing
    // something like shared_ptr<int>.  This occurs because int
    // is never tracked by default.  Wrap int in a trackable type
    BOOST_STATIC_ASSERT((tracking_level<T>::value != track_never));
    T* r;
    {
            ar >> boost::serialization::make_nvp("px", r);
    }
    get_helper<Archive, detail::loki_smartptr_helper >(ar).reset(t,r);
}

    template<class Archive,
             class T,
             template <class> class OP,
             class CP,
             template <class> class KP,
             template <class> class SP >
    inline void serialize(
        Archive & ar,
        Loki::SmartPtr<T,OP, CP, KP, SP> &t,
        const unsigned int file_version
        ){
        // correct shared_ptr serialization depends upon object tracking
        // being used.
        BOOST_STATIC_ASSERT(
            boost::serialization::tracking_level<T>::value
            != boost::serialization::track_never
            );
        boost::serialization::split_free(ar, t, file_version);
    }
    

} // namespace serialization
} // namespace boost

#endif
