/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

  Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
  All rights reserved

  Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >

  ------------------------------------------------------------------
  This library is free software;  you  can  redistribute  it  and/or
  modify it  under the  terms  of  the  GNU  Lesser  General  Public
  License as published  by  the  Free  Software  Foundation;  either
  version 3.0 of the License, or (at your option) any later version.

  This library is  distributed in  the hope that it will  be useful,
  but  WITHOUT ANY WARRANTY;  without  even  the implied warranty of
  MERCHANTABILITY  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of  the GNU Lesser General  Public
  License along with this library.

//////////////////////////////////////////////////////////////////////////////*/



#ifndef U_CORE_ARCHIVES_H
#define U_CORE_ARCHIVES_H


#include <boost/archive/detail/basic_pointer_iserializer.hpp>
#include <boost/archive/detail/basic_pointer_oserializer.hpp>

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>

#include <boost/archive/detail/register_archive.hpp>

#include <boost/archive/polymorphic_iarchive.hpp>
#include <boost/archive/polymorphic_oarchive.hpp>
#include <boost/archive/detail/polymorphic_iarchive_route.hpp>
#include <boost/archive/detail/polymorphic_oarchive_route.hpp>

#include <boost/archive/polymorphic_text_oarchive.hpp>

#include "StringReader.h"



////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// FWD DECLARATIONS OF ARCHIVES //




namespace uLib {
namespace Archive {

class xml_iarchive;
class xml_oarchive;
class text_iarchive;
class text_oarchive;
class log_archive;


}
}


namespace boost {
namespace archive {
namespace detail {
template<class ArchiveImplementation> class polymorphic_oarchive_route;
template<class ArchiveImplementation> class polymorphic_iarchive_route;
}
}
}

namespace boost {
namespace serialization {
    template <typename T> struct hrp;
}
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// ARCHIVES REGISTRATION //


namespace uLib {
namespace Archive {
namespace detail {
struct adl_tag {};
}
}
}

namespace boost {
namespace archive {
namespace detail {
// This function gets called, but its only purpose is to participate
// in overload resolution with the functions declared by
// BOOST_SERIALIZATION_REGISTER_ARCHIVE, below.
template <class Serializable>
void instantiate_ptr_serialization(Serializable*, int, uLib::Archive::detail::adl_tag ) {}
}
}
}

// The function declaration generated by this macro never actually
// gets called, but its return type gets instantiated, and that's
// enough to cause registration of serialization functions between
// Archive and any exported Serializable type.  See also:
// boost/serialization/export.hpp
# define ULIB_SERIALIZATION_REGISTER_ARCHIVE(_Archive)                  \
namespace boost { namespace archive { namespace detail {                \
                                                                        \
template <class Serializable>                                           \
BOOST_DEDUCED_TYPENAME _ptr_serialization_support<_Archive, Serializable>::type  \
instantiate_ptr_serialization( Serializable*, _Archive*, uLib::Archive::detail::adl_tag ); }}}






////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// ARCHIVES IO INTERFACES //


namespace boost {
namespace archive {
namespace detail {

/**
 * Custom implementation of boost interface_iarchive to add new operators
 */
template<class Archive>
class uLib_interface_iarchive
{
protected:
    uLib_interface_iarchive(){};
public:
    /////////////////////////////////////////////////////////
    // archive public interface
    typedef mpl::bool_<true> is_loading;
    typedef mpl::bool_<false> is_saving;

    // return a pointer to the most derived class
    Archive * This(){
        return static_cast<Archive *>(this);
    }

    template<class T>
    const basic_pointer_iserializer *
    register_type(T * = NULL){
        const basic_pointer_iserializer & bpis =
            boost::serialization::singleton<
                pointer_iserializer<Archive, T>
            >::get_const_instance();
        this->This()->register_basic_serializer(bpis.get_basic_serializer());
        return & bpis;
    }
    template<class T>
    Archive & operator>>(T & t){
        this->This()->load_override(t, 0);
        return * this->This();
    }

    // the & operator
    template<class T>
    Archive & operator&(T & t){
        return *(this->This()) >> t;
    }

    // the == operator
    template <class T>
    Archive & operator == (T & t) {
        return this->operator &(t);
    }

