/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

// MvGrid.h,    vk Aug-98

#pragma once

#include <float.h>

#include <string>
#include <map>
#include <vector>
#include <memory>

#include "mars.h"

#include "MvLocation.h"
#include "MvMatrix.h"

using namespace metview;

enum eGribHeaderType
{
    GRIB_LONG = 0,
    GRIB_DOUBLE,
    GRIB_STRING,
    GRIB_DOUBLE_ARRAY,
    GRIB_LONG_ARRAY,
    GRIB_TYPE_ERROR
};

const std::string cLatLonGrid("regular_ll");
const std::string cLatLonRotatedGrid("rotated_ll");
const std::string cLatLonReducedGrid("reduced_ll");
const std::string cGaussianGrid("regular_gg");
const std::string cGaussianReducedGrid("reduced_gg");
const std::string cPolarStereoGrid("polar_stereographic");
const std::string cLambertGrid("lambert");
const std::string cLambertAzEqGrid("lambert_azimuthal_equal_area");
const std::string cMercatorGrid("mercator");
const std::string cSatelliteImage("space_view");

const double cGridScaling = 1000.0;
// const int    cLevelNotGiven    = -999;


//-- temporary functions:
void eccodes_port_missing(const char* msg);
void nontested_eccodes_port(const char* msg);


class MvGridBase;
using MvGridPtr = std::shared_ptr<MvGridBase>;

//! Function to create an instance of a suitable MvGrid object.
MvGridBase* MvGridFactory(field* myfield,
                          bool memoryToBeReleased = true,
                          bool expandGrid = true);


//===================================================================
//
// MvGridPoint:
// -----------
//! \brief Utility class (struct) for a single grid point
//!
//! A utility class to hold the value and the coordinates of
//! a single grid point (or a location)
//!

struct MvGridPoint
{
    MvGridPoint(double value, double lat_y, double lon_x, long index = -1) :
        value_(value),
        loc_(lat_y, lon_x),
        index_(index) {}
    MvGridPoint(double value, const MvLocation& loc, long index = -1) :
        value_(value),
        loc_(loc),
        index_(index) {}
    MvGridPoint() :
        value_(mars.grib_missing_value),
        loc_(0, 0),
        index_(-1) {}

    static void sortByDistance(std::vector<MvGridPoint>& points, const MvLocation& refPoint);

    double value_;
    MvLocation loc_;
    long index_;
};

const double cMvGridMissingValue = DBL_MAX;   // mars.grib_missing_value;
const double cMvlatLonMissingValue = -999.9;  // mars.grib_missing_value;
const MvGridPoint cMissingPoint(cMvGridMissingValue, cMvlatLonMissingValue, cMvlatLonMissingValue);

//! Class to handle rectangular areas
/*! Metview has "an Area Problem" i.e. areas are defined differently
 *  for PlotWindow and for Macros. This class accepts geographical
 *  areas defined in either form:
 * <PRE>
 *     (1) [upper left corner]-[lower right corner] i.e. [n,w,s,e] (Macro)
 *  or:
 *     (2) [lower left corner]-[upper right corner] i.e. [s,w,n,e] (Window)
 * </PRE>
 *  where one corner value is a [latitude,longitude] pair.
 *
 *  Class asserts that internally:
 * <PRE>
 *     o  North >= South (North and South values swapped if (2) used)
 *     o   East >= West  (never swapped, only normalized)
 * </PRE>
 *  "East >= West" relation is guaranteed by normalising the values:
 * <PRE>
 *     o     0 <  East <= 360
 *     o  -360 <= West <= East
 *     o   (East-West) <= 360
 * </PRE>
 *  Prerequisite:\n
 *  Class is designed to work properly with input argument ranges:
 * <PRE>
 *     o  -360 <= longitude <= +360
 *     o   -90 <= latitude  <=  +90.
 * </PRE>
 */
class MvGeoBox
{
public:
    //! Constructor with area fully defined
    MvGeoBox(double north, double west, double south, double east)
    {
        set(north, west, south, east);
    }

