/****************************************************************************
**  SIONLIB     http://www.fz-juelich.de/jsc/sionlib                       **
*****************************************************************************
**  Copyright (c) 2008-2018                                                **
**  Forschungszentrum Juelich, Juelich Supercomputing Centre               **
**                                                                         **
**  See the file COPYRIGHT in the package base directory for details       **
****************************************************************************/

#define _XOPEN_SOURCE 700

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>

#include "sion_buffer.h"
#include "sion_const.h"
#include "sion_debug.h"
#include "sion_enums.h"
#include "sion_error_handler.h"
#include "sion_file.h"
#include "sion_filedesc.h"
#include "sion_hints.h"
#include "sion_internal.h"
#include "sion_internal_positioning.h"
#include "sion_internal_seek.h"

int _lsion_switch_logical_file_read(_sion_filedesc *filedesc, int rank)
{
  DPRINTFP((2, __func__, -1, "enter r=%d\n", rank));

  if ((filedesc->all_blockcount == NULL) || (filedesc->all_blocksizes == NULL)) {
    return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
      "lsion_switch_logical_file: internal error, data structure not initialized, aborting ...\n");
  }

  /* check if RANK changed */
  int rc = SION_SUCCESS;
  if (rank != filedesc->rank) {
    DPRINTFP((32, __func__, -1, "rank has changed %d -> %d\n", filedesc->rank, rank));

    if ((rank < 0) || (rank >= filedesc->ntasks)) {
      return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
        "lsion_switch_logical_file: parameter rank %d (max. %d) out of range, aborting ...\n", rank, filedesc->ntasks);
    }

    /* store current position in all_* vectors */
    filedesc->all_currentpos[filedesc->rank] = filedesc->currentpos;
    filedesc->all_currentblocknr[filedesc->rank] = filedesc->currentblocknr;

    /* pointer to keyval structure */
    if (filedesc->keyvalmode != SION_KEYVAL_NONE) {
      filedesc->all_keyvalptr[filedesc->rank] = filedesc->keyvalptr;
    }

    /* switch to new rank and restore current position of this rank */
    filedesc->rank = rank;
    filedesc->currentblocknr = filedesc->all_currentblocknr[filedesc->rank];
    filedesc->currentpos = filedesc->all_currentpos[filedesc->rank];
    filedesc->lastchunknr = filedesc->all_blockcount[filedesc->rank] - 1;
    filedesc->startpos = filedesc->all_startpointers[filedesc->rank];
    filedesc->chunksize = filedesc->all_chunksizes[filedesc->rank];
    filedesc->size = filedesc->all_sizes[filedesc->rank];

    /* pointer to keyval structure */
    if (filedesc->keyvalmode != SION_KEYVAL_NONE) {
      filedesc->keyvalptr = filedesc->all_keyvalptr[filedesc->rank];
    }

    for (int32_t blknum = 0; blknum <= filedesc->lastchunknr; blknum++) {
      filedesc->blocksizes[blknum] = filedesc->all_blocksizes[filedesc->ntasks * blknum + filedesc->rank];
    }

    _lsion_file_set_position(filedesc->fileptr, filedesc->currentpos);
  }

  DPRINTFP((2, __func__, -1, "leave rc=%d\n", rc));
  return rc;
}

