/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the files COPYING and Copyright.html.  COPYING can be found at the root   *
 * of the source code distribution tree; Copyright.html can be found at the  *
 * root level of an installed copy of the electronic HDF5 document set and   *
 * is linked from the top-level documents page.  It can also be found at     *
 * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html.  If you do not have     *
 * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#define H5S_PACKAGE             /*suppress error about including H5Spkg */

#include "H5private.h"          /* Generic Functions                    */
#include "H5Eprivate.h"         /* Error Handling                       */
#include "H5Oprivate.h"         /* Object Headers                       */
#include "H5Spkg.h"		/* Dataspace functions			*/
#include "H5TBprivate.h"        /* Threaded, Balanced, Binary Trees     */

#ifdef H5_HAVE_FPHDF5

#include "H5FPprivate.h"        /* Flexible Parallel Functions          */

/* Pablo mask */
#define PABLO_MASK      H5FPclient_mask

/* Is the interface initialized? */
static int interface_initialize_g = 0;
#define INTERFACE_INIT  NULL

MPI_Datatype SAP_request_t;     /* MPI datatype for the SAP_request obj */
MPI_Datatype SAP_reply_t;       /* MPI datatype for the SAP_reply obj   */
MPI_Datatype SAP_sync_t;        /* MPI datatype for the SAP_sync obj    */

/* SAP specific variables */
MPI_Comm H5FP_SAP_COMM;         /* Comm we use: Supplied by user        */
MPI_Comm H5FP_SAP_BARRIER_COMM; /* Comm if you want to do a barrier     */

unsigned H5FP_sap_rank;         /* The rank of the SAP: Supplied by user*/
unsigned H5FP_my_rank;          /* Rank of this process in the COMM     */
int H5FP_comm_size;             /* Size of the COMM                     */

/* local functions */
static unsigned int H5FP_gen_request_id(void);
static herr_t H5FP_update_metadata_cache(hid_t file_id, struct SAP_sync *sap_sync,
                                         H5O_fphdf5_t *fmeta);

/** Public Library (non-API) Functions **/

/*
 * Function:    H5FP_request_open
 * Purpose:     Request an open of an object from the SAP. You pass in
 *              the metadata string (MDATA) (this is normally the
 *              filename or pathname to the object), it's length in
 *              (MD_LEN), and the type of the object you're trying to
 *              open (OBJ_TYPE). The request ID is returned in a pointer
 *              supplied by the user.
 * Return:      Success:    SUCCEED
 *              Failure:    FAIL
 * Programmer:  Bill Wendling, 28. August, 2002
 * Modifications:
 */
/* FIXME: This seems to be only related to opening a file, not an object, contrary to the comments above... -QAK */
herr_t
H5FP_request_open(const char *mdata, int md_len, enum sap_obj_type obj_type,
                  unsigned *file_id, unsigned *req_id)
{
    struct SAP_request req;
    MPI_Status mpi_status;
    int ret_value = SUCCEED, mrc;

    FUNC_ENTER_NOAPI(H5FP_request_open, FAIL);

    assert(mdata);
    assert(file_id);
    assert(req_id);
    HDmemset(&mpi_status, 0, sizeof(MPI_Status));

    if (H5FP_my_rank == H5FP_capt_rank) {
        HDmemset(&req, 0, sizeof(req));
        req.req_type = H5FP_REQ_OPEN;
        req.req_id = H5FP_gen_request_id();
        req.proc_rank = H5FP_my_rank;
        req.md_len = md_len;
        req.obj_type = obj_type;

        if ((mrc = MPI_Send(&req, 1, SAP_request_t, (int)H5FP_sap_rank,
                            H5FP_TAG_REQUEST, H5FP_SAP_COMM)) != MPI_SUCCESS)
            HMPI_GOTO_ERROR(FAIL, "MPI_Send failed", mrc);

        /* The first MPI_Send will have been sent before this one will be read. */

        if (H5FP_send_metadata(mdata, md_len, (int)H5FP_sap_rank))
            HGOTO_ERROR(H5E_FPHDF5, H5E_CANTSENDMDATA, FAIL, "can't send metadata to server");
    }

    if ((mrc = MPI_Recv(file_id, 1, MPI_UNSIGNED, (int)H5FP_sap_rank,
                        H5FP_TAG_FILE_ID, H5FP_SAP_COMM, &mpi_status)) != MPI_SUCCESS)
        HMPI_GOTO_ERROR(FAIL, "MPI_Send failed", mrc);

    printf("%u: New File ID == %u\n", H5FP_my_rank, *file_id);
    fflush(stdout);

done:
    *req_id = req.req_id;
    FUNC_LEAVE(ret_value);
}