    //! Constructor that defines a belt between the given latitudes
    MvGeoBox(double north, double south) { set(north, 0, south, 360); }

    //! Default constructor, area covers the whole globe
    MvGeoBox() { set(90, 0, -90, 360); }

    //! Sets a new area
    void set(double north, double west, double south, double east);

    //! Tests if the given point is inside the area
    /*! The longitude argument \c lon_x is normalised before testing,
     *  trying to fit it between the normalised East-West values.
     */
    int isInside(double lat_y, double lon_x) const;

private:
    double n, w, s, e;
};

// Backwards compatibility: MvGeoBox was previously called just GeoBox
typedef MvGeoBox GeoBox;


//===================================================================
//
// MvGridBase:
// ----------
//! Abstract base class for different grid representations
/*! Classes have methods to traverse grid points consequtively.
 *  For each valid grid point its value, location and non-normalised
 *  weight can be requested.
 */

class MvGridBase
{
    friend class MvField;

public:
    MvGridBase(field* myfield, field_state oldState, bool memoryToBeReleased);
    virtual ~MvGridBase();

    //! sets whether to restore the field's original shape when destroyed
    void restoreShapeOnDestroy(bool b) { restoreShapeOnDestroy_ = b; }

    //! Returns the \c index:th value
    double operator[](int index);

    //! Initialises the iterator
    virtual void init();

    //! Returns the longitude value of the first grid point
    virtual double firstLonX() = 0;

    //! Returns the latitude value of the first grid point
    virtual double firstLatY() = 0;

    //! Advances the iterator to the next grid point
    /*! Returns \c false if no more grid points left
     */
    virtual bool advance() = 0;

    //! Returns the relative area weight of the grid point [iterator]
    virtual double weight() const;

    // Collects the surrounding grid point around the specified location
    virtual bool surroundingGridpoints(double /*lat_y*/, double /*lon_x*/, std::vector<MvGridPoint>& /*points*/,
                                       bool /*canHaveMissing*/, bool /*doSort*/) { return false; }

    //! Returns the nearest grid point value and location
    virtual MvGridPoint nearestGridpoint(double lat_y, double lon_x, bool nearestValid);

    //! Returns the value interpolated into the given point
    virtual double interpolatePoint(double lat_y, double lon_x, std::vector<MvGridPoint>* surroundingPoints = nullptr);

    //! Returns a matrix around the given point
    virtual bool getMatrixNN(double lat_y, double lon_x, MvMatrix& mat);

    //! Returns \c true if the underlying grid is geographical and implemented
    /*! Returns \c false for spectral data and for unimplemented grids
     */
    virtual bool hasLocationInfo() const { return true; }

    //! Returns \c true if the underlying grid has at least the iterator methods implemented
    /*! Returns \c false for spectral data and for unimplemented grids
     */
    virtual bool hasIterator() const { return true; }


    //! Returns \c true when the object contains a valid field descriptor
    bool isValid() const { return field_ != 0; }

    //! Returns \c true when the current grid is similar to \c otherGrib
    /*! Returns \c false if grids contain different number of points
     *  or grid scanning modes are different
     */
    bool isEqual(const MvGridBase* otherGrib) const;
    //-- isSimilar() to be removed (011025/vk) --//
    //	bool   isSimilar( const MvGridBase* g ) const { return isEqual(g); }

    //! Returns the grid type as a string
    std::string gridType() const { return gridType_; }

    //! Check grid type (implement for the other grids, if it is needed)
    bool isRegularLatLongGrid()
    {
        return gridType_ == cLatLonGrid;
    }

    //! Returns the value of the current grid point [iterator]
    virtual double value() const
    {
        return valueAt(currentIndex_);
    }
    //	         { return field_ ? (double)(field_->values[ currentIndex_ ]) : mars.grib_missing_value; }

    typedef void (MvGridBase::*DerivativeMethod)(MvGridBase*);

    //! Assigns \c xx to the value of the current grid point [iterator]
    void value(double xx)
    {
        if (field_)
            field_->values[currentIndex_] = xx;
    }

