
//******************************************************************************
// RCF - Remote Call Framework
// Copyright (c) 2005 - 2009, Jarl Lindrud. All rights reserved.
// Consult your license for conditions of use.
// Version: 1.1
// Contact: jarl.lindrud <at> gmail.com 
//******************************************************************************

#ifndef INCLUDE_RCF_SERIALIZATIONPROTOCOL_HPP
#define INCLUDE_RCF_SERIALIZATIONPROTOCOL_HPP

#include <map>
#include <string>
#include <strstream>

#include <RCF/ByteBuffer.hpp>

// Serialization protocols

#include <RCF/SerializationDefs.hpp>
#include <RCF/Protocol/Protocol.hpp>

#if defined(RCF_USE_SF_SERIALIZATION)
#include <RCF/Protocol/SF.hpp>
#endif

#if defined(RCF_USE_BOOST_SERIALIZATION) || defined(RCF_USE_BOOST_XML_SERIALIZATION)
#include <RCF/Protocol/BoostSerialization.hpp>
#endif

namespace RCF {

    RCF_EXPORT bool isSerializationProtocolSupported(int protocol);

    RCF_EXPORT std::string getSerializationProtocolName(int protocol);

    class PointerContext
    {
    public:
        template<typename SmartPtr, typename T>
        SmartPtr get(SmartPtr *, T t)
        {
            // TODO: need to use typeid().name()
            return static_cast<SmartPtr>(
                mPtrMap[&typeid(SmartPtr)][ static_cast<void *>(t) ] );
        }

        template<typename SmartPtr, typename T>
        void set(SmartPtr sp, T t)
        {
            mPtrMap[&typeid(SmartPtr)][ static_cast<void *>(t) ] =
                static_cast<void *>(sp);
        }

        void clear()
        {
            mPtrMap.clear();
        }

    private:
        std::map< const std::type_info *, std::map<void*, void*> > mPtrMap;
    };


    class Token;
    class MethodInvocationRequest;
    class MethodInvocationResponse;

    class RCF_EXPORT SerializationProtocolIn
    {
    public:
        SerializationProtocolIn();
        ~SerializationProtocolIn();

        void            setSerializationProtocol(int protocol);
        int             getSerializationProtocol() const;
        void            reset(const ByteBuffer &data, int protocol);
        void            clearByteBuffer();
        void            clear();
        void            extractSlice(ByteBuffer &byteBuffer, std::size_t len);
        std::size_t     getArchiveLength();
        std::size_t     getRemainingArchiveLength();

        template<typename T>
        void read(const T *pt)
        {
            read(*pt);
        }

        template<typename T>
        void read(T &t)
        {
            try
            {
                switch (mProtocol)
                {
                case 1: mInProtocol1 >> t; break;
                case 2: mInProtocol2 >> t; break;
                case 3: mInProtocol3 >> t; break;
                case 4: mInProtocol4 >> t; break;

#ifdef RCF_USE_BOOST_XML_SERIALIZATION
                case 5: mInProtocol5 >> boost::serialization::make_nvp("Dummy", t); break;
#else
                case 5: mInProtocol5 >> t; break;
#endif

                default: RCF_ASSERT(0)(mProtocol);
                }
            }
            catch(const RCF::Exception &e)
            {
                RCF_UNUSED_VARIABLE(e);
                throw;
            }
            catch(const std::exception &e)
            {
                std::ostringstream os;
                os
                    << "Deserialization error, object type: "
                    << typeid(t).name()
                    << ", error type: "
                    << typeid(e).name()
                    << ", error msg: "
                    << e.what();

                RCF_THROW(
                    RCF::SerializationException(
                        RcfError_Deserialization,
                        os.str()));
            }
        }

        PointerContext mPointerContext;

    private:

        void            bindProtocol();
        void            unbindProtocol();

        friend class ClientStub; // TODO
        friend class RcfSession; // TODO