/*
 * Function:    H5FP_request_lock
 * Purpose:     Request a lock on an object in a file from the SAP. The
 *              request ID is returned in a pointer supplied by the user.
 *              The status of the SAP is returned to the user in the
 *              supplied STATUS pointer.
 * Return:      Success:    SUCCEED
 *              Failure:    FAIL
 * Programmer:  Bill Wendling, 30. July, 2002
 * Modifications:
 */
herr_t
H5FP_request_lock(unsigned int sap_file_id, unsigned char *obj_oid,
                  enum sap_lock_type rw_lock, int last, unsigned *req_id,
                  enum sap_status *status)
{
    struct SAP_request req;
    int ret_value = SUCCEED, mrc;

    FUNC_ENTER_NOAPI(H5FP_request_lock, FAIL);

    assert(obj_oid);
    assert(req_id);
    assert(status);
    HDmemset(&req, 0, sizeof(req));

    *status = H5FP_STATUS_OK;
    req.req_type = last ? H5FP_REQ_LOCK_END : H5FP_REQ_LOCK;
    req.req_id = H5FP_gen_request_id();
    req.sap_file_id = sap_file_id;
    req.rw_lock = rw_lock;
    req.md_len = 0;
    req.proc_rank = H5FP_my_rank;
    H5FP_COPY_OID(req.oid, obj_oid);

    if ((mrc = MPI_Send(&req, 1, SAP_request_t, (int)H5FP_sap_rank,
                        H5FP_TAG_REQUEST, H5FP_SAP_COMM)) != MPI_SUCCESS)
        HMPI_GOTO_ERROR(FAIL, "MPI_Send failed", mrc);

    if (last) {
        /*
         * On the last lock in the lock-group to be acquired, we expect a
         * reply from the SAP
         */
        struct SAP_reply sap_reply;
        MPI_Status mpi_status;

        HDmemset(&mpi_status, 0, sizeof(mpi_status));

        if ((mrc = MPI_Recv(&sap_reply, 1, SAP_reply_t, (int)H5FP_sap_rank,
                            H5FP_TAG_REPLY, H5FP_SAP_COMM, &mpi_status)) != MPI_SUCCESS)
            HMPI_GOTO_ERROR(FAIL, "MPI_Recv failed", mrc);

        *status = sap_reply.status;

        if (sap_reply.status != H5FP_STATUS_LOCK_ACQUIRED)
            /* FIXME: Shouldn't this issue an error also? - QAK */
            HGOTO_DONE(FAIL);
    }

done:
    *req_id = req.req_id;
    FUNC_LEAVE(ret_value);
}

/*
 * Function:    H5FP_request_release_lock
 * Purpose:     Release a lock on the file from the SAP. Request a lock
 *              on an object in a file from the SAP. The request ID is
 *              returned in a pointer supplied by the user. The status
 *              of the SAP is returned to the user in the supplied STATUS
 *              pointer.
 * Return:      Success: The request ID.
 *              Failure: -1
 * Programmer:  Bill Wendling, 30. July, 2002
 * Modifications:
 */