    void setValueToMissing()
    {
        if (field_) {
            field_->values[currentIndex_] = mars.grib_missing_value;
            field_->bitmap = true;
        }
    }


    void setValueAtToMissing(int index)
    {
        if (field_) {
            field_->values[index] = mars.grib_missing_value;
            field_->bitmap = true;
        }
    }

    void setAllValuesToMissing(bool flagBitmap = true);
    static void setAllValuesToMissing(field*, bool flagBitmap = true);

    //! Returns the value of the \c i:th grid point
    double valueAt(int i) const;
    //	double valueAt( int i ) const
    //	         { return field_ ? (double)(field_->values[ i ]) : mars.grib_missing_value; }

    //! Assigns \c xx to the value of the \c i:th grid point
    void valueAt(int i, double xx)
    {
        if (field_)
            field_->values[i] = xx;
    }

    //! Returns \c true when the grid point has a value
    /*! Returns \c false when the grid point is missing its value
     */
    bool hasValue() const { return value() != mars.grib_missing_value; }

    //! Returns the code used for representing missing values
    double missingValue() const { return mars.grib_missing_value; }

    //! Returns the latitude of the current grid point [iterator]
    virtual double lat_y() const { return currentLaty_; }

    //! Returns the longitude of the current grid point [iterator]
    virtual double lon_x() const { return currentLonx_; }

    //! Returns the number of data values in the GRIB message
    /*! This is either the number of grid points (for geographical
     *  grids) or the number spectral coefficient (spherical harmonics)
     */
    long length() const { return field_ ? (long)(field_->value_count) : 0; }

    //! Returns the value and the location of the current grid point [iterator]
    MvGridPoint gridPoint() const { return MvGridPoint(value(), lat_y(), lon_x(), currentIndex_); }

    virtual std::vector<double> averageCalc(bool ew, double N, double W, double S, double E, double gridInterval);

    virtual void firstDerivativeX(MvGridBase*) {}
    virtual void firstDerivativeY(MvGridBase*) {}
    virtual void secondDerivativeX(MvGridBase*) {}
    virtual void secondDerivativeY(MvGridBase*) {}

    // computes the grid cell area for each gridpoint
    virtual void gridCellArea(MvGridBase*) {}
    virtual bool isGridCellAreaSupported() const { return false; }

    virtual void boundingBox(std::vector<double>&) {}

    static eGribHeaderType getNativeType(field* fld, const char* name);
    eGribHeaderType getNativeType(const char* name) const;
    static long getLong(field* fld, const char* name, bool throwOnError = false, bool quiet = false);
    long getLong(const char* name, bool throwOnError = false, bool quiet = false) const;
    static double getDouble(field* fld, const char* name, bool throwOnError = false, bool quiet = false);
    double getDouble(const char* name, bool throwOnError = false, bool quiet = false) const;
    static std::string getString(field* fld, const char* name, bool throwOnError = false, bool quiet = false);
    std::string getString(const char* name, bool throwOnError = false, bool quiet = false) const;

    static long getLongArray(field* fld, const char* name, long** lvals, bool throwOnError = false, bool quiet = false);
    long getLongArray(const char* name, long** lvals, bool throwOnError = false, bool quiet = false) const;
    static long getDoubleArray(field* fld, const char* name, double** lvals, bool throwOnError = false, bool quiet = false);
    long getDoubleArray(const char* name, double** lvals, bool throwOnError = false, bool quiet = false) const;

    static bool setLong(field* fld, const char* name, long val);
    bool setLong(const char* name, long val);
    static bool setDouble(field* fld, const char* name, double val);
    bool setDouble(const char* name, double val);

    static bool setLongArray(field* fld, const char* name, long* val, size_t len);
    bool setLongArray(const char* name, long* va, size_t len);

    static bool setString(field* fld, const char* name, std::string& val);
    bool setString(const char* name, std::string& val);

    static double validityDateTime(field* fld);
    double validityDateTime() const;

    static double yyyymmddFoh(field* fld);
    double yyyymmddFoh() const;

