/* -*- Mode: C; c-basic-offset:4 ; -*- */

/*
 *  (C) 2001 by Argonne National Laboratory.
 *      See COPYRIGHT in top-level directory.
 */

#include <mpiimpl.h>
#include <mpid_dataloop.h>
#include <stdlib.h>

#undef FCNAME
#define FCNAME "MPID_Type_dup"

/* #define MPID_TYPE_ALLOC_DEBUG */

/*@
  MPID_Type_dup - create a copy of a datatype
 
  Input Parameters:
- oldtype - handle of original datatype

  Output Parameters:
. newtype - handle of newly created copy of datatype

  Return Value:
  0 on success, -1 on failure.
@*/
int MPID_Type_dup(MPI_Datatype oldtype,
		  MPI_Datatype *newtype)
{
    int mpi_errno = MPI_SUCCESS;
    MPID_Datatype *new_dtp = 0, *old_dtp;
    struct MPID_Dataloop *dlp;

    if (HANDLE_GET_KIND(oldtype) == HANDLE_KIND_BUILTIN) {
	/* use contiguous function, then fix is_committed value */
	mpi_errno = MPID_Type_contiguous(1, oldtype, newtype);
	if (mpi_errno) { MPIU_ERR_POP(mpi_errno); }
	MPID_Datatype_get_ptr(*newtype, new_dtp);
	new_dtp->is_committed = 1;
    }
    else {
	/* get pointer to old datatype from handle */
	MPID_Datatype_get_ptr(oldtype, old_dtp); /* fills in old_dtp */
	
	/* allocate new datatype object and handle */
	new_dtp = (MPID_Datatype *) MPIU_Handle_obj_alloc(&MPID_Datatype_mem);
	if (!new_dtp) {
	    /* --BEGIN ERROR HANDLING-- */
	    mpi_errno = MPIR_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE,
					     "MPID_Type_dup", __LINE__, MPI_ERR_OTHER,
					     "**nomem", 0);
	    goto fn_fail;
	    /* --END ERROR HANDLING-- */
	}

	/* fill in datatype */
	MPIU_Object_set_ref(new_dtp, 1);
	/* new_dtp->handle is filled in by MPIU_Handle_obj_alloc() */
	new_dtp->is_contig     = old_dtp->is_contig;
	new_dtp->size          = old_dtp->size;
	new_dtp->extent        = old_dtp->extent;
	new_dtp->ub            = old_dtp->ub;
	new_dtp->lb            = old_dtp->lb;
	new_dtp->true_ub       = old_dtp->true_ub;
	new_dtp->true_lb       = old_dtp->true_lb;
	new_dtp->alignsize     = old_dtp->alignsize;
	new_dtp->has_sticky_ub = old_dtp->has_sticky_ub;
	new_dtp->has_sticky_lb = old_dtp->has_sticky_lb;
	new_dtp->is_permanent  = old_dtp->is_permanent;
	new_dtp->is_committed  = old_dtp->is_committed;
	new_dtp->attributes    = NULL; /* ??? */
	new_dtp->cache_id      = -1; /* ??? */
	new_dtp->name[0]       = 0; /* ??? */
	new_dtp->n_elements    = old_dtp->n_elements;
	new_dtp->element_size  = old_dtp->element_size;
	new_dtp->eltype        = old_dtp->eltype;
	
	MPID_Dataloop_dup(old_dtp->dataloop, old_dtp->dataloop_size, &dlp);

	/* --BEGIN ERROR HANDLING-- */
	if (dlp == NULL) {
	    mpi_errno = MPIR_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE,
					     "MPID_Type_dup", __LINE__,
					     MPI_ERR_OTHER,
					     "**nomem", 0);
	    return mpi_errno;
	}
	/* --END ERROR HANDLING-- */
	
	new_dtp->dataloop       = dlp;
	new_dtp->dataloop_depth = old_dtp->dataloop_depth;
	new_dtp->dataloop_size  = old_dtp->dataloop_size;

#if defined(MPID_HAS_HETERO) || 1
	MPID_Dataloop_dup(old_dtp->dataloop, old_dtp->dataloop_size, 
			  &new_dtp->hetero_dloop);
	new_dtp->hetero_dloop_depth = old_dtp->hetero_dloop_depth;
	new_dtp->hetero_dloop_size  = old_dtp->hetero_dloop_size;
#endif
	
	*newtype = new_dtp->handle;
    }

    MPIU_DBG_MSG_P(DATATYPE,VERBOSE,"dup type %x created.", new_dtp->handle);

 fn_fail:
    return mpi_errno;
}