int _lsion_switch_logical_file_read_master(_sion_filedesc *filedesc, int rank)
{
  DPRINTFP((2, __func__, -1, "enter r=%d\n", rank));

  /* lookup file which contains current rank */
  int lfile = filedesc->mapping[filedesc->rank * 2 + 0];
  int lrank = filedesc->mapping[filedesc->rank * 2 + 1];

  /* check if RANK changed */
  int rc = SION_SUCCESS;
  if (rank != filedesc->rank) {
    if ((rank < 0) || (rank >= filedesc->ntasks)) {
      return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
        "lsion_switch_logical_file: parameter rank %d (max. %d) out of range, aborting ...\n", rank, filedesc->ntasks);
    }

    /* store current position in all_* vectors */
    filedesc->multifiles[lfile]->all_currentpos[lrank] = filedesc->currentpos;
    filedesc->multifiles[lfile]->all_currentblocknr[lrank] = filedesc->currentblocknr;

    /* pointer to keyval structure */
    if (filedesc->keyvalmode != SION_KEYVAL_NONE) {
      filedesc->multifiles[lfile]->all_keyvalptr[lrank] = filedesc->keyvalptr;
    }

    /* switch to new rank and restore current position of this rank */
    filedesc->rank = rank;

    /* lookup again file which contains current rank */
    lfile = filedesc->mapping[filedesc->rank * 2 + 0];
    lrank = filedesc->mapping[filedesc->rank * 2 + 1];

    filedesc->currentblocknr = filedesc->multifiles[lfile]->all_currentblocknr[lrank];
    filedesc->currentpos = filedesc->multifiles[lfile]->all_currentpos[lrank];
    filedesc->lastchunknr = filedesc->multifiles[lfile]->all_blockcount[lrank] - 1;
    filedesc->startpos = filedesc->multifiles[lfile]->all_startpointers[lrank];
    filedesc->chunksize = filedesc->multifiles[lfile]->all_chunksizes[lrank];
    filedesc->size = filedesc->multifiles[lfile]->all_sizes[lrank];
    filedesc->globalskip = filedesc->multifiles[lfile]->globalskip;
    for (int32_t blknum = 0; blknum < filedesc->multifiles[lfile]->all_blockcount[lrank]; blknum++) {
      filedesc->blocksizes[blknum] =
        filedesc->multifiles[lfile]->all_blocksizes[filedesc->multifiles[lfile]->ntasks * blknum + lrank];
    }
    filedesc->fileptr = filedesc->multifiles[lfile]->fileptr;

    DPRINTFP((32, __func__, -1, "switch to file %d and lrank %d currentpos=%" PRId64 ", currentblocknr=%" PRId32 "\n", lfile,
      lrank, filedesc->currentpos, filedesc->currentblocknr));

    /* pointer to keyval structure */
    if (filedesc->keyvalmode != SION_KEYVAL_NONE) {
      filedesc->keyvalptr = filedesc->multifiles[lfile]->all_keyvalptr[lrank];
    }

    /* set rank info in sub-file */
    filedesc->multifiles[lfile]->rank = lrank;

    _lsion_file_set_position(filedesc->fileptr, filedesc->currentpos);
  }

  DPRINTFP((2, __func__, -1, "leave rc=%d\n", rc));
  return rc;
}