herr_t
H5FP_request_release_lock(unsigned int sap_file_id, unsigned char *obj_oid,
                          int last, unsigned *req_id, enum sap_status *status)
{
    struct SAP_request req;
    herr_t ret_value = SUCCEED;
    int mrc;

    FUNC_ENTER_NOAPI(H5FP_request_release_lock, FAIL);

    assert(req_id);
    assert(status);

    HDmemset(&req, 0, sizeof(req));

    *status = H5FP_STATUS_OK;
    req.req_type = last ? H5FP_REQ_RELEASE_END : H5FP_REQ_RELEASE;
    req.req_id = H5FP_gen_request_id();
    req.sap_file_id = sap_file_id;
    req.md_len = 0;
    req.proc_rank = H5FP_my_rank;

    if (obj_oid)
        H5FP_COPY_OID(req.oid, obj_oid);
    else
        HDmemset(req.oid, '\0', sizeof(req.oid));

    if ((mrc = MPI_Send(&req, 1, SAP_request_t, (int)H5FP_sap_rank,
                        H5FP_TAG_REQUEST, H5FP_SAP_COMM)) != MPI_SUCCESS)
        HMPI_GOTO_ERROR(FAIL, "MPI_Send failed", mrc);

    if (last) {
        /*
         * On the last lock released in this lock-group, we expect a
         * reply from the SAP
         */
        struct SAP_reply sap_reply;
        MPI_Status mpi_status;

        HDmemset(&mpi_status, 0, sizeof(mpi_status));

        if ((mrc = MPI_Recv(&sap_reply, 1, SAP_reply_t, (int)H5FP_sap_rank,
                            H5FP_TAG_REPLY, H5FP_SAP_COMM, &mpi_status)) != MPI_SUCCESS)
            HMPI_GOTO_ERROR(FAIL, "MPI_Recv failed", mrc);

        *status = sap_reply.status;

        if (sap_reply.status != H5FP_STATUS_LOCK_RELEASED) {
            HDfprintf(stderr, "Release: For some reason, we couldn't release the lock\n");
            HDfprintf(stderr, "Release: reply status == %d\n", sap_reply.status);
            HGOTO_DONE(FAIL);
        }
    }

done:
    *req_id = req.req_id;
    FUNC_LEAVE(ret_value);
}

/*
 * Function:    H5FP_request_change
 * Purpose:     Tell the SAP that we want to change the structure of the file.
 *              Include the information the SAP will need to send to the
 *              other processes so that they can be synced with what you
 *              are doing. The request ID is returned in a pointer
 *              supplied by the user.
 * Return:      Success:    SUCCEED
 *              Failure:    FAIL
 * Programmer:  Bill Wendling, 02. August, 2002
 * Modifications:
 */
herr_t
H5FP_request_change(unsigned int sap_file_id, enum sap_obj_type obj_type,
                    enum sap_action action, int mdata_len, const char *mdata,
                    unsigned *req_id)
{
    struct SAP_request req;
    herr_t ret_value = SUCCEED;
    int mrc;

    FUNC_ENTER_NOAPI(H5FP_request_change, FAIL);

    assert(mdata);
    HDmemset(&req, 0, sizeof(req));

    req.req_type = H5FP_REQ_CHANGE;
    req.req_id = H5FP_gen_request_id();
    req.sap_file_id = sap_file_id;
    req.obj_type = obj_type;
    req.action = action;
    req.md_len = mdata_len;
    req.proc_rank = H5FP_my_rank;

    if ((mrc = MPI_Send(&req, 1, SAP_request_t, (int)H5FP_sap_rank,
                        H5FP_TAG_REQUEST, H5FP_SAP_COMM)) != MPI_SUCCESS)
        HMPI_GOTO_ERROR(FAIL, "MPI_Send failed", mrc);

    /* The first MPI_Send will have been sent before this one will be read. */
    if (H5FP_send_metadata(mdata, mdata_len, (int)H5FP_sap_rank) != SUCCEED)
        HGOTO_ERROR(H5E_FPHDF5, H5E_CANTSENDMDATA, FAIL, "can't send metadata to server");

done:
    *req_id = req.req_id;
    FUNC_LEAVE(ret_value);
}

