/**
 * @file xt_exchanger_internal.h
 * @brief internal functions for data exchangers
 *
 * @copyright Copyright  (C)  2024 Jörg Behrens <behrens@dkrz.de>
 *                                 Moritz Hanke <hanke@dkrz.de>
 *                                 Thomas Jahns <jahns@dkrz.de>
 *
 * @author Jörg Behrens <behrens@dkrz.de>
 *         Moritz Hanke <hanke@dkrz.de>
 *         Thomas Jahns <jahns@dkrz.de>
 */
/*
 * Keywords:
 * Maintainer: Jörg Behrens <behrens@dkrz.de>
 *             Moritz Hanke <hanke@dkrz.de>
 *             Thomas Jahns <jahns@dkrz.de>
 * URL: https://dkrz-sw.gitlab-pages.dkrz.de/yaxt/
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are
 * met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * Neither the name of the DKRZ GmbH nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef XT_EXCHANGER_INTERNAL_H
#define XT_EXCHANGER_INTERNAL_H

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdbool.h>
#include <mpi.h>

#include "xt/xt_core.h"
#include "xt/xt_request.h"
#include "xt_exchanger.h"
#include "xt_config_internal.h"
#include "xt_redist_internal.h"
#include "core/ppm_visibility.h"


typedef struct Xt_exchanger_thread_share_ *Xt_exchanger_thread_share;

typedef void (*xt_exchanger_s_exchange_func)(
  Xt_exchanger, const void *, void *);
typedef void (*xt_exchanger_a_exchange_func)(
  Xt_exchanger, const void *, void *, Xt_request *request);
typedef Xt_exchanger_thread_share (*xt_exchanger_create_thread_share_func)(
  Xt_exchanger, Xt_config);
typedef void (*xt_exchanger_destroy_thread_share_func)(
  Xt_exchanger, Xt_exchanger_thread_share);

struct xt_exchanger_vtable {
  Xt_exchanger (*copy)(Xt_exchanger, MPI_Comm, int);
  void (*delete)(Xt_exchanger);
  xt_exchanger_s_exchange_func s_exchange;
  xt_exchanger_a_exchange_func a_exchange;
  int (*get_num_msg)(Xt_exchanger exchanger, int direction);
  int (*get_msg_ranks)(Xt_exchanger, enum xt_msg_direction, int *restrict *);
  MPI_Datatype (*get_MPI_Datatype)(Xt_exchanger, int, enum xt_msg_direction,
                                   bool);
  MPI_Aint (*get_displacement)(Xt_exchanger, int, enum xt_msg_direction);
  int (*get_count)(Xt_exchanger, int, enum xt_msg_direction);
  void (*team_share_default_init)(void *share);
  void (*team_share_destroy)(void *share);
  size_t team_share_size;
  xt_exchanger_create_thread_share_func create_thread_share;
  xt_exchanger_destroy_thread_share_func destroy_thread_share;
  Xt_exchanger_iter (*get_iterator)(Xt_exchanger exchanger, Xt_config config);
};

struct Xt_exchanger_ {
  const struct xt_exchanger_vtable *vtable;
};

struct xt_exchanger_iter_vtable {
  int (*next)(Xt_exchanger_iter);
  MPI_Datatype (*get_MPI_Datatype)(Xt_exchanger_iter iter);
  int (*get_rank)(Xt_exchanger_iter iter);
  int (*get_count)(Xt_exchanger_iter iter);
  int (*get_direction)(Xt_exchanger_iter iter);
  MPI_Aint (*get_displacement)(Xt_exchanger_iter iter);
  void (*delete)(Xt_exchanger_iter);
};

struct Xt_exchanger_iter_ {
  const struct xt_exchanger_iter_vtable *vtable;
};

/**
 * gets a copy of the MPI_Datatype used for a specificed message
 * @param[in] exchanger exchanger object
 * @param[in] rank      MPI rank
 * @param[in] direction specific whether the datatype of an incoming or outgoing
 *                      message is requested
 * @param[in] do_dup    mpi datatype copy will be dup if true
 * @return MPI_Datatype for the specificed message
 * @remark returns MPI_DATATYPE_NULL if there is no message matching the
 *         specificed configuration
 */
PPM_DSO_INTERNAL MPI_Datatype
xt_exchanger_get_MPI_Datatype(Xt_exchanger exchanger,
                              int rank, enum xt_msg_direction direction,
                              bool do_dup);

/**
 * gets the displacement used as offset for a specificed message
 * @param[in] exchanger exchanger object
 * @param[in] rank      MPI rank
 * @param[in] direction specific whether the datatype of an incoming or outgoing
 *                      message is requested
 * @return displacement for the specificed message
 * @remark returns -1 if there is no message matching the
 *         specificed configuration
 */
PPM_DSO_INTERNAL MPI_Aint
xt_exchanger_get_displacement(Xt_exchanger exchanger,
                              int rank, enum xt_msg_direction direction);

/**
 * gets item count used for a specificed message
 * @param[in] exchanger exchanger object
 * @param[in] rank      MPI rank
 * @param[in] direction specific whether the datatype of an incoming or outgoing
 *                      message is requested
 * @return element count for the specificed message
 * @remark returns -1 if there is no message matching the
 *         specificed configuration
 */
PPM_DSO_INTERNAL int
xt_exchanger_get_count(Xt_exchanger exchanger,
                       int rank, enum xt_msg_direction direction);