int _lsion_seek_on_current_rank_read(_sion_filedesc *filedesc, int64_t offset, sion_seek_mode whence)
{
  DPRINTFP((2, __func__, -1, "enter o=%" PRId64 "\n", offset));

  int newblocknr;
  int64_t newposinblk;
  if (whence == SION_SEEK_BEGIN) {
    /* search absolute position */
    if (!_lsion_seek_search_forward_read(filedesc, offset, 0, &newblocknr, &newposinblk)) {
      return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN, "lsion_seek: error in searching abs pos, returning  ...\n");
    }
    DPRINTFP((32, __func__, -1, "sion_absolute_pos  newblocknr=%d newpos=%" PRId64 "\n", newblocknr, newposinblk));
  } else if (whence == SION_SEEK_END) {
    /* search position relative to end */
    if (!_lsion_seek_search_backward_read(filedesc, offset, filedesc->lastchunknr, &newblocknr, &newposinblk)) {
      return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN, "lsion_seek: error in searching end pos, returning  ...\n");
    }
    DPRINTFP((32, __func__, -1, "sion_end_pos  newblocknr=%d newpos=%" PRId64 "\n", newblocknr, newposinblk));
  } else if (whence == SION_SEEK_CURRENT) {
    if (offset >= 0) {
      /* search relative position forward */
      if (!_lsion_seek_search_forward_read(filedesc, offset, filedesc->currentblocknr, &newblocknr, &newposinblk)) {
        return _lsion_errorprint(
          SION_NOT_SUCCESS, _SION_ERROR_RETURN, "lsion_seek: error in searching relative pos, returning  ...\n");
      }
    } else {
      /* search position relative to end */
      if (!_lsion_seek_search_backward_read(filedesc, offset, filedesc->currentblocknr, &newblocknr, &newposinblk)) {
        return _lsion_errorprint(
          SION_NOT_SUCCESS, _SION_ERROR_RETURN, "lsion_seek: error in searching relative pos, returning  ...\n");
      }
    }
    DPRINTFP((32, __func__, -1, "sion_relative_pos  newblocknr=%d newpos=%" PRId64 "\n", newblocknr, newposinblk));
  } else { // SION_SEEK_CHUNK_BEGIN or SION_SEEK_CHUNK_END
    newblocknr = filedesc->currentblocknr;
    newposinblk = offset;
    DPRINTFP((32, __func__, -1, "sion_current_blk  newblocknr=%d newpos=%" PRId64 "\n", newblocknr, newposinblk));
  }

  // allow position outside block for keyval, file could already be scanned completly
  if (!((filedesc->keyvalmode != SION_KEYVAL_NONE)
        || ((newposinblk >= 0) && (newposinblk <= filedesc->blocksizes[newblocknr])))) {
    DPRINTFP((2, __func__, -1, "lsion_seek: parameter posinblk (%" PRId64 ") is out of range (0 .. %" PRId64 "), aborting ...\n",
      newposinblk, filedesc->blocksizes[newblocknr]));
    return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
      "lsion_seek: parameter posinblk (%" PRId64 ") is out of range (0 .. %" PRId64 "), aborting ...\n", newposinblk,
      filedesc->blocksizes[newblocknr]);
  }
  DPRINTFP((2, __func__, -1, "new posinblk  newblocknr=%d newpos=%" PRId64 "\n", newblocknr, newposinblk));

  DPRINTFP((32, __func__, -1,
    " before set pos: startpos=%" PRId64 " currentblocknr=%" PRId32 " globalskip=%" PRId64 " newposinblk=%" PRId64 "\n",
    filedesc->startpos, filedesc->currentblocknr, filedesc->globalskip, newposinblk));

  /* SET NEW POSITION */
  DPRINTFP(
    (32, __func__, 0, "file pos=%" PRId64 " (%" PRId64 ")\n", _lsion_file_get_position(filedesc->fileptr), filedesc->currentpos));
  filedesc->currentblocknr = newblocknr;
  filedesc->currentpos = filedesc->startpos + filedesc->currentblocknr * filedesc->globalskip + newposinblk;
  _lsion_file_set_position(filedesc->fileptr, filedesc->currentpos);

  DPRINTFP((32, __func__, -1,
    " set pos: newblocknr=%4" PRId32 " newposinblk=%4" PRId64 ", set fileptr to position %14" PRId64 " (%14" PRId64 ")\n",
    filedesc->currentblocknr, newposinblk, _lsion_file_get_position(filedesc->fileptr), filedesc->currentpos));

  DPRINTFP((2, __func__, -1, "leave rc=%d\n", SION_SUCCESS));

  return SION_SUCCESS;
}