/*
 * Function:    H5FP_request_sync
 * Purpose:     Tell the SAP that we want all of the structural changes
 *              made on the file that we don't know about. The request ID
 *              is returned in a pointer supplied by the user. The status
 *              of the SAP is returned to the user in the supplied STATUS
 *              pointer.
 * Return:      Success:    SUCCEED
 *              Failure:    FAIL
 * Programmer:  Bill Wendling, 02. August, 2002
 * Modifications:
 */
herr_t
H5FP_request_sync(unsigned int sap_file_id, hid_t hdf_file_id,
                  unsigned *req_id, enum sap_status *status)
{
    struct SAP_request req;
    herr_t ret_value = SUCCEED;
    int mrc;

    FUNC_ENTER_NOAPI(H5FP_request_sync, FAIL);

    assert(req_id);
    assert(status);

    HDmemset(&req, 0, sizeof(req));

    *status = H5FP_STATUS_OK;
    req.req_type = H5FP_REQ_SYNC;
    req.req_id = H5FP_gen_request_id();
    req.sap_file_id = sap_file_id;
    req.proc_rank = H5FP_my_rank;

    if ((mrc = MPI_Send(&req, 1, SAP_request_t, (int)H5FP_sap_rank,
                        H5FP_TAG_REQUEST, H5FP_SAP_COMM)) != MPI_SUCCESS)
        HMPI_GOTO_ERROR(FAIL, "MPI_Send failed", mrc);

    for (;;) {
        MPI_Status mpi_status;
        struct SAP_sync sap_sync;

        HDmemset(&mpi_status, 0, sizeof(mpi_status));

        if ((mrc = MPI_Recv(&sap_sync, 1, SAP_sync_t, (int)H5FP_sap_rank, H5FP_TAG_SYNC,
                            H5FP_SAP_COMM, &mpi_status)) != MPI_SUCCESS)
            HMPI_GOTO_ERROR(FAIL, "MPI_Recv failed", mrc);

        if (sap_sync.status != H5FP_STATUS_OK) {
            *status = sap_sync.status;
            HGOTO_DONE(FAIL);
        }

        if (sap_sync.last_msg)
            break;

        /* use the info in the SAP_sync_t structure to update the
         * metadata */
        if (sap_sync.md_len) {
            H5O_fphdf5_t *fmeta;
            char *buf;

            if ((buf = (char *)HDcalloc((size_t)sap_sync.md_len + 1, 1)) == NULL)
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "out of memory");

            HDmemset(&mpi_status, 0, sizeof(mpi_status));

            if ((mrc = MPI_Recv(buf, sap_sync.md_len, MPI_BYTE, (int)H5FP_sap_rank,
                                H5FP_TAG_METADATA, H5FP_SAP_COMM, &mpi_status)) != MPI_SUCCESS) {
                HDfree(buf);
                HMPI_GOTO_ERROR(FAIL, "MPI_Recv failed", mrc);
            }

            /*
             * FIXME: perform whatever metadata updates we can with the
             * metadata returned from the SAP
             */
            fmeta = H5O_FPHDF5[0].decode(NULL, buf, NULL);
            HDfree(buf);

            if (H5FP_update_metadata_cache(hdf_file_id, &sap_sync, fmeta) != SUCCEED) {
                H5O_FPHDF5[0].free(fmeta);
                HGOTO_DONE(FAIL);
            }

            H5O_FPHDF5[0].free(fmeta);
        }
    }

done:
    *req_id = req.req_id;
    FUNC_LEAVE(ret_value);
}

