// (C) Copyright 2005 Matthias Troyer



// 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)



//  Authors: Matthias Troyer



#ifndef BOOST_MPI_DETAIL_MPI_DATATYPE_OPRIMITIVE_HPP

#define BOOST_MPI_DETAIL_MPI_DATATYPE_OPRIMITIVE_HPP



#include <boost/mpi/config.hpp>

#include <cstddef> // size_t



#include <boost/config.hpp>

#if defined(BOOST_NO_STDC_NAMESPACE)

namespace std{

    using ::size_t;

} // namespace std

#endif



#include <boost/mpi/datatype_fwd.hpp>

#include <boost/mpi/exception.hpp>

#include <boost/throw_exception.hpp>

#include <boost/assert.hpp>

#include <boost/mpl/placeholders.hpp>

#include <boost/serialization/array.hpp>

#include <boost/serialization/detail/get_data.hpp>

#include <stdexcept>

#include <iostream>

#include <vector>



namespace boost { namespace mpi { namespace detail {



/////////////////////////////////////////////////////////////////////////

// class mpi_data_type_oprimitive - creation of custom MPI data types



class mpi_datatype_primitive

{

public:



    // trivial default constructor

    mpi_datatype_primitive()

     : is_committed(false),

       origin(0)

    {}



    mpi_datatype_primitive(void const* orig)

     : is_committed(false),

       origin()

    {

      BOOST_MPI_CHECK_RESULT(MPI_Address,(const_cast<void*>(orig), &origin));

    }



    void save_binary(void const *address, std::size_t count)

    {

      save_impl(address,MPI_BYTE,count);

    }



    // fast saving of arrays of MPI types

    template<class T>

    void save_array(serialization::array<T> const& x, unsigned int /* version */)

    {

      if (x.count())

        save_impl(x.address(), boost::mpi::get_mpi_datatype(*x.address()), x.count());

    }



    typedef is_mpi_datatype<mpl::_1> use_array_optimization;



    // create and return the custom MPI data type

    MPI_Datatype get_mpi_datatype()

    {

      if (!is_committed)

      {

        BOOST_MPI_CHECK_RESULT(MPI_Type_struct,

                    (

                      addresses.size(),

                      boost::serialization::detail::get_data(lengths),

                      boost::serialization::detail::get_data(addresses),

                      boost::serialization::detail::get_data(types),

                      &datatype_

                    ));



        BOOST_MPI_CHECK_RESULT(MPI_Type_commit,(&datatype_));



        is_committed = true;

      }



      return datatype_;

    }



    // default saving of primitives.

    template<class T>

    void save(const T & t)

    {

        save_impl(&t, boost::mpi::get_mpi_datatype(t), 1);

    }



private:



    void save_impl(void const * p, MPI_Datatype t, int l)

    {

      BOOST_ASSERT ( !is_committed );



      // store address, type and length



      MPI_Aint a;

      BOOST_MPI_CHECK_RESULT(MPI_Address,(const_cast<void*>(p), &a));



      addresses.push_back(a-origin);

      types.push_back(t);

      lengths.push_back(l);

    }



    std::vector<MPI_Aint> addresses;

    std::vector<MPI_Datatype> types;

    std::vector<int> lengths;



    bool is_committed;

    MPI_Datatype datatype_;

    MPI_Aint origin;

};





} } } // end namespace boost::mpi::detail





#endif // BOOST_MPI_DETAIL_MPI_DATATYPE_OPRIMITIVE_HPP