int _lsion_switch_logical_file_read_mapped(_sion_filedesc *filedesc_master, int rank)
{
  DPRINTFP((2, __func__, -1, "enter r=%d\n", rank));

  /* check if RANK changed */
  int rc = SION_SUCCESS;
  if (rank != filedesc_master->globalrank) {
    DPRINTFP((32, __func__, -1, "rank has changed %d -> %d\n", filedesc_master->globalrank, rank));

    if ((rank < 0) || (rank >= filedesc_master->ntotaltasksinfile)) {
      return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
        "lsion_switch_logical_file: parameter rank %d (max. %d) out of range, aborting ...\n", rank,
        filedesc_master->ntotaltasksinfile);
    }

    /* transfer meta data to corresponding sub datastructure */
    int lfile = filedesc_master->filenumber;
    int lrank = filedesc_master->rank; /* index to local list */
    _sion_filedesc *filedesc_sub = filedesc_master->multifiles[lfile];

    filedesc_sub->currentpos = filedesc_master->currentpos;
    filedesc_sub->currentblocknr = filedesc_master->currentblocknr;

    /* pointer to keyval structure */
    if (filedesc_sub->keyvalmode != SION_KEYVAL_NONE) {
      DPRINTFP(
        (32, __func__, -1, "set keyvalptr on sub to master lfile=%d,lrank=%d to %x\n", lfile, lrank, filedesc_master->keyvalptr));
      filedesc_sub->keyvalptr = filedesc_master->keyvalptr;
    }

    DPRINTFP((32, __func__, -1, "on file %d: sub,maxchunk=%d master,maxchunk=%d \n", lfile, filedesc_sub->maxchunks,
      filedesc_master->maxchunks));

    /* store data of current rank on sub datastructure */
    DPRINTFP((32, __func__, -1, "store current information lrank=%d lastchunknr=%d\n", lrank, filedesc_sub->lastchunknr));
    filedesc_sub->all_currentpos[lrank] = filedesc_sub->currentpos;
    filedesc_sub->all_currentblocknr[lrank] = filedesc_sub->currentblocknr;

    /* pointer to keyval structure -> all_keyvalptr */
    DPRINTFP((32, __func__, -1, "keyvalptr sub file=%d all_keyvalptr[%d] = %x\n", lfile, lrank, filedesc_sub->keyvalptr));
    if (filedesc_sub->keyvalmode != SION_KEYVAL_NONE) {
      filedesc_sub->all_keyvalptr[lrank] = filedesc_sub->keyvalptr;
    }

    /* lookup file which contains new global rank and set master */
    filedesc_sub = NULL;
    lfile = lrank = -1;
    DPRINTFP((32, __func__, -1, " nfiles=%d\n", filedesc_master->nfiles));

    for (int filenr = 0; (filenr < filedesc_master->nfiles) && (lrank == -1); filenr++) {
      DPRINTFP(
        (4, __func__, -1, " filenr=%d nlocaltasksinfile\n", filenr, filedesc_master->multifiles[filenr]->nlocaltasksinfile));

      for (int t = 0; ((t < filedesc_master->multifiles[filenr]->nlocaltasksinfile) && (lrank == -1)); t++) {
        DPRINTFP((4, __func__, -1, " check filenr=%d t=%d grank=%d with rank=%d\n", filenr, t,
          filedesc_master->multifiles[filenr]->all_globalranks[t], rank));
        if (filedesc_master->multifiles[filenr]->all_globalranks[t] == rank) {
          filedesc_sub = filedesc_master->multifiles[filenr];
          lfile = filenr;
          lrank = t;
          break;
        }
      }
    }
    DPRINTFP((32, __func__, -1, "grank %d is found in file %d with lrank %d\n", rank, lfile, lrank));
    if ((lrank == -1) || (filedesc_sub == NULL)) {
      return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
        "lsion_switch_logical_file: parameter rank %d is not opened on this tasks (mapped mode), aborting ...\n", rank);
    }

    /* switch to new rank and restore current position of this rank */
    filedesc_master->globalrank = filedesc_sub->all_globalranks[lrank];
    filedesc_master->rank = lrank;
    filedesc_master->filenumber = lfile;
    filedesc_master->currentblocknr = filedesc_sub->all_currentblocknr[lrank];
    filedesc_master->currentpos = filedesc_sub->all_currentpos[lrank];
    filedesc_master->lastchunknr = filedesc_sub->all_blockcount[lrank] - 1;
    filedesc_master->startpos = filedesc_sub->all_startpointers[lrank];
    filedesc_master->chunksize = filedesc_sub->all_chunksizes[lrank];
    filedesc_master->size = filedesc_sub->all_sizes[lrank];

    /* pointer to keyval structure */
    if (filedesc_master->keyvalmode != SION_KEYVAL_NONE) {
      DPRINTFP(
        (32, __func__, -1, "set keyvalptr on lfile=%d,lrank=%d to %x\n", lfile, lrank, filedesc_sub->all_keyvalptr[lrank]));
      filedesc_master->keyvalptr = filedesc_sub->all_keyvalptr[lrank];
    }

    if (filedesc_sub->maxchunks > filedesc_master->maxchunks) {
      _lsion_realloc_filedesc_blocklist(filedesc_master, filedesc_sub->maxchunks);
    }
    for (int32_t blknum = 0; blknum <= filedesc_master->lastchunknr; blknum++) {
      filedesc_master->blocksizes[blknum] = filedesc_sub->all_blocksizes[filedesc_sub->ntasks * blknum + lrank];
    }
    filedesc_master->globalskip = filedesc_sub->globalskip;
    filedesc_master->fileptr = filedesc_sub->fileptr;

    _lsion_file_set_position(filedesc_master->fileptr, filedesc_master->currentpos);
  }

  _lsion_print_filedesc(filedesc_master, 512, "_lsion_switch_logical_file_read_mapped", 1);

  DPRINTFP((2, __func__, -1, "leave rc=%d\n", rc));
  return rc;
}

