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

#include "ad_lustre.h"
#include "adio_extern.h"

void ADIOI_LUSTRE_Fcntl(ADIO_File fd, int flag, ADIO_Fcntl_t * fcntl_struct, int *error_code)
{
    int i, ntimes;
    ADIO_Offset curr_fsize, alloc_size, size, len, done;
    ADIO_Status status;
    char *buf;
#if defined(MPICH) || !defined(PRINT_ERR_MSG)
    static char myname[] = "ADIOI_LUSTRE_FCNTL";
#endif

    switch (flag) {
        case ADIO_FCNTL_GET_FSIZE:
            fcntl_struct->fsize = lseek(fd->fd_sys, 0, SEEK_END);
            if (fd->fp_sys_posn != -1)
                lseek(fd->fd_sys, fd->fp_sys_posn, SEEK_SET);
            if (fcntl_struct->fsize == -1) {
                *error_code = MPIO_Err_create_code(MPI_SUCCESS,
                                                   MPIR_ERR_RECOVERABLE, myname, __LINE__,
                                                   MPI_ERR_IO, "**io", "**io %s", strerror(errno));
            } else
                *error_code = MPI_SUCCESS;
            break;

        case ADIO_FCNTL_SET_DISKSPACE:
            /* will be called by one process only */
            /* On file systems with no preallocation function, I have to
             * explicitly write
             * to allocate space. Since there could be holes in the file,
             * I need to read up to the current file size, write it back,
             * and then write beyond that depending on how much
             * preallocation is needed.
             * read/write in sizes of no more than ADIOI_PREALLOC_BUFSZ */

            curr_fsize = lseek(fd->fd_sys, 0, SEEK_END);
            alloc_size = fcntl_struct->diskspace;

            size = MPL_MIN(curr_fsize, alloc_size);

            ntimes = (size + ADIOI_PREALLOC_BUFSZ - 1) / ADIOI_PREALLOC_BUFSZ;
            buf = (char *) ADIOI_Malloc(ADIOI_PREALLOC_BUFSZ);
            done = 0;

            for (i = 0; i < ntimes; i++) {
                len = MPL_MIN(size - done, ADIOI_PREALLOC_BUFSZ);
                ADIO_ReadContig(fd, buf, len, MPI_BYTE, ADIO_EXPLICIT_OFFSET, done,
                                &status, error_code);
                if (*error_code != MPI_SUCCESS) {
                    *error_code = MPIO_Err_create_code(MPI_SUCCESS,
                                                       MPIR_ERR_RECOVERABLE, myname, __LINE__,
                                                       MPI_ERR_IO, "**io", "**io %s",
                                                       strerror(errno));
                    return;
                }
                ADIO_WriteContig(fd, buf, len, MPI_BYTE, ADIO_EXPLICIT_OFFSET,
                                 done, &status, error_code);
                if (*error_code != MPI_SUCCESS)
                    return;
                done += len;
            }

            if (alloc_size > curr_fsize) {
                memset(buf, 0, ADIOI_PREALLOC_BUFSZ);
                size = alloc_size - curr_fsize;
                ntimes = (size + ADIOI_PREALLOC_BUFSZ - 1) / ADIOI_PREALLOC_BUFSZ;
                for (i = 0; i < ntimes; i++) {
                    len = MPL_MIN(alloc_size - done, ADIOI_PREALLOC_BUFSZ);
                    ADIO_WriteContig(fd, buf, len, MPI_BYTE, ADIO_EXPLICIT_OFFSET,
                                     done, &status, error_code);
                    if (*error_code != MPI_SUCCESS)
                        return;
                    done += len;
                }
            }
            ADIOI_Free(buf);
            if (fd->fp_sys_posn != -1)
                lseek(fd->fd_sys, fd->fp_sys_posn, SEEK_SET);
            *error_code = MPI_SUCCESS;
            break;

        case ADIO_FCNTL_SET_ATOMICITY:
            fd->atomicity = (fcntl_struct->atomicity == 0) ? 0 : 1;
            *error_code = MPI_SUCCESS;
            break;

        default:
            FPRINTF(stderr, "Unknown flag passed to ADIOI_LUSTRE_Fcntl\n");
            MPI_Abort(MPI_COMM_WORLD, 1);
    }
}