        int                                     mProtocol;
        ByteBuffer                              mByteBuffer;
        std::istrstream *                       mIsPtr;
        std::vector<char>                       mIstrVec;

        Protocol< boost::mpl::int_<1> >::In     mInProtocol1;
        Protocol< boost::mpl::int_<2> >::In     mInProtocol2;
        Protocol< boost::mpl::int_<3> >::In     mInProtocol3;
        Protocol< boost::mpl::int_<4> >::In     mInProtocol4;
        Protocol< boost::mpl::int_<5> >::In mInProtocol5;
    };

    class RCF_EXPORT SerializationProtocolOut
    {
    public:
        SerializationProtocolOut();

        void    setSerializationProtocol(int protocol);
        int     getSerializationProtocol() const;
        void    clear();
        void    reset(
                    int protocol,
                    std::size_t margin = 32,
                    ByteBuffer byteBuffer = ByteBuffer());

        template<typename T>
        void    write(const T &t)
        {
            try
            {
                switch (mProtocol)
                {
                case 1: mOutProtocol1 << t; break;
                case 2: mOutProtocol2 << t; break;
                case 3: mOutProtocol3 << t; break;
                case 4: mOutProtocol4 << t; break;

#ifdef RCF_USE_BOOST_XML_SERIALIZATION
                case 5: mOutProtocol5 << boost::serialization::make_nvp("Dummy", t); break;
#else
                case 5: mOutProtocol5 << t; break;
#endif

                default: RCF_ASSERT(0)(mProtocol);
                }
            }
            catch(const std::exception &e)
            {
                std::ostringstream os;
                os
                    << "serialization error, object type: "
                    << typeid(t).name()
                    << ", error type: "
                    << typeid(e).name()
                    << ", error msg: "
                    << e.what();

                RCF_THROW(
                    RCF::SerializationException(
                        RcfError_Serialization,
                        os.str()));
            }
        }

        void    insert(const ByteBuffer &byteBuffer);
        void    extractByteBuffers();
        void    extractByteBuffers(std::vector<ByteBuffer> &byteBuffers);

    private:

        void    bindProtocol();
        void    unbindProtocol();


        friend class ClientStub; // TODO
        friend class RcfSession; // TODO
        
        int                                                 mProtocol;
        std::size_t                                         mMargin;
        boost::shared_ptr<std::ostrstream>                  mOsPtr;
        std::vector<std::pair<std::size_t, ByteBuffer> >    mByteBuffers;

        // these need to be below mOsPtr, for good order of destruction
        Protocol< boost::mpl::int_<1> >::Out                mOutProtocol1;
        Protocol< boost::mpl::int_<2> >::Out                mOutProtocol2;
        Protocol< boost::mpl::int_<3> >::Out                mOutProtocol3;
        Protocol< boost::mpl::int_<4> >::Out                mOutProtocol4;
        Protocol< boost::mpl::int_<5> >::Out                mOutProtocol5;
    };

    inline void serializeImpl(
        SerializationProtocolOut &,
        const Void &,
        long int)
    {
    }

    inline void deserializeImpl(
        SerializationProtocolIn &,
        Void &,
        long int)
    {
    }

    template<typename T>
    void serializeImpl(
        SerializationProtocolOut &out,
        const T &t,
        long int)
    {
        out.write(t);
    }

    template<typename T>
    void deserializeImpl(
        SerializationProtocolIn &in,
        T &t,
        long int)
    {
        in.read(t);
    }

    template<typename T>
    void serialize(
        SerializationProtocolOut &out,
        const T &t)
    {
        serializeImpl(out, t, 0);
    }

    template<typename T>
    void deserialize(
        SerializationProtocolIn &in,
        T &t)
    {
        deserializeImpl(in, t, 0);
    }


} // namespace RCF

#endif // ! INCLUDE_RCF_SERIALIZATIONPROTOCOL_HPP