int _lsion_switch_logical_file_write(_sion_filedesc *filedesc, int rank)
{
  DPRINTFP((2, __func__, -1, "enter r=%d\n", rank));

  if ((filedesc->all_blockcount == NULL) || (filedesc->all_blocksizes == NULL) || (filedesc->all_sizes == NULL)) {
    return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
      "lsion_switch_logical_file: internal error, data structure not initialized, aborting ...\n");
  }

  /* check if RANK changed */
  int rc = SION_SUCCESS;
  if (rank != filedesc->rank) {
    DPRINTFP((32, __func__, -1, "rank has changed %d -> %d\n", filedesc->rank, rank));

    if ((rank < 0) || (rank >= filedesc->ntasks)) {
      return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
        "lsion_switch_logical_file: parameter rank %d (max. %d) out of range, aborting ...\n", rank, filedesc->ntasks);
    }

    /* flush write buffer */
    if (filedesc->usebuffer) {
      _lsion_buffer_flush(filedesc);
    }

    /* store current position in all_* vectors */
    DPRINTFP(
      (32, __func__, -1, "store data for rank %d currentpos=%" PRId64 " currentblocknr=%" PRId32 " lastchunknr=%" PRId64 "\n",
        filedesc->rank, filedesc->currentpos, filedesc->currentblocknr, filedesc->lastchunknr));
    filedesc->all_currentpos[filedesc->rank] = filedesc->currentpos;
    filedesc->all_currentblocknr[filedesc->rank] = filedesc->currentblocknr;

    /* pointer to keyval structure */
    if (filedesc->keyvalmode != SION_KEYVAL_NONE) {
      filedesc->all_keyvalptr[filedesc->rank] = filedesc->keyvalptr;
    }

    filedesc->all_sizes[filedesc->rank] = filedesc->size;
    filedesc->all_blockcount[filedesc->rank] = filedesc->lastchunknr + 1;
    for (int32_t blknum = 0; blknum <= filedesc->lastchunknr; blknum++) {
      filedesc->all_blocksizes[filedesc->ntasks * blknum + filedesc->rank] = filedesc->blocksizes[blknum];
      filedesc->blocksizes[blknum] = 0; /* reset entry */
    }

    /* switch to new rank and restore current position of this rank */
    filedesc->rank = rank;
    filedesc->currentblocknr = filedesc->all_currentblocknr[filedesc->rank];
    filedesc->currentpos = filedesc->all_currentpos[filedesc->rank];
    filedesc->lastchunknr = filedesc->all_blockcount[filedesc->rank] - 1;
    filedesc->startpos = filedesc->all_startpointers[filedesc->rank];
    filedesc->chunksize = filedesc->all_chunksizes[filedesc->rank];
    filedesc->size = filedesc->all_sizes[filedesc->rank];

    /* pointer to keyval structure */
    if (filedesc->keyvalmode != SION_KEYVAL_NONE) {
      filedesc->keyvalptr = filedesc->all_keyvalptr[filedesc->rank];
    }

    for (int32_t blknum = 0; blknum <= filedesc->lastchunknr; blknum++) {
      filedesc->blocksizes[blknum] = filedesc->all_blocksizes[filedesc->ntasks * blknum + filedesc->rank];
    }

    _lsion_file_set_position(filedesc->fileptr, filedesc->currentpos);
  }

  DPRINTFP((2, __func__, -1, "leave rc=%d\n", rc));
  return rc;
}