    static double stepFoh(field* fld);
    double stepFoh() const;

    long vertCoordCoefPairCount();
    bool vertCoordCoefs(double& C1, double& C2, int level);

    field* fieldPtr() { return field_; }
    void setForgetValuesOnDestroy(bool b) { forgetValuesOnDestroy_ = b; }

protected:
    virtual bool ScanModeCheck(double first, double last, double step) const;

    field* field_;
    double* vertCoord_;
    long vertCoordPairCount_;
    std::string gridType_;
    long horisLines_;   //-- points_meridian
    long horisPoints_;  //-- points_parallel
    int horisPointCount_;
    long currentIndex_;
    double currentLaty_;
    double currentLonx_;
    double dx_;
    field_state oldState_;
    bool memoryToBeReleased_;
    bool restoreShapeOnDestroy_;
    bool forgetValuesOnDestroy_{false};
    static constexpr double lonDeltaNearEastBorder_{1E-7};
};

//===================================================================
//
// MvUnimplementedGrid:
// -------------------
//
//! \brief MvGrid class for unimplemented grids
//!
//! This class is for unimplemented grids.
//!
//!  Used by MvGridFactory to return a valid ptr, always, i.e.
//!  applications can safely use the ptr. (Method 'isValid()'
//!  can be used to check if the grid behind the ptr has been
//!  properly implemented.)
//!

class MvUnimplementedGrid : public MvGridBase
{
public:
    MvUnimplementedGrid(field* myfield, field_state oldState);
    ~MvUnimplementedGrid() {}

    void init();
    bool advance();
    double firstLonX() { return cMvlatLonMissingValue; }
    double firstLatY() { return cMvlatLonMissingValue; }
    bool hasLocationInfo() const { return false; }
    bool hasIterator() const { return false; }
    double weight() const { return 1; }
    MvGridPoint nearestGridpoint(double, double, bool)
    {
        return cMissingPoint;
    }
    double interpolatePoint(double, double, std::vector<MvGridPoint>* /*surroundingPoints*/)
    {
        return DBL_MAX;
    }
    std::vector<double> averageCalc(bool ew, double N, double W, double S, double E, double grid);
};


//===================================================================
//
// MvGridUsingGribIterator:
// ------------------------
//
//! \brief MvGrid base class for grids that use the ecCodes GRIB iterator
//!
//! This base class is for grids that use the ecCodes GRIB iterator
//!

class MvGridUsingGribIterator : public MvGridBase
{
public:
    MvGridUsingGribIterator(field* myfield, field_state oldState, bool memoryToBeReleased);

    ~MvGridUsingGribIterator();

    bool advance();

    //! We may/may not have the iterator for this projection
    bool hasIterator() const { return (iter_ != nullptr); }

    double value() const;

private:
    grib_iterator* iter_;
    double currentVal_;
};


//===================================================================
//
// MvLatLonGrid:
// ------------
//! \brief MvGrid class for fields in latitude/longitude grid
//!
//! This class is for fields in normal latitude/longitude grid
//!

class MvLatLonGrid : public MvGridBase
{
public:
    MvLatLonGrid(field* myfield, field_state oldState, bool memoryToBeReleased, bool isRotated = false);
    ~MvLatLonGrid() {}

