/*
  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.
*/

#include <cdi.h>

#include "cdi_lockedIO.h"
#include "cdo_options.h"
#include "cdo_output.h"
#include "cthread_debug.h"

#include <mutex>

static std::mutex streamOpenReadMutex;
static std::mutex streamMutex;


int
streamOpenReadLocked(const char *const p_filename)
{
  openLock();
  const auto streamID = streamOpenRead(p_filename);
  openUnlock();
  if (streamID < 0) cdiOpenError(streamID, "Open failed on >%s<", p_filename);

  return streamID;
}

void
streamCloseLocked(const int p_fileID)
{
  if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
  streamClose(p_fileID);
  if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
}

void
streamInqRecLocked(const int p_fileID, int *const p_varID, int *const p_levelID)
{
  if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
  streamInqRecord(p_fileID, p_varID, p_levelID);
  if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
}

void
streamDefRecLocked(const int p_fileID, const int p_varID, const int p_levelID)
{
  if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
  streamDefRecord(p_fileID, p_varID, p_levelID);
  if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
}

void
streamReadrecordFloatLocked(const int p_fileID, float *const p_data, size_t *const p_nmiss)
{
  if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
  streamReadRecordF(p_fileID, p_data, p_nmiss);
  if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
}

void
streamReadrecordDoubleLocked(const int p_fileID, double *const p_data, size_t *const p_nmiss)
{
  if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
  streamReadRecord(p_fileID, p_data, p_nmiss);
  if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
}

void
streamDefVlistLocked(const int p_fileID, const int p_vlistID)
{
  if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
  streamDefVlist(p_fileID, p_vlistID);
  if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
}

int
streamInqVlistLocked(const int p_fileID)
{
  if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
  const auto vlistID = streamInqVlist(p_fileID);
  if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);

  return vlistID;
}

void
streamWriteRecordDoubleLocked(const int p_fileID, const double *const p_data, const size_t p_nmiss)
{
  if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
  streamWriteRecord(p_fileID, p_data, p_nmiss);
  if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
}

void
streamWriteRecordFloatLocked(const int p_fileID, const float *const p_data, const size_t p_nmiss)
{
  if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
  streamWriteRecordF(p_fileID, p_data, p_nmiss);
  if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
}

int
streamInqTimeStepLocked(const int p_fileID, const int p_tsID)
{
  if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
  const auto nrecs = streamInqTimestep(p_fileID, p_tsID);
  if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);

  return nrecs;
}

int
streamDefTimeStepLocked(const int p_fileID, const int p_tsID)
{
  if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
  const auto success = streamDefTimestep(p_fileID, p_tsID);
  if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
  return success;
}

int
streamCopyRecordLocked(const int p_fileID, const int p_targetFileID)
{
  if (Threading::cdoLockIO) cthread_mutex_lock(streamMutex);
  streamCopyRecord(p_fileID, p_targetFileID);
  if (Threading::cdoLockIO) cthread_mutex_unlock(streamMutex);
  return p_targetFileID;
}

void
vlistCopyFlagLocked(const int p_vlistID2, const int p_vlistID1)
{
  cthread_mutex_lock(streamMutex);
  vlistCopyFlag(p_vlistID2, p_vlistID1);
  cthread_mutex_unlock(streamMutex);
}

void
openLock(void)
{
  cthread_mutex_lock(Threading::cdoLockIO ? streamMutex : streamOpenReadMutex);
}

void
openUnlock(void)
{
  cthread_mutex_unlock(Threading::cdoLockIO ? streamMutex : streamOpenReadMutex);
}

void
cdoVlistCopyFlag(const int vlistID2, const int vlistID1)
{
  vlistCopyFlagLocked(vlistID2, vlistID1);
}