int _lsion_switch_logical_file_write_mapped(_sion_filedesc *filedesc_master, int rank)
{
  DPRINTFP((2, __func__, -1, "enter r=%d\n", rank));

  /* check if RANK changed */
  int rc = SION_SUCCESS;
  if (rank != filedesc_master->globalrank) {
    DPRINTFP((32, __func__, -1, "rank has changed %d -> %d\n", filedesc_master->globalrank, rank));

    if ((rank < 0) || (rank >= filedesc_master->ntotaltasksinfile)) {
      return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
        "lsion_switch_logical_file: parameter rank %d (max. %d) out of range, aborting ...\n", rank,
        filedesc_master->ntotaltasksinfile);
    }

    /* flush write buffer */
    if (filedesc_master->usebuffer) {
      _lsion_buffer_flush(filedesc_master);
    }

    /* transfer meta data to corresponding sub datastructure */
    int lfile = filedesc_master->filenumber;
    int lrank = filedesc_master->rank; /* index to local list */
    _sion_filedesc *filedesc_sub = filedesc_master->multifiles[lfile];

    filedesc_sub->currentpos = filedesc_master->currentpos;
    filedesc_sub->currentblocknr = filedesc_master->currentblocknr;
    filedesc_sub->lastchunknr = filedesc_master->lastchunknr;
    filedesc_sub->size = filedesc_master->size;

    /* pointer to keyval structure */
    if (filedesc_sub->keyvalmode != SION_KEYVAL_NONE) {
      filedesc_sub->keyvalptr = filedesc_master->keyvalptr;
    }

    DPRINTFP((32, __func__, -1, "on file %d: sub,maxchunk=%d master,maxchunk=%d \n", lfile, filedesc_sub->maxchunks,
      filedesc_master->maxchunks));
    if (filedesc_sub->maxchunks < filedesc_master->maxchunks) {
      _lsion_realloc_filedesc_blocklist(filedesc_sub, filedesc_master->maxchunks);
    }

    /* store data of current rank on sub datastructure */
    DPRINTFP((32, __func__, -1, "store current information lrank=%d lastchunknr=%d\n", lrank, filedesc_sub->lastchunknr));
    filedesc_sub->all_currentpos[lrank] = filedesc_sub->currentpos;
    filedesc_sub->all_currentblocknr[lrank] = filedesc_sub->lastchunknr;
    filedesc_sub->all_blockcount[lrank] = filedesc_sub->lastchunknr + 1;
    filedesc_sub->all_sizes[lrank] = filedesc_sub->size;

    /* pointer to keyval structure */
    if (filedesc_sub->keyvalmode != SION_KEYVAL_NONE) {
      filedesc_sub->all_keyvalptr[lrank] = filedesc_sub->keyvalptr;
    }

    for (int32_t blknum = 0; blknum <= filedesc_sub->lastchunknr; blknum++) {
      filedesc_sub->blocksizes[blknum] = filedesc_master->blocksizes[blknum];
      filedesc_sub->all_blocksizes[filedesc_sub->ntasks * blknum + lrank] = filedesc_master->blocksizes[blknum];
    }

    /* lookup file which contains new global rank and set master */
    filedesc_sub = NULL;
    lfile = lrank = -1;
    DPRINTFP((32, __func__, -1, " nfiles=%d\n", filedesc_master->nfiles));

    for (int filenr = 0; (filenr < filedesc_master->nfiles) && (lrank == -1); filenr++) {
      DPRINTFP(
        (32, __func__, -1, " filenr=%d nlocaltasksinfile\n", filenr, filedesc_master->multifiles[filenr]->nlocaltasksinfile));

      for (int t = 0; (t < filedesc_master->multifiles[filenr]->nlocaltasksinfile) && (lrank == -1); t++) {
        DPRINTFP((32, __func__, -1, " check filenr=%d t=%d grank=%d with rank=%d\n", filenr, t,
          filedesc_master->multifiles[filenr]->all_globalranks[t], rank));
        if (filedesc_master->multifiles[filenr]->all_globalranks[t] == rank) {
          filedesc_sub = filedesc_master->multifiles[filenr];
          lfile = filenr;
          lrank = t;
          break;
        }
      }
    }
    DPRINTFP((32, __func__, -1, "grank %d is found in file %d with lrank %d\n", rank, lfile, lrank));
    if ((lrank == -1) || (filedesc_sub == NULL)) {
      return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
        "lsion_switch_logical_file: parameter rank %d is not opened on this tasks (mapped mode), aborting ...\n", rank);
    }

    /* switch to new rank and restore current position of this rank */
    filedesc_master->globalrank = filedesc_sub->all_globalranks[lrank];
    filedesc_master->rank = lrank;
    filedesc_master->filenumber = lfile;
    filedesc_master->currentblocknr = filedesc_sub->all_currentblocknr[lrank];
    filedesc_master->currentpos = filedesc_sub->all_currentpos[lrank];
    filedesc_master->lastchunknr = filedesc_sub->all_blockcount[lrank] - 1;
    filedesc_master->startpos = filedesc_sub->all_startpointers[lrank];
    filedesc_master->chunksize = filedesc_sub->all_chunksizes[lrank];
    filedesc_master->size = filedesc_sub->all_sizes[lrank];

    /* pointer to keyval structure */
    if (filedesc_master->keyvalmode != SION_KEYVAL_NONE) {
      filedesc_master->keyvalptr = filedesc_sub->all_keyvalptr[lrank];
    }

    if (filedesc_sub->maxchunks > filedesc_master->maxchunks) {
      _lsion_realloc_filedesc_blocklist(filedesc_master, filedesc_sub->maxchunks);
    }
    for (int32_t blknum = 0; blknum <= filedesc_sub->lastchunknr; blknum++) {
      filedesc_master->blocksizes[blknum] = filedesc_sub->all_blocksizes[filedesc_sub->ntasks * blknum + lrank];
    }

    filedesc_master->globalskip = filedesc_sub->globalskip;
    filedesc_master->fileptr = filedesc_sub->fileptr;

    _lsion_file_set_position(filedesc_master->fileptr, filedesc_master->currentpos);
  }

  _lsion_print_filedesc(filedesc_master, 512, "_lsion_switch_logical_file_write_mapped", 1);

  DPRINTFP((2, __func__, -1, "leave rc=%d\n", rc));
  return rc;
}