    virtual double firstLonX() override;
    virtual double firstLatY() override;
    virtual double lastLonX();
    virtual double lastLatY();
    virtual void init() override;
    virtual bool advance() override;
    bool surroundingGridpoints(double lat_y, double lon_x, std::vector<MvGridPoint>& points,
                               bool canHaveMissing, bool doSort) override;
    virtual bool surroundingGridpointsForExtrapolation(double lat_y, double lon_x, std::vector<MvGridPoint>& points,
                                                       bool canHaveMissing, bool doSort);
    virtual MvGridPoint nearestGridpoint(double lat_y, double lon_x, bool nearestValid) override;
    virtual double interpolatePoint(double lat_y, double lon_x, std::vector<MvGridPoint>* surroundingPoints) override;
    void firstDerivativeX(MvGridBase*) override;
    void firstDerivativeY(MvGridBase*) override;
    void secondDerivativeX(MvGridBase*) override;
    void secondDerivativeY(MvGridBase*) override;
    void gridCellArea(MvGridBase*) override;
    bool isGridCellAreaSupported() const override { return true; }
    void boundingBox(std::vector<double>&) override;

protected:
    // gribsec2_ll* sec2_;

private:
    bool isGlobalInLon() const;
    double centralDiff(int idx1, int idx2, double w);
    double forwardDiff(int idx0, int idx1, int idx2, double w);
    double backwardDiff(int idx0, int idx1, int idx2, double w);
    double centralDiffSecond(int idx0, int idx1, int idx2, double w);
    double forwardDiffSecond(int idx0, int idx1, int idx2, int idx3, double w);
    double backwardDiffSecond(int idx0, int idx1, int idx2, int idx3, double w);

    double dy_;
    double firstLonX_;
    double firstLatY_;
    double lastLonX_;
    double lastLatY_;
    double southernLat_;
    double northernLat_;
    bool globalNS_;
    bool globalEW_;
};

//===================================================================
//
// MvLatLonRotatedGrid:
// -------------------
//! \brief MvGrid class for fields in rotated latitude/longitude grid
//!
//! This class is for fields in rotated latitude/longitude grid
//!

class MvLatLonRotatedGrid : public MvLatLonGrid
{
public:
    MvLatLonRotatedGrid(field* myfield, field_state oldState, bool memoryToBeReleased);
    ~MvLatLonRotatedGrid() {}

    virtual double lat_y() const
    {
        return unRotate(MvLatLonGrid::lat_y(), MvLatLonGrid::lon_x()).latitude();
    }
    virtual double lon_x() const
    {
        return unRotate(MvLatLonGrid::lat_y(), MvLatLonGrid::lon_x()).longitude();
    }

    virtual MvGridPoint nearestGridpoint(double lat_y, double lon_x, bool nearestValid);
    virtual double interpolatePoint(double lat_y, double lon_x, std::vector<MvGridPoint>* surroundingPoints);

protected:
    MvLocation unRotate() const { return unRotate(currentLaty_, currentLonx_); }
    MvLocation unRotate(double laty, double lonx) const;
    MvLocation rotate(double laty, double lonx) const;

private:
    double southPoleLaty_;
    double southPoleLonx_;
    // double       southPoleRotation_;
};

//===================================================================
//
// MvLambertGrid:
// -------------
//! \brief MvGrid class for fields in Lambert grid
//!
//! This class is for fields in Lambert grid. Member functions nearestGridpoint()
//! and interpolatePoint() have not been implemented for this class and
//! thus the methods in the base class are used (these methods do not try
//! to calculate which grid point is the right one, but instead go through
//! all points in order to find the closest one).
//!

class MvLambertGrid : public MvGridBase
{
public:
    MvLambertGrid(field* myfield, field_state oldState, bool memoryToBeReleased);
    ~MvLambertGrid() {}

    virtual double firstLonX();
    virtual double firstLatY();
    virtual bool advance();
    //	virtual MvGridPoint nearestGridpoint( double lat_y, double lon_x );
    //	virtual double      interpolatePoint( double lat_y, double lon_x, std::vector<MvGridPoint> *surroundingPoints );
    virtual double weight() const;

protected:
    bool computeLatLon(int ip, int jp);

protected:
    //	fortint*     sec2_;

private:
    // double       dy_;
    double gridLat1_;
    double gridLon1_;
    double gridVertLon_;
    double gridTanLat_;
    double sinLatTan_;
    double sinLatTanInvPer2_;
    double cosLatTan_;
    double hemiSphere_;
    double poleI_;
    double poleJ_;
    double earthRadius_;
    double earthRadiusPerDx_;
    double theThing_;
    int currentI_;
    int currentJ_;
    bool jPositive_;
};