#if 0
struct SAP_sync {
    unsigned int req_id;        /* Request ID copied from the SAP_request   */
    unsigned int sync_id;       /* Sync ID to order the sync messages       */
    unsigned int sap_file_id;   /* SAP's file ID for the specific file      */
    unsigned int last_msg;      /* Indicates this is the last sync msg sent */
    int md_len;                 /* Length of the metadata sent in next msg  */
    enum sap_obj_type obj_type; /* Type of object                           */
    enum sap_action action;     /* Action done on the object                */
    enum sap_status status;     /* Status of the request                    */
};

typedef struct H5O_fphdf5_t {
    uint8_t oid[H5R_OBJ_REF_BUF_SIZE];  /* OID of object                    */
    struct H5S_simple_t *sdim;          /* Simple dimensionality structure  */
    H5T_t *dtype;                       /* Datatype structure               */
    time_t *mtime;                      /* Modification time                */
    H5O_name_t *group;                  /* Group name                       */
    H5O_name_t *dset;                   /* Dataset name                     */
    struct H5P_genplist_t *plist;       /* Pathname of the object           */
} H5O_fphdf5_t;
#endif

static herr_t
H5FP_update_metadata_cache(hid_t file_id, struct SAP_sync *sap_sync, H5O_fphdf5_t *fmeta)
{
    herr_t ret_value = SUCCEED;
    hid_t gid, dset_id;

    FUNC_ENTER_NOINIT(H5FP_update_metadata_cache);

    /* check args */
    assert(sap_sync);
    assert(fmeta);

    switch (sap_sync->action) {
    case H5FP_ACT_CREATE:
    case H5FP_ACT_EXTEND:
        if (sap_sync->obj_type == H5FP_OBJ_DATASET) {
            /* FIXME: These shouldn't be calling API functions. -QAK */
            gid = H5Gopen(file_id, fmeta->group->s);
            dset_id = H5Dopen(gid, fmeta->dset->s);

            if (H5Dextend(dset_id, fmeta->sdim->size) != SUCCEED)
                HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "metadata update failed");
        }

        break;
    case H5FP_ACT_DELETE:
    default:
        break;
    }

done:
    FUNC_LEAVE(ret_value);
}

/*
 * Function:    H5FP_request_close
 * Purpose:     Tell the SAP that we want all of the structural changes
 *              made on the file and then close the file. The request ID
 *              is returned in a pointer passed to the function by the
 *              user.
 * Return:      Success:    SUCCEED
 *              Failure:    FAIL
 * Programmer:  Bill Wendling, 02. August, 2002
 * Modifications:
 */
herr_t
H5FP_request_close(unsigned sap_file_id, unsigned *req_id)
{
    struct SAP_request req;
    int ret_value = SUCCEED, mrc;

    FUNC_ENTER_NOAPI(H5FP_request_close, FAIL);

    HDmemset(&req, 0, sizeof(req));
    req.req_type = H5FP_REQ_CLOSE;
    req.req_id = H5FP_gen_request_id();
    req.sap_file_id = sap_file_id;
    req.proc_rank = H5FP_my_rank;

    if ((mrc = MPI_Send(&req, 1, SAP_request_t, (int)H5FP_sap_rank,
                        H5FP_TAG_REQUEST, H5FP_SAP_COMM)) != MPI_SUCCESS)
        HMPI_GOTO_ERROR(FAIL, "MPI_Send failed", mrc);

    *req_id = req.req_id;

done:
    FUNC_LEAVE(ret_value);
}

/** Private Functions **/

/*
 * Function:    H5FP_gen_request_id
 * Purpose:     Generate a unique request ID to send along with a
 *              message.
 * Return:      Integer >= 0 - Doesn't fail.
 * Programmer:  Bill Wendling, 30. July, 2002
 * Modifications:
 */
static unsigned int H5FP_gen_request_id()
{
    static unsigned int i = 0;

    FUNC_ENTER_NOINIT(H5FP_gen_request_id);
    FUNC_LEAVE(i++);
}

#endif  /* H5_HAVE_FPHDF5 */