/*
  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 "cdo_output.h"
#include "field.h"
#include "array.h"
#include "functs.h"

void
vfarcmul(Field &field, const double rconst)
{
  const auto missval1 = field.missval;
  const auto missval2 = field.missval;
  auto &array = field.vec_d;

  const auto len = field.size;

  if (field.nmiss)
    {
      for (size_t i = 0; i < len; i++) array[i] = MULMN(array[i], rconst);
    }
  else
    {
      for (size_t i = 0; i < len; i++) array[i] *= rconst;
    }
}

void
vfarcdiv(Field &field, const double rconst)
{
  const auto missval1 = field.missval;
  const auto missval2 = field.missval;
  auto &array = field.vec_d;

  const auto len = field.size;

  if (field.nmiss || IS_EQUAL(rconst, 0))
    {
      for (size_t i = 0; i < len; i++) array[i] = DIVMN(array[i], rconst);

      if (IS_EQUAL(rconst, 0)) field.nmiss = len;
    }
  else
    {
      for (size_t i = 0; i < len; i++) array[i] /= rconst;
    }
}

void
vfarcadd(Field &field, const double rconst)
{
  const auto missval1 = field.missval;
  const auto missval2 = field.missval;
  auto &array = field.vec_d;

  const auto len = field.size;

  if (field.nmiss)
    {
      for (size_t i = 0; i < len; i++) array[i] = ADDMN(array[i], rconst);
    }
  else
    {
      for (size_t i = 0; i < len; i++) array[i] += rconst;
    }
}

void
vfarcsub(Field &field, const double rconst)
{
  vfarcadd(field, -rconst);
}

void
vfarcmin(Field &field, const double rconst)
{
  const auto missval1 = field.missval;
  auto &array = field.vec_d;

  const auto len = field.size;

  if (field.nmiss)
    {
      for (size_t i = 0; i < len; i++)
        if (DBL_IS_EQUAL(array[i], missval1) || array[i] > rconst) array[i] = rconst;
    }
  else
    {
      for (size_t i = 0; i < len; i++)
        if (array[i] > rconst) array[i] = rconst;
    }
}

void
vfarcmax(Field &field, const double rconst)
{
  const auto missval1 = field.missval;
  auto &array = field.vec_d;

  const auto len = field.size;

  if (field.nmiss)
    {
      for (size_t i = 0; i < len; i++)
        if (DBL_IS_EQUAL(array[i], missval1) || array[i] < rconst) array[i] = rconst;
    }
  else
    {
      for (size_t i = 0; i < len; i++)
        if (array[i] < rconst) array[i] = rconst;
    }
}

void
vfarmod(Field &field, const double divisor)
{
  const auto missval1 = field.missval;
  auto &array = field.vec_d;

  const auto len = field.size;

  for (size_t i = 0; i < len; i++)
    {
      array[i] = DBL_IS_EQUAL(array[i], missval1) ? missval1 : fmod(array[i], divisor);
    }
}

void
vfarcfun(Field &field, const double rconst, const int function)
{
  switch (function)
    {
    case func_add: vfarcadd(field, rconst); break;
    case func_sub: vfarcsub(field, rconst); break;
    case func_mul: vfarcmul(field, rconst); break;
    case func_div: vfarcdiv(field, rconst); break;
    case func_min: vfarcmin(field, rconst); break;
    case func_max: vfarcmax(field, rconst); break;
    case func_mod: vfarmod(field, rconst); break;
    default: cdoAbort("%s: function %d not implemented!", __func__, function);
    }
}
