/*
  This file is part of CDO. CDO is a collection of Operators to
  manipulate and analyse Climate model Data.

  Copyright (C) 2003-2020 Uwe Schulzweida, <uwe.schulzweida AT mpimet.mpg.de>
  See COPYING file for copying and redistribution conditions.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; version 2 of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_LIBPTHREAD
#include <pthread.h>
#endif

#include "process_int.h"
#include "par_io.h"

void *
readRecord(void *arg)
{
  auto *read_arg = (read_arg_t *) arg;
  auto streamID = read_arg->streamID;
  auto p_varID = read_arg->varID;
  auto p_levelID = read_arg->levelID;
  auto p_nmiss = read_arg->nmiss;
  auto p_array = read_arg->array;

  // fprintf(stderr, "readRecord: streamID = %d\n", streamID);
  cdoInqRecord(streamID, p_varID, p_levelID);
  cdoReadRecord(streamID, p_array, p_nmiss);
  // fprintf(stderr, "readRecord: varID %d levelID %d\n", *p_varID, *p_levelID);

  return nullptr;
}

void
parReadRecord(CdoStreamID streamID, int *varID, int *levelID, double *array, size_t *nmiss, par_io_t *parIO)
{
  bool lpario = false;
  int recID = 0, nrecs = 0;
#ifdef HAVE_LIBPTHREAD
  pthread_t thrID = 0;
  // pthread_attr_t attr;
  int rval;

  if (parIO)
    {
      lpario = true;
      recID = parIO->recID;
      nrecs = parIO->nrecs;
      thrID = parIO->thrID;
    }
#endif

  if (recID == 0 || !lpario)
    {
      read_arg_t read_arg;
      read_arg.streamID = streamID;
      read_arg.varID = varID;
      read_arg.levelID = levelID;
      read_arg.nmiss = nmiss;
      read_arg.array = array;

      readRecord(&read_arg);
    }
#ifdef HAVE_LIBPTHREAD
  else
    {
      // fprintf(stderr, "parIO1: %ld streamID %d %d %d\n", (long)thrID, streamID, recID, nrecs);
      rval = pthread_join(thrID, nullptr);
      if (rval != 0) cdoAbort("pthread_join failed!");

      *varID = parIO->varID;
      *levelID = parIO->levelID;
      *nmiss = parIO->nmiss;
      // fprintf(stderr, "parIO2: %ld streamID %d %d %d\n", (long)thrID, streamID, *varID, *levelID);
      arrayCopy(parIO->array_size, parIO->array, array);
    }

  if (lpario && nrecs > 1)
    {
      read_arg_t *read_arg = &(parIO->read_arg);
      if ((recID + 1) < nrecs)
        {
          if (recID == 0)
            {
              pthread_attr_init(&parIO->attr);
              pthread_attr_setdetachstate(&parIO->attr, PTHREAD_CREATE_JOINABLE);
            }

          read_arg->streamID = streamID;
          read_arg->varID = &parIO->varID;
          read_arg->levelID = &parIO->levelID;
          read_arg->nmiss = &parIO->nmiss;
          read_arg->array = parIO->array;

          // fprintf(stderr, "pthread_create: streamID %d %d\n", read_arg->streamID,streamID);
          rval = pthread_create(&thrID, &parIO->attr, readRecord, read_arg);
          if (rval != 0) cdoAbort("pthread_create failed!");

          // fprintf(stderr, "thrID = %ld\n", (long) thrID);
          parIO->thrID = thrID;
        }
      else
        pthread_attr_destroy(&parIO->attr);
    }
#endif
}
