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

  Copyright (C) 2003-2021 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.
*/
#ifndef DATARANGELIST_H
#define DATARANGELIST_H

#include <cdi.h>
#include <cstddef>
#include "cdo_output.h"
#include "varray.h"

struct Datarange
{
  bool check_datarange;
  size_t gridsize;
  int datatype;
  double missval;
  double addoffset;
  double scalefactor;

  Datarange() : check_datarange(false), gridsize(0), datatype(0), missval(0), addoffset(0), scalefactor(1){};

  void
  checkDatarange(double *array, size_t nmiss)
  {
    auto mm = nmiss ? varray_min_max_mv(gridsize, array, missval) : varray_min_max(gridsize, array);
    size_t ivals = (nmiss == 0) ? gridsize : mm.n;

    if (ivals > 0)
      {
        auto smin = (mm.min - addoffset) / scalefactor;
        auto smax = (mm.max - addoffset) / scalefactor;

        if (datatype == CDI_DATATYPE_INT8 || datatype == CDI_DATATYPE_UINT8 || datatype == CDI_DATATYPE_INT16
            || datatype == CDI_DATATYPE_UINT16)
          {
            smin = (int) std::lround(smin);
            smax = (int) std::lround(smax);
          }

        double vmin = 0, vmax = 0;

        // clang-format off
        if      ( datatype == CDI_DATATYPE_INT8   ) { vmin =        -128.; vmax =        127.; }
        else if ( datatype == CDI_DATATYPE_UINT8  ) { vmin =           0.; vmax =        255.; }
        else if ( datatype == CDI_DATATYPE_INT16  ) { vmin =      -32768.; vmax =      32767.; }
        else if ( datatype == CDI_DATATYPE_UINT16 ) { vmin =           0.; vmax =      65535.; }
        else if ( datatype == CDI_DATATYPE_INT32  ) { vmin = -2147483648.; vmax = 2147483647.; }
        else if ( datatype == CDI_DATATYPE_UINT32 ) { vmin =           0.; vmax = 4294967295.; }
        else if ( datatype == CDI_DATATYPE_FLT32  ) { vmin = -3.40282e+38; vmax = 3.40282e+38; }
        else                                        { vmin =     -1.e+300; vmax =     1.e+300; }
        // clang-format on

        if (smin < vmin || smax > vmax)
          cdo_warning("Some data values (min=%g max=%g) are outside the\n"
                      "    valid range (%g - %g) of the used output precision!\n"
                      "    Use the CDO option%s -b F64 to increase the output precision.",
                      smin, smax, vmin, vmax, (datatype == CDI_DATATYPE_FLT32) ? "" : " -b F32 or");
      }
  }
};

#endif