//===================================================================
//
// MvLambertAzimuthalEqualAreaGrid:
// -------------
//! \brief MvGrid class for fields in Lambert Azimuthal Equal Area grid
//!
//! This class is for fields in Lambert Azimuthal Equal Area grid.
//! It pretty much just does what the MvGridUsingGribIterator class does.

class MvLambertAzimuthalEqualAreaGrid : public MvGridUsingGribIterator
{
public:
    MvLambertAzimuthalEqualAreaGrid(field* myfield, field_state oldState, bool memoryToBeReleased);
    ~MvLambertAzimuthalEqualAreaGrid() {}

    //! Dummy function. Returns always missing_value.
    double firstLonX() { return cMvlatLonMissingValue; }

    //! Dummy function. Returns always missing_value.
    double firstLatY() { return cMvlatLonMissingValue; }
};


//===================================================================
//
// MvMercatorGrid:
// -------------
//! \brief MvGrid class for fields in Merctor grid
//!
//! This class is for fields in Merctor grid.
//! It pretty much just does what the MvGridUsingGribIterator class does.

class MvMercatorGrid : public MvGridUsingGribIterator
{
public:
    MvMercatorGrid(field* myfield, field_state oldState, bool memoryToBeReleased);
    ~MvMercatorGrid() {}

    //! Dummy function. Returns always missing_value.
    double firstLonX() { return cMvlatLonMissingValue; }

    //! Dummy function. Returns always missing_value.
    double firstLatY() { return cMvlatLonMissingValue; }
};


//===================================================================
//
// MvIrregularGrid:
// ------------------
//! \brief MvGrid parent class for fields in Gaussian and reduced_latlong grid
//!
//! An abstract parent class for fields in Gaussian and reduced_latlong grid.
//!

class MvIrregularGrid : public MvGridBase
{
public:
    MvIrregularGrid(field* myfield, field_state oldState, bool memoryToBeReleased);
    ~MvIrregularGrid();

    virtual void init() override;
    virtual double firstLonX() override;
    virtual double firstLatY() override;
    virtual double firstLonXInArea(int row);
    virtual double lastLonXInArea(int row);
    void currentBoundingLats(double& lat1, double& lat2);
    bool surroundingGridpoints(double lat_y, double lon_x, std::vector<MvGridPoint>& points,
                               bool canHaveMissing, bool doSort) override;
    virtual bool surroundingGridpointsForExtrapolation(double lat_y, double lon_x, std::vector<MvGridPoint>& points,
                                                       bool canHaveMissing, bool doSort) = 0;
    virtual MvGridPoint nearestGridpoint(double lat_y, double lon_x, bool nearestValid) override;
    virtual double interpolatePoint(double lat_y, double lon_x, std::vector<MvGridPoint>* surroundingPoints) override;
    virtual int pointsInRow(int row) = 0;
    bool globalNS_;
    bool globalEW_;
    virtual double extrapolatePoint(double lat_y, double lon_x, std::vector<MvGridPoint>* surroundingPoints) = 0;
    double lastLonX();
    double lastLatY();
    void gridCellArea(MvGridBase*) override;
    bool isGridCellAreaSupported() const override { return true; }
    void boundingBox(std::vector<double>& v) override;

protected:
    void checkAreaLimits();
    int findLatIndex(double latitude);
    bool computeLonIndex(double lon_x, double firstLon, double dataLonWidth, double dx, int pointsInRow, int& idx1, int& idx2) const;

    long numGlobalParallels_{0};
    double* latitudes_{nullptr};
    int firstLatIndex_{0};
    int lastLatIndex_{0};
    int currentLatIndex_{0};
    bool isSouthToNorthScanning_{false};
    double firstLonX_{0.};
    double lastLonX_{0.};
};


//===================================================================
//
// MvReducedLatLongGrid:
// ---------------------
//! \brief MvGrid class for fields in reduced lat/long grid
//!
//! The number of points on each parallel is taken from
//! the GRIB message.
//!

class MvReducedLatLongGrid : public MvIrregularGrid
{
public:
    MvReducedLatLongGrid(field* myfield, field_state oldState, bool memoryToBeReleased);
    ~MvReducedLatLongGrid();

