/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// void_cast.cpp: implementation of run-time casting of void pointers

// (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)
// <gennadiy.rozental@tfn.com>

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

#if (defined _MSC_VER) && (_MSC_VER == 1200)
# pragma warning (disable : 4786) // too long name, harmless warning
#endif

#include <cassert>
#include <cstddef> // NULL
#ifdef BOOST_SERIALIZATION_LOG
#include <iostream>
#endif

// STL
#include <set>
#include <functional>
#include <algorithm>
#include <cassert>

// BOOST
#define BOOST_SERIALIZATION_SOURCE
#include <boost/serialization/singleton.hpp>
#include <boost/serialization/extended_type_info.hpp>
#include <boost/serialization/void_cast.hpp>

namespace boost { 
namespace serialization {
namespace void_cast_detail {

// note that void_casters are keyed on value of
// member extended type info records - NOT their
// addresses.  This is necessary in order for the
// void cast operations to work across dll and exe
// module boundries.
bool void_caster::operator<(const void_caster & rhs) const {
    // include short cut to save time and eliminate
    // problems when when base class aren't virtual
    if(m_derived != rhs.m_derived){
        if(*m_derived < *rhs.m_derived)
            return true;
        if(*rhs.m_derived < *m_derived)
            return false;
    }
    // m_derived == rhs.m_derived
    if(m_base != rhs.m_base)
        return *m_base < *rhs.m_base;
    else
        return false;
}

struct void_caster_compare {
    bool operator()(const void_caster * lhs, const void_caster * rhs) const {
        return *lhs < *rhs;
    }
};

typedef std::set<const void_caster *, void_caster_compare> set_type;
typedef boost::serialization::singleton<set_type> void_caster_registry;

#ifdef BOOST_MSVC
#  pragma warning(push)
#  pragma warning(disable : 4511 4512)
#endif

// implementation of shortcut void caster
class void_caster_shortcut : public void_caster
{
    bool m_includes_virtual_base;