int _lsion_seek_on_current_rank_write(_sion_filedesc *filedesc, int64_t offset, sion_seek_mode whence)
{
  int rc = SION_SUCCESS;

  DPRINTFP((2, __func__, -1, "enter o=%" PRId64 "\n", offset));

  _sion_chunk_sequence sequence = _lsion_chunk_sequence_from_filedesc(filedesc);
  _sion_range chunk;
  int64_t new_position;
  switch (whence) {
  case SION_SEEK_BEGIN:
    new_position = _lsion_chunk_sequence_translate_l2p(&sequence, offset);
    break;
  case SION_SEEK_END:
    new_position = _lsion_chunk_sequence_translate_l2p(&sequence, filedesc->size + offset);
    break;
  case SION_SEEK_CURRENT:
    new_position = _lsion_chunk_sequence_translate_l2p(&sequence, _lsion_get_logical_position(filedesc) + offset);
    break;
  case SION_SEEK_CHUNK_BEGIN:
    chunk = _lsion_get_current_chunk(filedesc);
    if ((offset < 0) || (offset >= chunk.length)) {
      return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
        "lsion_seek: offset %" PRId64 " points outside of current chunk [0, %" PRId64 "), aborting ...\n", offset, chunk.length);
    }
    new_position = chunk.start + offset;
    break;
  case SION_SEEK_CHUNK_END:
    chunk = _lsion_get_current_chunk(filedesc);
    if ((offset >= 0) || (chunk.length + offset < 0)) {
      return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
        "lsion_seek: offset %" PRId64 " points outside of current chunk [0, %" PRId64 "), aborting ...\n", chunk.length + offset,
        chunk.length);
    }
    new_position = chunk.start + chunk.length + offset;
    break;
  default:
    _lsion_errorprint(0, _SION_ERROR_WARN, "lsion_seek: unknown seek mode, doing nothing ...\n");
    new_position = filedesc->currentpos;
    break;
  }

  if (new_position < 0) {
    return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
      "lsion_seek: position %" PRId64 " is before beginning of file, aborting ...\n", new_position);
  }

  _lsion_apply_hints(filedesc, SION_HINTS_FREE_TYPE_CHUNK);
  filedesc->currentpos = new_position;
  filedesc->currentblocknr = _lsion_chunk_sequence_translate_p2n(&sequence, new_position);
  _lsion_apply_hints(filedesc, SION_HINTS_ACCESS_TYPE_CHUNK);

  _lsion_file_set_position(filedesc->fileptr, filedesc->currentpos);

  DPRINTFP((2, __func__, -1, "leave rc=%d\n", rc));
  return rc;
}