    virtual bool advance();
    virtual int pointsInRow(int row);
    bool surroundingGridpointsForExtrapolation(double /*lat_y*/, double /*lon_x*/, std::vector<MvGridPoint>& /*points*/,
                                               bool /*canHaveMissing*/, bool /*doSort*/) { return false; }

    virtual double extrapolatePoint(double, double, std::vector<MvGridPoint>*)
    {
        return DBL_MAX;
    }

protected:
    long* pointsInRow_;
};


//===================================================================
//
// GaussianLatitudes:
// ------------------
//! \brief Retrieves, caches and returns the results of grib_get_gaussian_latitudes()


class GaussianLatitudes
{
public:
    GaussianLatitudes() = default;
    ~GaussianLatitudes() = default;
    int latitudes(long trunc, double* v, unsigned int numLatitudes);

private:
    std::map<long, std::vector<double> > cachedLatitudes_;
};


//===================================================================
//
// MvGaussianGridBase:
// ------------------
//! \brief MvGrid parent class for fields in Gaussian grid
//!
//! An abstract parent class for fields in Gaussian grid.
//!

class MvGaussianGridBase : public MvIrregularGrid
{
public:
    MvGaussianGridBase(field* myfield, field_state oldState, bool memoryToBeReleased);
    ~MvGaussianGridBase() = default;

    bool surroundingGridpointsForExtrapolation(double lat_y, double lon_x, std::vector<MvGridPoint>& points,
                                               bool canHaveMissing, bool doSort);
    double extrapolatePoint(double lat_y, double lon_x, std::vector<MvGridPoint>* surroundingPoints);
    static GaussianLatitudes gLatitudes_;
};

//===================================================================
//
// MvGaussianGrid:
// --------------
//! \brief MvGrid class for fields in Gaussian grid
//!
//! This class is for fields in non-reduced Gaussian grid.
//!

class MvGaussianGrid : public MvGaussianGridBase
{
public:
    MvGaussianGrid(field* myfield, field_state oldState, bool memoryToBeReleased);
    ~MvGaussianGrid() {}

    virtual bool advance();
    virtual int pointsInRow(int row);
};

//===================================================================
//
// MvReducedGaussianGrid:
// ---------------------
//! \brief MvGrid class for fields in reduced Gaussian grid
//!
//! The number of points on each parallel is taken from
//! the GRIB message.
//!

class MvReducedGaussianGrid : public MvGaussianGridBase
{
public:
    MvReducedGaussianGrid(field* myfield, field_state oldState, bool memoryToBeReleased);
    ~MvReducedGaussianGrid() { delete[] pointsInRow_; }

    virtual double firstLonXInArea(int row);
    virtual double lastLonXInArea(int row);
    virtual bool advance();
    virtual int pointsInRow(int row);

protected:
    long* pointsInRow_;
};

//===================================================================
//
// MvSatelliteImage:
// -------------------
//! \brief MvGrid class for Satellite images
//!
//! Note that some member functions have not been implemented.
//!

class MvSatelliteImage : public MvGridUsingGribIterator
{
public:
    MvSatelliteImage(field* myfield, field_state oldState, bool memoryToBeReleased);

    ~MvSatelliteImage();

    //! Dummy function. Returns always missing_value.
    double firstLonX() { return cMvlatLonMissingValue; }

    //! Dummy function. Returns always missing_value.
    double firstLatY() { return cMvlatLonMissingValue; }

    //! Dummy function. Returns always 1.
    double weight() const { return 1; }

    MvGridPoint nearestGridpoint(double, double, bool nearestValid);
    double interpolatePoint(double, double, std::vector<MvGridPoint>* surroundingPoints);
    bool getMatrixNN(double lat_y, double lon_x, MvMatrix& mat);

    //! Returns always false (location info not available).
    bool hasLocationInfo() const { return false; }

    double GetValue(int lin, int col);

protected:
    //	gribsec2_sv*       sec2_;
    unsigned char* Sbuf_;
};


// MvGrid_DEFINED