    void const * 
    vbc_upcast(
        void const * const t
    ) const;
    void const *
    vbc_downcast(
        void const * const t
    ) const;
    virtual void const *
    upcast(void const * const t) const{
        if(m_includes_virtual_base)
            return vbc_upcast(t);
        return static_cast<const char *> ( t ) - m_difference;
    }
    virtual void const *
    downcast(void const * const t) const{
        if(m_includes_virtual_base)
            return vbc_downcast(t);
        return static_cast<const char *> ( t ) + m_difference;
    }
    virtual bool is_shortcut() const {
        return true;
    }
    virtual bool has_virtual_base() const {
        return m_includes_virtual_base;
    }
public:
    void_caster_shortcut(
        extended_type_info const * derived,
        extended_type_info const * base,
        std::ptrdiff_t difference,
        bool includes_virtual_base,
        void_caster const * const parent
    ) :
        void_caster(derived, base, difference, parent),
        m_includes_virtual_base(includes_virtual_base)
    {
        recursive_register(includes_virtual_base);
    }
    virtual ~void_caster_shortcut(){
        recursive_unregister();
    }
};

#ifdef BOOST_MSVC
#  pragma warning(pop)
#endif

void const * 
void_caster_shortcut::vbc_downcast(
    void const * const t
) const {
    // try to find a chain that gives us what we want
    const void_cast_detail::set_type & s
        = void_cast_detail::void_caster_registry::get_const_instance();
    void_cast_detail::set_type::const_iterator it;
    for(it = s.begin(); it != s.end(); ++it){
        // if the current candidate casts to the desired target type
        if ((*it)->m_derived == m_derived){
            // and if it's not us
            if ((*it)->m_base != m_base){
                // try to cast from the candidate base to our base
                const void * t_new;
                t_new = void_downcast(*(*it)->m_base, *m_base, t);
                // if we were successful
                if(NULL != t_new){
                    // recast to our derived
                    const void_caster * vc = *it;
                    return vc->downcast(t_new);
                }
            }
        }
    }
    return NULL;
}

void const * 
void_caster_shortcut::vbc_upcast(
    void const * const t
) const {
    // try to find a chain that gives us what we want
    const void_cast_detail::set_type & s
        = void_cast_detail::void_caster_registry::get_const_instance();
    void_cast_detail::set_type::const_iterator it;
    for(it = s.begin(); it != s.end(); ++it){
        // if the current candidate casts from the desired base type
        if((*it)->m_base == m_base){
            // and if it's not us
            if ((*it)->m_derived != m_derived){
                // try to cast from the candidate derived to our our derived
                const void * t_new;
                t_new = void_upcast(*m_derived, *(*it)->m_derived, t);
                if(NULL != t_new)
                    return (*it)->upcast(t_new);
            }
        }
    }
    return NULL;
}

#ifdef BOOST_MSVC
#  pragma warning(push)
#  pragma warning(disable : 4511 4512)
#endif

// just used as a search key
class void_caster_argument : public void_caster
{
    virtual void const *
    upcast(void const * const /*t*/) const {
        assert(false);
        return NULL;
    }
    virtual void const *
    downcast( void const * const /*t*/) const {
        assert(false);
        return NULL;
    }
    virtual bool has_virtual_base() const {
        assert(false);
        return false;
    }
public:
    void_caster_argument(
        extended_type_info const * derived,
        extended_type_info const * base
    ) :
        void_caster(derived, base)
    {}
    virtual ~void_caster_argument(){};
};

#ifdef BOOST_MSVC
#  pragma warning(pop)
#endif

// implementation of void caster base class
BOOST_SERIALIZATION_DECL(void)
void_caster::recursive_register(bool includes_virtual_base) const {
    void_cast_detail::set_type & s
        = void_cast_detail::void_caster_registry::get_mutable_instance();

    #ifdef BOOST_SERIALIZATION_LOG
    std::clog << "recursive_register\n";
    std::clog << m_derived->get_debug_info();
    std::clog << "<-";
    std::clog << m_base->get_debug_info();
    std::clog << "\n";
    #endif

    std::pair<void_cast_detail::set_type::const_iterator, bool> result;
    result = s.insert(this);
    assert(result.second);

    // generate all implied void_casts.
    void_cast_detail::set_type::const_iterator it;
    for(it = s.begin(); it != s.end(); ++it){
        if(* m_derived == * (*it)->m_base){
            const void_caster_argument vca(
                (*it)->m_derived, 
                m_base
            );
            void_cast_detail::set_type::const_iterator i;
            i = s.find(& vca);
            if(i == s.end()){
                new void_caster_shortcut(
                    (*it)->m_derived, 
                    m_base,
                    m_difference + (*it)->m_difference,
                    (*it)->has_virtual_base() || includes_virtual_base,
                    this
                );
            }
        }
        if(* (*it)->m_derived == * m_base){
            const void_caster_argument vca(
                m_derived, 
                (*it)->m_base
            );
            void_cast_detail::set_type::const_iterator i;
            i = s.find(& vca);
            if(i == s.end()){
                new void_caster_shortcut(
                    m_derived, 
                    (*it)->m_base, 
                    m_difference + (*it)->m_difference,
                    (*it)->has_virtual_base() || includes_virtual_base,
                    this
                );
            }
        }
    }
}

BOOST_SERIALIZATION_DECL(void)
void_caster::recursive_unregister() const {
    if(void_caster_registry::is_destroyed())
        return;

    #ifdef BOOST_SERIALIZATION_LOG
    std::clog << "recursive_unregister\n";
    std::clog << m_derived->get_debug_info();
    std::clog << "<-";
    std::clog << m_base->get_debug_info();
    std::clog << "\n";
    #endif

    void_cast_detail::set_type & s 
        = void_caster_registry::get_mutable_instance();

    // delete all shortcuts which use this primitive
    void_cast_detail::set_type::iterator it;
    for(it = s.begin(); it != s.end();){
        const void_caster * vc = *it;
        if(vc->m_parent == this){
            s.erase(it);
            delete vc;
            it = s.begin();
        }
        else
            it++;
    }

    // delete this guy if he's in there
    it = s.find(this);
    if(it != s.end())
        s.erase(it);
}

} // namespace void_cast_detail

// Given a void *, assume that it really points to an instance of one type
// and alter it so that it would point to an instance of a related type.
// Return the altered pointer. If there exists no sequence of casts that
// can transform from_type to to_type, return a NULL.  
BOOST_SERIALIZATION_DECL(void const *)  
void_upcast(
    extended_type_info const & derived,
    extended_type_info const & base,
    void const * const t
){
    // same types - trivial case
    if (derived == base)
        return t;

    // check to see if base/derived pair is found in the registry
    const void_cast_detail::set_type & s
        = void_cast_detail::void_caster_registry::get_const_instance();
    const void_cast_detail::void_caster_argument ca(& derived, & base);

    void_cast_detail::set_type::const_iterator it;
    it = s.find(& ca);
    if (s.end() != it)
        return (*it)->upcast(t);

    return NULL;
}

BOOST_SERIALIZATION_DECL(void const *)  
void_downcast(
    extended_type_info const & derived,
    extended_type_info const & base,
    void const * const t
){
    // same types - trivial case
    if (derived == base)
        return t;

    // check to see if base/derived pair is found in the registry
    const void_cast_detail::set_type & s
        = void_cast_detail::void_caster_registry::get_const_instance();
    const void_cast_detail::void_caster_argument ca(& derived, & base);

    void_cast_detail::set_type::const_iterator it;
    it = s.find(&ca);
    if (s.end() != it)
        return(*it)->downcast(t);

    return NULL;
}

} // namespace serialization
} // namespace boost