/**
 * Gets the ranks of all processes that receive data from/send data to the local
 * process in the specificed message.
 * @param[in]  exchanger exchanger object
 * @param[in]  direction specifices whether ranks for the outgoing or incoming
 *                       messages are requested
 * @param[out] ranks     ranks for all outgoing/incoming messages
 * @return number of outgoing/incoming message
 * @remark the user needs to ensure that array ranks is big enough to hold all
 *         ranks
 */
PPM_DSO_INTERNAL int
xt_exchanger_get_msg_ranks(Xt_exchanger exchanger,
                           enum xt_msg_direction direction,
                           int *restrict *ranks);


/**
 * Given an exchanger constructor, query the vtable pointer
 *
 * @param[in] exchanger_new function to create new exchanger to query
 *                          vtable pointer for
 * @return vtable pointer
 */
PPM_DSO_INTERNAL const struct xt_exchanger_vtable *
xt_exchanger_new_get_vtable(Xt_exchanger_new exchanger_new);

/* team shares refer to expensive data that can be shared among
 * multiple instances of exchangers that are of the same class and
 * derived from the same xmap or communication matrix */
enum { team_share_align = sizeof (void *) };

/**
 * Given an exchanger, query the size of shared data for a team.
 *
 * @param[in] exchanger exchanger object to query team shared data size for.
 * @return size of shared data
 */
PPM_DSO_INTERNAL size_t
xt_exchanger_team_get_share_size(Xt_exchanger exchanger);

/**
 * Given an exchanger constructor, query the size of shared data for a
 * team.
 *
 * @param[in] exchanger_new function to create new exchanger to query
 *                          team shared data size for.
 * @return size of shared data
 */
PPM_DSO_INTERNAL size_t
xt_exchanger_new_team_get_share_size(Xt_exchanger_new exchanger_new);

/**
 * Given an exchanger, initialize shared data for a team to default values.
 *
 * @param[in] exchanger exchanger object to default initialize team
 * shared data for.
 * @param[out] share    object to initialize
 */
PPM_DSO_INTERNAL void
xt_exchanger_team_share_default_init(Xt_exchanger exchanger,
                                     void *share);
/**
 * Given an exchanger constructor, initialize shared data for a team
 * to default values.
 *
 * @param[in] exchanger_new exchanger constructor to default initialize team
 * shared data for.
 * @param[out] share    object to initialize
 */
PPM_DSO_INTERNAL void
xt_exchanger_new_team_share_default_init(Xt_exchanger_new exchanger_new,
                                         void *share);

/**
 * Given an exchanger, destroy shared data for a team and reset to default
 * values.
 * This call is collective for all MPI ranks in the communicator the
 * exchanger was constructed for.
 *
 * @param[in] exchanger exchanger object to destroy team shared data for.
 * @param[out] share    object to destroy
 */
PPM_DSO_INTERNAL void
xt_exchanger_team_share_destroy(Xt_exchanger exchanger, void *share);

/**
 * Given an exchanger constructor, destroy shared data for a team and
 * reset to default values.
 *
 * This call is collective for all MPI ranks
 * in the communicator the exchanger team was constructed for.
 *
 * @param[in] exchanger_new exchanger constructor to destroy team
 *                          shared data for.
 * @param[out] share        shared state object to destroy
 */
PPM_DSO_INTERNAL void
xt_exchanger_new_team_share_destroy(Xt_exchanger_new exchanger_new,
                                    void *share);


/**
 * Given an exchanger, create an object holding shared data for
 * multi-threaded invocation.
 *
 * @param[in] exchanger exchanger to prepare the shared data for.
 * @param[in] config custom configuration parameters
 */
PPM_DSO_INTERNAL Xt_exchanger_thread_share
xt_exchanger_create_thread_share(Xt_exchanger exchanger, Xt_config config);

/**
 * Given an exchanger, destroy an object holding shared data for
 * multi-threaded invocation.
 *
 * @param[in] exchanger exchanger the shared data belongs to
 * @param[in,out] thread_share shared state object to destroy
 */
PPM_DSO_INTERNAL void
xt_exchanger_destroy_thread_share(Xt_exchanger exchanger,
                                  Xt_exchanger_thread_share thread_share);


PPM_DSO_INTERNAL void
xt_msg_params_copy(size_t n,
                   const struct Xt_msg_param *restrict src,
                   struct Xt_msg_param *restrict dst,
                   MPI_Comm comm, bool dt_dup);

PPM_DSO_INTERNAL void
xt_msg_params_destruct(size_t n, struct Xt_msg_param *msgs,
                       MPI_Comm comm);

typedef int (*Xt_a_send_func_ptr)
  (void *buf, int count, MPI_Datatype datatype, int dest,
   int tag, MPI_Comm comm, MPI_Request *request);

typedef int (*Xt_send_func_ptr)
  (void *buf, int count, MPI_Datatype datatype, int dest,
   int tag, MPI_Comm comm);

typedef void (*Xt_code_ptr)(void);

PPM_DSO_INTERNAL extern const Xt_code_ptr
xt_send_func_tab[2][xt_exch_send_comm_mode_count];

#endif // XT_EXCHANGER_INTERNAL_H
/*
 * Local Variables:
 * c-basic-offset: 2
 * coding: utf-8
 * indent-tabs-mode: nil
 * show-trailing-whitespace: t
 * require-trailing-newline: t
 * End:
 */