    // the != operator for human readable access
    template <class T>
    Archive & operator != (T & t) {
        std::cerr << std::flush << "cauch string: " << t << "\n"; // REMOVE THIS !
        return * this->This();
    }
};


/**
 * Custom implementation of boost interface_oarchive to add new operators
 */
template <class Archive>
class uLib_interface_oarchive {
protected:
    uLib_interface_oarchive(){};
public:
    /////////////////////////////////////////////////////////
    // archive public interface
    typedef mpl::bool_<false> is_loading;
    typedef mpl::bool_<true> is_saving;

    // return a pointer to the most derived class
    Archive * This(){
        return static_cast<Archive *>(this);
    }

    template<class T>
    const basic_pointer_oserializer *
    register_type(const T * = NULL){
        const basic_pointer_oserializer & bpos =
            boost::serialization::singleton<
                pointer_oserializer<Archive, T>
            >::get_const_instance();
        this->This()->register_basic_serializer(bpos.get_basic_serializer());
        return & bpos;
    }

    template<class T>
    Archive & operator<<(T & t){
        // to get access you must redefine save_override by typing
        // "using save_override" in archive impl
        this->This()->save_override(t, 0);
        return * this->This();
    }

    // the & operator
    template<class T>
    Archive & operator&(T & t){
        #ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
            return * this->This() << const_cast<const T &>(t);
        #else
            return * this->This() << t;
        #endif
    }

    // the == operator
    template <class T>
    Archive & operator == (T & t) {
        return this->operator &(t);
    }

