/*
 * Copyright Staffan Gimåker 2009.
 *
 * ---
 *
 * 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 PEEKABOT_ANY_HH_INCLUDED
#define PEEKABOT_ANY_HH_INCLUDED


#include "Visibility.hh"

#include <algorithm>
#include <typeinfo>


namespace peekabot
{
    namespace serialization
    {
        class SerializationInterface;
        class DeserializationInterface;
    }

    /**
     * \internal
     *
     * \brief A Boost.Any-like heterogeneous storage type.
     *
     * This any implementation is inspired by and modeled after Boost.Any.
     * It adds a number of peekabot-specific extensions (serialization support) and
     * fixes some issues with the Boost implementation on GCC (Boost bug #754).
     *
     * To use the serialization support, include "serialization/Any.hh".
     *
     * \par Value type requirements
     *
     * The types used with Any must be default-constructable and
     * copy-constructable.
     *
     * \par Type registration
     *
     * All types that are to be serialized must be registered through the
     * static register_any_type() method, which is analogous to
     * SerializableFactory::register_class(), the register_any_type() is 
     * defined in serialization/Any.hh. For example:
     *
     * \code
     * register_any_type<boost::uint32_t>(10000);
     * register_any_type<bool>(10001);
     * \endcode
     */
    class Any
    {
        template<typename ValueType> friend const ValueType &any_cast(const Any &);
        template<typename ValueType> friend ValueType &any_cast(Any &);

        friend serialization::SerializationInterface &operator<<(
            serialization::SerializationInterface &, const Any &);
        friend serialization::DeserializationInterface &operator>>(
            serialization::DeserializationInterface &, Any &);

        template<typename> friend void register_any_type(boost::uint16_t id);

        struct PEEKABOT_API HolderBase
        {
            virtual HolderBase *clone() = 0;
        };

        template<typename ValueType>
        struct PEEKABOT_API Holder : public HolderBase
        {
            Holder() {}

            Holder(const ValueType &value) : m_held(value) {}

            virtual HolderBase *clone()
            {
                return new Holder<ValueType>(m_held);
            }

            ValueType m_held;

            void save(serialization::SerializationInterface &ar) const
            {
                ar << m_held;
            }

            void load(serialization::DeserializationInterface &ar)
            {
                ar >> m_held;
            }
        };

    public:
        Any() : m_content(0) {}

        template<typename ValueType>
        Any(const ValueType &value)
            : m_content(new Holder<ValueType>(value)) {}

        Any(const Any &other)
            : m_content(other.m_content ? other.m_content->clone() : 0) {}

        ~Any()
        {
            if( m_content )
                delete m_content;
        }

        inline bool empty() const
        {
            return !m_content;
        }

        inline Any &swap(Any &rhs)
        {
            std::swap(m_content, rhs.m_content);
            return *this;
        }

        template<typename ValueType> Any &operator=(const ValueType &rhs)
        {
            Any(rhs).swap(*this);
            return *this;
        }

        Any &operator=(const Any &rhs)
        {
            Any(rhs).swap(*this);
            return *this;
        }

    private:
        HolderBase *m_content;
    };

    struct PEEKABOT_API BadAnyCast : public std::bad_cast
    {
        virtual const char *what() const throw()
        {
            return "Bad any_cast";
        }
    };

    /**
     * \internal
     *
     * \exception BadAnyCast Thrown when the requested and held type are of
     * different types.
     */
    template<typename ValueType> const ValueType &any_cast(const Any &x)
    {
        const Any::Holder<ValueType> *tmp =
            dynamic_cast<const Any::Holder<ValueType> *>(x.m_content);
        if( tmp )
            return tmp->m_held;
        else
            throw BadAnyCast();
    }

    /**
     * \internal
     *
     * \exception BadAnyCast Thrown when the requested and held type are of
     * different types.
     */
    template<typename ValueType> ValueType &any_cast(Any &x)
    {
        Any::Holder<ValueType> *tmp =
            dynamic_cast<Any::Holder<ValueType> *>(x.m_content);
        if( tmp )
            return tmp->m_held;
        else
            throw BadAnyCast();
    }
}


#endif // PEEKABOT_ANY_HH_INCLUDED