int _lsion_seek_search_forward_read(
  _sion_filedesc *filedesc, int64_t abspos, int from_block, int *newblocknr, int64_t *newposinblk)
{
  if (abspos < 0) {
    return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
      "lsion_seek: absolute position from beginning (%" PRId64 ") is negative, aborting  ...\n", abspos);
  }

  *newposinblk = abspos;
  *newblocknr = from_block;
  int64_t bytes = 0;
  int64_t bytesinblock = filedesc->blocksizes[*newblocknr];
  while ((*newblocknr < filedesc->lastchunknr) && (bytes + bytesinblock < *newposinblk)) {
    bytes += bytesinblock;
    (*newblocknr)++;
    bytesinblock = filedesc->blocksizes[*newblocknr];
  }
  *newposinblk -= bytes;

  return SION_SUCCESS;
}

int _lsion_seek_search_backward_read(
  _sion_filedesc *filedesc, int64_t posend, int from_block, int *newblocknr, int64_t *newposinblk)
{
  if (posend > 0) {
    return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
      "lsion_seek: absolute position from end (%" PRId64 ") is positive, aborting  ...\n", posend);
  }

  *newposinblk = posend;
  *newblocknr = from_block;
  do {
    *newposinblk += filedesc->blocksizes[*newblocknr];
    (*newblocknr)--;
  } while ((*newblocknr >= 0) && (*newposinblk < 0));
  /* Add one after loop */
  (*newblocknr)++;
  if (*newposinblk < 0) {
    return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
      "lsion_seek: seek before beginning of file (posend = %" PRId64 ", abspos = %" PRId64 "), aborting  ...\n", posend,
      *newposinblk);
  }

  return SION_SUCCESS;
}