    // the != operator for human readable access
    template <class T>
    Archive & operator != (T & t) {
        std::cerr << std::flush << "cauch string: " << t << "\n"; // REMOVE THIS !
        return * this->This();
    }
};



// DECLARE INTERFACE SPECIALIZATIONS ///////////////////////////////////////////
// With this declarations all uLib archive Implementation will use their own
// extended interface //

template <>
class interface_iarchive        <uLib::Archive::xml_iarchive> : public
        uLib_interface_iarchive <uLib::Archive::xml_iarchive> {};

template <>
class interface_oarchive        <uLib::Archive::xml_oarchive> : public
        uLib_interface_oarchive <uLib::Archive::xml_oarchive> {};

template <>
class interface_iarchive        <uLib::Archive::text_iarchive> : public
        uLib_interface_iarchive <uLib::Archive::text_iarchive> {};

template <>
class interface_oarchive        <uLib::Archive::text_oarchive> : public
        uLib_interface_oarchive <uLib::Archive::text_oarchive> {};

template <>
class interface_oarchive        <uLib::Archive::log_archive> : public
        uLib_interface_oarchive <uLib::Archive::log_archive> {};


//// Veritical repetition macro // FINIRE !!!!!!!!!!!!!!!!!!!!!!!!!
//#define _DECL_INTERFACE_ARCHIVE_V(vz,vn,vdata)           \
//    template <class TypeSeq>                             \
//    struct inherit_nofold<TypeSeq,BOOST_PP_INC(vn)> :    \
//    BOOST_PP_REPEAT(BOOST_PP_INC(vn),_INERIT_NOFOLD_H,~) \
//    {};

//// Multiple size declaration //
//BOOST_PP_REPEAT(ULIB_CFG_MPL_INERIT_NOFOLD_MAXSIZE,_INERIT_NOFOLD_V,~)

//#undef  _INERIT_NOFOLD_H
//#undef  _INERIT_NOFOLD_V


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













////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// ARCHIVES DEFINITIONS //

namespace boost {
namespace archive {

//template<class Archive>
//inline void load_const_override(Archive & ar, const char *t ){
//    typedef typename mpl::identity<detail::load_non_pointer_type<Archive> >::type typex;
//    typex::invoke(ar, t);
//}

//template<class Archive, class T>
//inline void load(Archive & ar, T &t){
//    // if this assertion trips. It means we're trying to load a
//    // const object with a compiler that doesn't have correct
//    // funtion template ordering.  On other compilers, this is
//    // handled below.
//    //    detail::check_const_loading< T >();
//    typedef
//        BOOST_DEDUCED_TYPENAME mpl::eval_if<is_pointer< T >,
//            mpl::identity<detail::load_pointer_type<Archive> >
//        ,//else
//        BOOST_DEDUCED_TYPENAME mpl::eval_if<is_array< T >,
//            mpl::identity<detail::load_array_type<Archive> >
//        ,//else
//        BOOST_DEDUCED_TYPENAME mpl::eval_if<is_enum< T >,
//            mpl::identity<detail::load_enum_type<Archive> >
//        ,//else
//            mpl::identity<detail::load_non_pointer_type<Archive> >
//        >
//        >
//        >::type typex;
//    typex::invoke(ar, t);
//}



}
}




namespace uLib {

namespace Archive {


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// POLYMORPHIC //


//class polymorphic_iarchive :
//        public boost::archive::polymorphic_iarchive {

//public:
//    void load_override(const char *t, BOOST_PFTO int)
//    {
//        boost::archive::load_const_override(* this->This(), const_cast<char*>(t));
//    }

//};


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// XML //

class xml_iarchive :
    public boost::archive::xml_iarchive_impl<xml_iarchive>,
    public boost::archive::detail::shared_ptr_helper
{
    typedef xml_iarchive Archive;
    typedef boost::archive::xml_iarchive_impl<Archive> base;

    // give serialization implementation access to this class
    friend class boost::archive::detail::interface_iarchive<Archive>;
    friend class boost::archive::basic_xml_iarchive<Archive>;
    friend class boost::archive::load_access;
public:
    xml_iarchive(std::istream & is, unsigned int flags = 0) :
        xml_iarchive_impl<xml_iarchive>(is, flags)
    {}

    using basic_xml_iarchive::load_override;

    // Anything not an attribute should be a name value pair as nvp or hrp
    typedef boost::archive::detail::common_iarchive<Archive> detail_common_iarchive;
    template<class T>
    void load_override(
        #ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
        const
        #endif
        boost::serialization::hrp< T > & t,
        int v
    ){
        this->This()->load_start(t.name());
        this->detail_common_iarchive::load_override(t.value(), 0);
//        t.stov();
        this->This()->load_end(t.name());
    }


    // class_name_type can't be handled here as it depends upon the
    // char type used by the stream.  So require the derived implementation.
    // derived in this case is xml_iarchive_impl or base ..
    using base::load_override;

    void load_override(const char *str, int v) {
        //        StringReader sr(basic_text_iprimitive::is);
        //        sr >> str;
    }

    ~xml_iarchive(){};
};

//typedef boost::archive::detail::polymorphic_iarchive_route<
//boost::archive::xml_iarchive_impl<xml_iarchive>
//> polymorphic_xml_iarchive;

template <class ArchiveImpl>
struct polymorphic_iarchive_route :
        boost::archive::detail::polymorphic_iarchive_route<ArchiveImpl>
{
    virtual void load(const char * t){
        ArchiveImpl::load(t);
    }
};


class polymorphic_xml_iarchive :
        public  polymorphic_iarchive_route< boost::archive::xml_iarchive_impl<xml_iarchive> >
{
    // give serialization implementation access to this class
//    friend class boost::archive::detail::interface_iarchive<Archive>;
//    friend class boost::archive::basic_xml_iarchive<Archive>;
//    friend class boost::archive::load_access;
public:
    virtual void load_override(const char *str, int v) {
        ;
    }
};



class xml_oarchive :
    public boost::archive::xml_oarchive_impl<xml_oarchive>
{
    typedef xml_oarchive Archive;
    typedef boost::archive::xml_oarchive_impl<Archive> base;

    // give serialization implementation access to this class
    friend class boost::archive::detail::interface_oarchive<Archive>;
    friend class boost::archive::basic_xml_oarchive<Archive>;
    friend class boost::archive::save_access;
public:
    xml_oarchive(std::ostream & os, unsigned int flags = 0) :
        boost::archive::xml_oarchive_impl<xml_oarchive>(os, flags)
    {}

    // example of implementing save_override for const char* //
    //    void save_override(const char *t, int) {
    //        std::cout << "found char: " << t << "\n";
    //    }

    using basic_xml_oarchive::save_override;

    // special treatment for name-value pairs.
    typedef boost::archive::detail::common_oarchive<Archive> detail_common_oarchive;
    template<class T>
    void save_override(
        #ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
        const
        #endif
        ::boost::serialization::hrp< T > & t,
        int v
    ){
        this->This()->save_start(t.name());
//        t.vtos();
//        this->detail_common_oarchive::save_override(t.const_value(), 0);
        this->This()->save_end(t.name());
    }

    void save_override(const char *str, int v) {
        // Do not save any human decoration string //
        //        basic_text_oprimitive::save(str);
    }

    ~xml_oarchive(){}
};

//typedef boost::archive::detail::polymorphic_oarchive_route<
//boost::archive::xml_oarchive_impl<xml_oarchive>
//> polymorphic_xml_oarchive;

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// TEXT //

class text_iarchive :
    public boost::archive::text_iarchive_impl<text_iarchive>,
    public boost::archive::detail::shared_ptr_helper
{
    typedef text_iarchive Archive;
    typedef boost::archive::text_iarchive_impl<Archive> base;

    // give serialization implementation access to this class
    friend class boost::archive::detail::interface_iarchive<Archive>;
    friend class boost::archive::basic_text_iarchive<Archive>;
    friend class boost::archive::load_access;
public:
    text_iarchive(std::istream & is, unsigned int flags = 0) :
        text_iarchive_impl<Archive>(is, flags)
    {}

    using basic_text_iarchive::load_override;

    void load_override(boost::archive::object_id_type & t, int)               {}

    // class_name_type can't be handled here as it depends upon the
    // char type used by the stream.  So require the derived implementation.
    // derived in this case is xml_iarchive_impl or base ..
    using base::load_override;

    void load_override(const char *str, int v) {
        StringReader sr(basic_text_iprimitive::is);
        sr >> str;
    }

    ~text_iarchive() {};
};

typedef text_iarchive naked_text_iarchive;

//typedef boost::archive::detail::polymorphic_iarchive_route<
//naked_text_iarchive
//> polymorphic_text_iarchive;



class text_oarchive :
    public boost::archive::text_oarchive_impl<text_oarchive>
{
    typedef text_oarchive Archive;
    typedef boost::archive::text_oarchive_impl<Archive> base;

    // give serialization implementation access to this class
    friend class boost::archive::detail::interface_oarchive<Archive>;
    friend class boost::archive::basic_text_oarchive<Archive>;
    friend class boost::archive::save_access;
public:
    text_oarchive(std::ostream & os, unsigned int flags = 0) :
        boost::archive::text_oarchive_impl<Archive>(os, flags)
    {}

    using basic_text_oarchive::save_override;

    void save_override(const char *str, int v) {
        basic_text_oprimitive::save(str);
    }

    ~text_oarchive(){}
};

//typedef boost::archive::detail::polymorphic_oarchive_route<
//boost::archive::text_oarchive_impl<text_oarchive>
//> polymorphic_text_oarchive;


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// SIMPLE HUMAN READABLE TEXT //

class hrt_iarchive :
    public boost::archive::text_iarchive_impl<hrt_iarchive>,
    public boost::archive::detail::shared_ptr_helper
{
    typedef hrt_iarchive Archive;
    typedef boost::archive::text_iarchive_impl<Archive> base;

    // give serialization implementation access to this class
    friend class boost::archive::detail::interface_iarchive<Archive>;
    friend class boost::archive::basic_text_iarchive<Archive>;
    friend class boost::archive::load_access;
public:
    hrt_iarchive(std::istream & is, unsigned int flags = 0) :
        base(is, flags | boost::archive::no_header )
    {}

    using basic_text_iarchive::load_override;

    // hide all archive props //
    void load_override( boost::archive::object_id_type & t, int)         {}
    void load_override( boost::archive::object_reference_type & t, int)  {}
    void load_override( boost::archive::version_type & t, int)           {}
    void load_override( boost::archive::class_id_type & t, int)          {}
    void load_override( boost::archive::class_id_optional_type & t, int) {}
    void load_override( boost::archive::class_id_reference_type & t, int){}
    void load_override( boost::archive::class_name_type & t, int)        {}
    void load_override( boost::archive::tracking_type & t, int)          {}

    // class_name_type can't be handled here as it depends upon the
    // char type used by the stream.  So require the derived implementation.
    // derived in this case is xml_iarchive_impl or base ..
    using base::load_override;

    void load_override(const char *str, int v) {
        StringReader sr(basic_text_iprimitive::is);
        sr >> str;
    }

    ~hrt_iarchive() {};
};


class hrt_oarchive :
        public boost::archive::text_oarchive_impl<hrt_oarchive>
    {
        typedef hrt_oarchive Archive;
        typedef boost::archive::text_oarchive_impl<Archive> base;

        // give serialization implementation access to this class
        friend class boost::archive::detail::interface_oarchive<Archive>;
        friend class boost::archive::basic_text_oarchive<Archive>;
        friend class boost::archive::save_access;
    public:
        hrt_oarchive(std::ostream & os, unsigned int flags = 0) :
            base(os, flags | boost::archive::no_header )
        {}

        using basic_text_oarchive::save_override;

        void save_override(const boost::archive::object_id_type & t, int)         {}
        void save_override(const boost::archive::object_reference_type & t, int)  {}
        void save_override(const boost::archive::version_type & t, int)           {}
        void save_override(const boost::archive::class_id_type & t, int)          {}
        void save_override(const boost::archive::class_id_optional_type & t, int) {}
        void save_override(const boost::archive::class_id_reference_type & t, int){}
        void save_override(const boost::archive::class_name_type & t, int)        {}
        void save_override(const boost::archive::tracking_type & t, int)          {}


        void save_override(const char *str, int v) {
            basic_text_oprimitive::save(str);
        }

        ~hrt_oarchive(){}
    };




////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// XML FOR LOG OUTPUT PURPOUSE //

/////////////////////////////////////////////////////////////////////////
// log data to an output stream.  This illustrates a simpler implemenation
// of text output which is useful for getting a formatted display of
// any serializable class.  Intended to be useful as a debugging aid.
class log_archive :
    /* protected ? */
    public boost::archive::xml_oarchive_impl<log_archive>
{
    typedef boost::archive::xml_oarchive_impl<log_archive> base;
    typedef log_archive Archive;
    // give serialization implementation access to this clas
    friend class boost::archive::detail::interface_oarchive<log_archive>;
    friend class boost::archive::basic_xml_oarchive<log_archive>;
    friend class boost::archive::save_access;

public:

    void save_override(const char *str, int v) {
        // Do not save any human decoration string //
        //        basic_text_oprimitive::save(str);
    }

    template<class T>
    void save_override(T & t, BOOST_PFTO int){
        base::save_override(boost::serialization::make_nvp(NULL, t), 0);
    }

    // activate this if you want to trap non nvp objects //
    //    template<class T>
    //    void save_override(T & t, BOOST_PFTO int)
    //    {
    //        BOOST_MPL_ASSERT((boost::serialization::is_wrapper< T >));
    //        //        this->detail_common_oarchive::save_override(t, 0);
    //    }

    template<class T>
    void save_override(const boost::serialization::nvp<T> & t, int){
        base::save_override(t, 0);
    }


    // specific overrides for attributes - not name value pairs so we
    // want to trap them before the above "fall through"
    // since we don't want to see these in the output - make them no-ops.
    void save_override(const boost::archive::object_id_type & t, int)         {}
    void save_override(const boost::archive::object_reference_type & t, int)  {}
    void save_override(const boost::archive::version_type & t, int)           {}
    void save_override(const boost::archive::class_id_type & t, int)          {}
    void save_override(const boost::archive::class_id_optional_type & t, int) {}
    void save_override(const boost::archive::class_id_reference_type & t, int){}
    void save_override(const boost::archive::class_name_type & t, int)        {}
    void save_override(const boost::archive::tracking_type & t, int)          {}


public:
    log_archive(std::ostream & os, unsigned int flags = 0) :
        boost::archive::xml_oarchive_impl<log_archive>(
            os,
            flags | boost::archive::no_header
        )
    {}
};

//typedef boost::archive::detail::polymorphic_oarchive_route<
//boost::archive::xml_oarchive_impl<log_archive>
//> polymorphic_log_archive;




} // Archive



} // uLib


ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::xml_iarchive)
ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::xml_oarchive)
ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::text_iarchive)
ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::text_oarchive)
ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::hrt_iarchive)
ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::hrt_oarchive)
ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::log_archive)

//ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::polymorphic_xml_iarchive)
//ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::polymorphic_xml_oarchive)
//ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::polymorphic_text_iarchive)
//ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::polymorphic_text_oarchive)
//ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::polymorphic_log_archive)




#endif // U_CORE_ARCHIVES_H
