/*
 * (C) Copyright 1996-2016 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation
 * nor does it submit to any jurisdiction.
 */

/*! \file GribRegularInterpretor.cc
 \brief Implementation of the Template class GribRegularInterpretor.
 \author Meteorological Visualisation Section, ECMWF

 Started: Mon 18-Apr-2005

 Changes:
 */

#include "GribRegularInterpretor.h"

#include <limits>

#include "ContourLibrary.h"
#include "CustomisedPoint.h"
#include "GribDecoder.h"
#include "LocalTable.h"
#include "Matrix.h"
#include "ProjP.h"
#include "RasterData.h"
#include "Timer.h"

using namespace magics;

GribRegularInterpretor::GribRegularInterpretor() {}

GribRegularInterpretor::~GribRegularInterpretor() {}

void GribInterpretor::longitudesSanityCheck(double& west, double& east) const {
    // make sure that the west longitudes is always inferior to teh East
    // longitudes and always betwwen -180 and 360...

    while (east <= west) {
        // We add 360 to the east ...
        east += 360;
    }

    // We reposition if needed

    while (east > 360) {
        west -= 360.;
        east -= 360.;
    }
}
void GribInterpretor::scaling(GribDecoder& grib, double& scaling, double& offset) const {
    string originalUnits, derivedUnits;
    this->scaling(grib, scaling, offset, originalUnits, derivedUnits);
}

/// @brief The usual PI/180 constant
// static const double DEG_TO_RAD = 0.017453292519943295769236907684886;
/// @brief Earth's quatratic mean radius for WGS-84
static const double EARTH_RADIUS_IN_METERS = 6372797.560856;

/** @brief Computes the arc, in radian, between two WGS-84 positions.
 *
 * The result is equal to <code>Distance(from,to)/EARTH_RADIUS_IN_METERS</code>
 *    <code>= 2*asin(sqrt(h(d/EARTH_RADIUS_IN_METERS )))</code>
 *
 * where:<ul>
 *    <li>d is the distance in meters between 'from' and 'to' positions.</li>
 *    <li>h is the haversine function: <code>h(x)=sin²(x/2)</code></li>
 * </ul>
 *
 * The haversine formula gives:
 *    <code>h(d/R) =
 * h(from.lat-to.lat)+h(from.lon-to.lon)+cos(from.lat)*cos(to.lat)</code>
 *
 * @sa http://en.wikipedia.org/wiki/Law_of_haversines
 */
double distance(double lat1, double lon1, double lat2, double lon2) {
    double latitudeArc  = RAD(lat1 - lat2);
    double longitudeArc = RAD(lon1 - lon2);
    double latitudeH    = sin(latitudeArc * 0.5);
    latitudeH *= latitudeH;
    double lontitudeH = sin(longitudeArc * 0.5);
    lontitudeH *= lontitudeH;
    double tmp = cos(RAD(lat1)) * cos(RAD(lat2));
    return EARTH_RADIUS_IN_METERS * (2.0 * asin(sqrt(latitudeH + tmp * lontitudeH)));
}

void GribInterpretor::new_index(const GribDecoder& grib) {
    // Use the grib Iterator to create the index

    grib_handle* handle = grib.handle();

    int error;

    grib_iterator* iter = grib_iterator_new(handle, 0, &error);

    double lat1, lon1, lat2, lon2, val;
    grib_iterator_next(iter, &lat1, &lon1, &val);
    grib_iterator_next(iter, &lat2, &lon2, &val);

    grib_iterator_delete(iter);
    // Get the fisrt 2 points ...

    indexStep_ = distance(lat1, lon1, lat2, lon2) * 4;
    indexStep_ = 0.5;

    indexLon_ = 360. / indexStep_;
    indexLat_ = 180. / indexStep_;

    /* scanning mode :
        bit	value	meaning
        1	0	Points scan in +i direction
        1	1	Points scan in -i direction
        2	0	Points scan in -j direction
        2	1	Points scan in +j direction
        3	0	Adjacent points in i direction are consecutive
        3	1	Adjacent points in j direction are consecutive
    */
    if (grib.getLong("jScansPositively") == 1 || grib.getLong("iScansPositively") == 0) {
        MagLog::error() << " Scanning mode not yet supported" << endl;
        return;
    }

    helper_ = vector<vector<Index> >((indexLon_ + 1) * (indexLat_ + 1), vector<Index>());

    iter = grib_iterator_new(handle, 0, &error);

    double lat, lon, u;
    int ilat, ilon;
    {
        Timer timer("index", "index");
        int i           = 0;
        bool flag_error = true;
        // prepare enveloppe
        minlon_ = 360;
        maxlon_ = 0;
        minlat_ = 90;
        maxlat_ = -90;

        while (grib_iterator_next(iter, &lat, &lon, &u)) {
            // Send a warning if lat lon are not in the expected rangs :
            if (lat < -90 || lat > 90 || lon < -180 || lon > 360) {
                if (flag_error) {
                    MagLog::warning() << "Check Grib Iterator: Position is not in the expected range [" << lat << ", "
                                      << lon << "]" << endl;
                    flag_error = false;
                }
                continue;
            }

            if (minlat_ > lat)
                minlat_ = lat;
            if (maxlat_ < lat)
                maxlat_ = lat;
            if (minlon_ > lon)
                minlon_ = lon;
            if (maxlon_ < lon)
                maxlon_ = lon;

            ilat = floor((lat + 90) / indexStep_);
            ilon = floor(lon / indexStep_);

            helper_[ilat * indexLon_ + ilon].push_back(Index(i, lat, lon));

            i++;
        }

        west_ = 0;
        east_ = 360.;
        grib_iterator_delete(iter);
    }
}

Index GribInterpretor::nearest(double ulat, double ulon) {
    Index index(-1, 0, 0);
    if (ulat < minlat_)
        return index;
    if (ulat > maxlat_)
        return index;
    if (ulon < minlon_)
        return index;
    if (ulon > maxlon_)
        return index;

    if (ulat == 1000. || ulon == 1000.)
        return index;
    if (ulat >= 90 || ulat <= -90)
        return index;

    int ilat = floor((ulat + 90) / indexStep_);
    int ilon = floor(ulon / indexStep_);
    int lat1, lat2;

    if (ilon == indexLon_)
        ilon = 0;

    lat1 = (ilat == 0) ? ilat : ilat - 1;
    lat2 = (ilat == indexLat_) ? ilat : ilat + 1;

    // now make sure that we have

    double nearest = std::numeric_limits<double>::max();
    for (int clat = lat1; clat <= lat2; clat++) {
        // find the first non empty cell
        // Try ilon ..
        vector<Index> points = helper_[clat * indexLon_ + ilon];
        set<int> lonn;

        lonn.insert(ilon);
        bool empty = true;
        int count  = 0;
        // Try the first cell not empty on the left
        int lon = ilon - 1;

        while (empty && count < indexLon_) {
            if (lon < 0) {
                lon = indexLon_ - 1;
            }

            points = helper_[clat * indexLon_ + lon];
            if (!points.empty()) {
                lonn.insert(lon);
                empty = false;
            }
            count++;
            lon--;
        }
        empty = true;
        count = 0;
        lon   = ilon + 1;
        // try the first cell not empty on the right
        while (empty && count < indexLon_) {
            if (lon >= indexLon_) {
                lon = 0;
            }

            points = helper_[clat * indexLon_ + lon];
            if (!points.empty()) {
                lonn.insert(lon);
                empty = false;
            }
            count++;
            lon++;
        }

        for (set<int>::iterator lon = lonn.begin(); lon != lonn.end(); ++lon) {
            vector<Index> points = helper_[clat * indexLon_ + (*lon)];

            for (vector<Index>::iterator point = points.begin(); point != points.end(); ++point) {
                Index i = *point;

                double dist = distance(ulat, ulon, i.lat_, i.lon_);

                if (dist < nearest) {
                    nearest = dist;
                    index   = i;
                }
            }
        }
    }

    return index;
}

void GribInterpretor::scaling(GribDecoder& grib, double& scaling, double& offset, string& originalUnits,
                              string& derivedUnits) const {
    scaling = 1;
    offset  = 0;

    // First check that they are not derived fields!

    long derived = grib.getLong("generatingProcessIdentifier");
    bool scale   = (derived == 254) ? grib.derived_scaling_ : grib.scaling_;

    if (scale) {
        WebLibrary settings;
        MetaDataCollector needs;
        settings.askId(needs);
        grib.ask(needs);
        settings.getScaling(needs, scaling, offset);
    }
    else {
        scaling = grib.scaling_factor_;
        offset  = grib.scaling_offset_;
    }
    // Add a sanity check : the factor cannot be 0..
    if (scaling == 0)
        scaling = 1;
}

void GribInterpretor::scaling(GribDecoder& grib, Matrix& matrix) const {
    double factor, offset;

    scaling(grib, factor, offset);

    matrix.multiply(factor);
    matrix.plus(offset);
}
void GribInterpretor::index(const GribDecoder& grib) {
    throw MethodNotYetImplemented("GribInterpretor::index");
}

int GribInterpretor::nearest(double lon, double lat, double& nlon, double& nlat) {
    ASSERT(false);
    return -1;
}

void GribInterpretor::raw(GribDecoder& grib, const Transformation& transformation,
                          vector<pair<double, vector<pair<double, CustomisedPoint*> > > >& points, double& minlon,
                          double& maxlon) const {
    Timer timer("grib", "raw");
    double factor, offset;
    scaling(grib, factor, offset);
    int err;

    string uname, vname;

    grib_handle* uc = grib.uHandle(uname);
    grib_handle* vc = grib.vHandle(vname);

    long nblat = grib.getLong("numberOfPointsAlongAMeridian");

    points.reserve(nblat);

    grib_iterator* uiter = grib_iterator_new(uc, 0, &err);
    grib_iterator* viter = grib_iterator_new(vc, 0, &err);
    double missing       = grib.getDouble("missingValue");

    double lat, lon, u, v;
    double last = -99999999;

    minlon = 999999;
    maxlon = -999999;

    while (grib_iterator_next(uiter, &lat, &lon, &u) && grib_iterator_next(viter, &lat, &lon, &v)) {
        if (transformation.in(lon, lat) || transformation.in(lon - 360, lat)) {
            if (minlon > lon)
                minlon = lon;
            if (maxlon < lon)
                maxlon = lon;
            if (u != missing)
                u = (u * factor) + offset;
            if (v != missing)
                v = (v * factor) + offset;

            if (lat != last) {
                last = lat;
                points.push_back(make_pair(lat, vector<pair<double, CustomisedPoint*> >()));
                points.back().second.reserve(nblat * 2);
            }

            CustomisedPoint* point = new CustomisedPoint(lon, lat, "");
            point->insert(make_pair(uname, u));

            point->insert(make_pair(vname, v));
            points.back().second.push_back(make_pair(lon, point));
        }
    }

    grib_iterator_delete(viter);
    grib_iterator_delete(uiter);

    for (vector<pair<double, vector<pair<double, CustomisedPoint*> > > >::iterator ilat = points.begin();
         ilat != points.end(); ++ilat) {
        vector<pair<double, CustomisedPoint*> >& lons = ilat->second;

        CustomisedPoint* first = lons.front().second;
        ;

        // if (last->longitude() - first->longitude() > XResolution(grib)) {
        CustomisedPoint* dup = new CustomisedPoint(first->longitude() + 360, first->latitude(), "");
        dup->insert(make_pair("x_component", (*first)["x_component"]));
        dup->insert(make_pair("y_component", (*first)["y_component"]));
        lons.push_back(make_pair(dup->longitude(), dup));
        //}
    }
}

void GribInterpretor::raw(GribDecoder& grib, const Transformation& transformation, const string& key,
                          map<double, map<double, CustomisedPoint*> >& points) const {
    Timer timer("grib", "raw");
    double factor, offset;
    scaling(grib, factor, offset);
    int err;
    size_t nb;
    grib_get_size(grib.id(), "values", &nb);

    map<double, map<double, CustomisedPoint*> >::iterator ilat;
    map<double, CustomisedPoint*>::iterator ilon;

    grib_iterator* iter = grib_iterator_new(grib.handle(), 0, &err);
    double missing      = grib.getDouble("missingValue");

    double lat, lon, value;

    /* Loop on all the lat/lon/values. */
    while (grib_iterator_next(iter, &lat, &lon, &value)) {
        if (transformation.in(lon, lat) || transformation.in(lon - 360, lat)) {
            if (value != missing)
                value = (value * factor) + offset;

            ilat = points.find(lat);
            if (ilat == points.end()) {
                points.insert(make_pair(lat, map<double, CustomisedPoint*>()));
                ilat = points.find(lat);
            }
            ilon = ilat->second.find(lon);

            if (ilon == ilat->second.end()) {
                CustomisedPoint* pt = new CustomisedPoint(lon, lat, "");
                pt->missing(true);
                ilat->second.insert(make_pair(lon, pt));
                ilon = ilat->second.find(lon);
            }
            ilon->second->insert(make_pair(key, value));
        }
    }

    /* At the end the iterator is deleted to free memory. */
    grib_iterator_delete(iter);
}
/*!
 Class information are given to the output-stream.
 */
void GribRegularInterpretor::print(ostream& out) const {
    out << "GribRegularInterpretor[";
    out << "]";
}

void GribRegularInterpretor::interpretAsMatrix(GribDecoder& grib, const Transformation&) const {
    interpretAsMatrix(grib);
}

void GribRegularInterpretor::index(const GribDecoder& grib) {
    if (!index_.empty())
        return;
    long nblon = grib.getLong("numberOfPointsAlongAParallel");

    west_ = grib.getDouble("longitudeOfFirstGridPointInDegrees");

    east_ = grib.getDouble("longitudeOfLastGridPointInDegrees");

    longitudesSanityCheck(west_, east_);

    double lon = (east_ - west_) / (nblon - 1);

    vector<double> latitudes;
    vector<double> longitudes;

    this->latitudes(grib, latitudes);

    double x = west_;
    for (int i = 0; i < nblon; i++) {
        longitudes.push_back(x);
        x = west_ + (i + 1) * lon;
    }

    long jPointsAreConsecutive = grib.getLong("jPointsAreConsecutive");
    int index                  = 0;
    try {
        if (jPointsAreConsecutive) {
            for (vector<double>::iterator lat = latitudes.begin(); lat != latitudes.end(); ++lat) {
                index_.insert(make_pair(*lat, map<double, int>()));
            }
            for (vector<double>::iterator lon = longitudes.begin(); lon != longitudes.end(); ++lon) {
                for (vector<double>::iterator lat = latitudes.begin(); lat != latitudes.end(); ++lat) {
                    index_[*lat].insert(make_pair(*lon, index));
                    index++;
                }
            }
        }
        else  // otherwise, just copy the array of values as they are
        {
            for (vector<double>::iterator lat = latitudes.begin(); lat != latitudes.end(); ++lat) {
                index_.insert(make_pair(*lat, map<double, int>()));
                for (vector<double>::iterator lon = longitudes.begin(); lon != longitudes.end(); ++lon) {
                    index_[*lat].insert(make_pair(*lon, index));
                    index++;
                }
            }
        }
    }

    catch (...) {
        if (MagicsGlobal::strict()) {
            throw;
        }
        throw MagicsException("GribRegularInterpretor - Not enough memory");
    }
}

void GribRegularInterpretor::interpretAsMatrix(GribDecoder& grib) const {
    Timer timer("gribapi", " read grib");
    MagLog::dev() << "GribRegularInterpretor::interpretAsMatrix"
                  << "\n";
    long nblon = grib.getLong("numberOfPointsAlongAParallel");
    long nblat = grib.getLong("numberOfPointsAlongAMeridian");

    Matrix* u = grib.u(new Matrix(nblat, nblon));
    Matrix* v = grib.v(new Matrix(nblat, nblon));
    Matrix* c = grib.colour(new Matrix(nblat, nblon));

    size_t nb;
    grib_get_size(grib.id(), "values", &nb);

    MagLog::dev() << "numberOfFieldValues[" << nb << "]"
                  << "\n";
    double missing = INT_MAX;
    grib.setDouble("missingValue", missing);
    u->missing(missing);
    u->akimaEnabled();

    double north = grib.getDouble("latitudeOfFirstGridPointInDegrees");
    double west  = grib.getDouble("longitudeOfFirstGridPointInDegrees");
    double south = grib.getDouble("latitudeOfLastGridPointInDegrees");
    double east  = grib.getDouble("longitudeOfLastGridPointInDegrees");

    longitudesSanityCheck(west, east);

    MagLog::dev() << "NewAPI---> area[" << west << ", " << north << ", " << east << ", " << south << "]"
                  << "\n";
    double loni = longitudeIncrement(grib);

    double lon = (east - west) / (nblon - 1);

    MagLog::dev() << "increment -->" << loni << " (from->" << west << " to-->" << west + (nblon - 1) * loni << ")"
                  << endl;
    MagLog::dev() << "calcul -->" << lon << " (from->" << west << " to-->" << west + (nblon - 1) * lon << ")" << endl;

    latitudes(grib, u->rowsAxis());

    if (v != NULL) {
        u->xIndex_.reserve(u->rowsAxis().size());
        int offset = 0;
        int l      = 0;
        for (vector<double>::iterator lat = u->rowsAxis().begin(); lat != u->rowsAxis().end(); ++lat) {
            u->yIndex_.insert(make_pair(*lat, l));
            l++;
            u->xIndex_.push_back(InfoIndex(west, east, nblon, offset));
            offset += nblon;
        }
        u->data_.resize(nb);
        v->data_.resize(nb);
    }
    double x = west;
    for (int i = 0; i < nblon; i++) {
        u->columnsAxis().push_back(x);
        x = west + (i + 1) * lon;
    }

    u->setMapsAxis();

    long jPointsAreConsecutive = grib.getLong("jPointsAreConsecutive");

    try {
        u->resize(nb);
        size_t aux = size_t(nb);

        // if jPointsAreConsecutive=1 then the values represent columns of data
        // instead of rows, so we have to 'reshape' the array so that it is
        // reorganised into rows.

        if (jPointsAreConsecutive) {
            vector<double>* d = new vector<double>(nb);  // temporary array
            double* d1        = &d->front();             // temporary array pointer
            double* d2        = &u->front();             // final array

            grib_get_double_array(grib.id(), "values", d1, &aux);

            for (int i = 0; i < nblon; i++) {
                for (int j = 0; j < nblat; j++) {
                    d2[j * nblon + i] = d1[i * nblat + j];
                }
            }
            delete d;
        }
        else  // otherwise, just copy the array of values as they are
        {
            if (v != NULL) {
                grib_get_double_array(grib.uHandle(), "values", &u->front(), &aux);
                grib_get_double_array(grib.uHandle(), "values", &u->data_.front(), &aux);
                grib_get_double_array(grib.vHandle(), "values", &v->front(), &aux);
                grib_get_double_array(grib.vHandle(), "values", &v->data_.front(), &aux);
                if (c) {
                    c->data_.resize(nb);
                    grib_get_double_array(grib.cHandle(), "values", &c->front(), &aux);
                    grib_get_double_array(grib.cHandle(), "values", &c->data_.front(), &aux);
                }
            }
            else
                grib_get_double_array(grib.handle(), "values", &u->front(), &aux);
        }
        
        u->missing(missing);
    }
    catch (...) {
        if (MagicsGlobal::strict()) {
            throw;
        }
        throw MagicsException("GribRegularInterpretor - Not enough memory");
    }
}

void GribRegularInterpretor::latitudes(const GribDecoder& grib, vector<double>& latitudes) const {
    double north = grib.getDouble("latitudeOfFirstGridPointInDegrees");

    long nblat   = grib.getLong("numberOfPointsAlongAMeridian");
    int scanning = grib.getLong("jScansPositively") ? 1 : -1;
    double lat   = scanning * grib.getDouble("jDirectionIncrementInDegrees");

    double y = north;
    for (int i = 0; i < nblat; i++) {
        latitudes.push_back(y);
        y += lat;
    }
}

void GribRegularGaussianInterpretor::latitudes(const GribDecoder& grib, vector<double>& latitudes) const {
    long res      = grib.getLong("numberOfParallelsBetweenAPoleAndTheEquator");
    double* array = new double[2 * res];
    grib_get_gaussian_latitudes(res, array);
    double first = grib.getDouble("latitudeOfFirstGridPointInDegrees");
    double last  = grib.getDouble("latitudeOfLastGridPointInDegrees");
    ;
    // get the scanning mode !
    double north  = first;
    double south  = last;
    long scanning = grib.getLong("jScansPositively");
    ;
    if (scanning == 1) {
        north = last;
        south = first;
    }

    for (int i = 0; i < 2 * res; i++) {
        if (same(array[i], north, 10e-2)) {
            latitudes.push_back(array[i]);
            continue;
        }
        if (same(array[i], south, 10e-2)) {
            latitudes.push_back(array[i]);
            continue;
        }
        if (array[i] < north && array[i] > south)
            latitudes.push_back(array[i]);
    }
    delete[] array;

    if (scanning == 1)
        std::reverse(latitudes.begin(), latitudes.end());
}

double GribRegularInterpretor::longitudeIncrement(const GribDecoder& grib) const {
    int scanning = grib.getLong("iScansNegatively") ? -1 : 1;
    return scanning * grib.getDouble("iDirectionIncrementInDegrees");
}

PaperPoint GribInterpretor::reference(const GribDecoder& grib, const Transformation& transformation) {
    double lon = grib.getDouble("longitudeOfFirstGridPointInDegrees");
    double lat = grib.getDouble("latitudeOfFirstGridPointInDegrees");

    UserPoint point(grib.getDouble("longitudeOfFirstGridPointInDegrees"),
                    grib.getDouble("latitudeOfFirstGridPointInDegrees"));

   
    if (transformation.in(point))
        return transformation(point);
    int err;
    grib_iterator* iter = grib_iterator_new(grib.handle(), 0, &err);

    double value;
    /* Loop on all the lat/lon/values. */
    while (grib_iterator_next(iter, &lat, &lon, &value)) {
        point = UserPoint(lon, lat);
        if (transformation.in(point)) {
            grib_iterator_delete(iter);
            return transformation(point);
        }
    }

    /* At the end the iterator is deleted to free memory. */
    grib_iterator_delete(iter);
    return PaperPoint(transformation.getMinPCX(), transformation.getMinPCY());
}

void GribRegularInterpretor::interpretAsRaster(const GribDecoder& grib, RasterData& raster,
                                               const Transformation& transformation) const {
    MagLog::dev() << "GribRegularInterpretor::interpretAsRaster"
                  << "\n";

    BoxMatrixHandler box(const_cast<GribDecoder*>(&grib)->matrix(), transformation);

    int nblon    = box.columns();
    int nblat    = box.rows();
    double east  = box.column(0, nblon - 1);
    double west  = box.column(0, 0);
    double south = box.row(0, 0);
    double north = box.row(nblat - 1, 0);

    raster.setUpperRightCorner(east, north);
    raster.setLowerLeftCorner(west, south);

    double lon = (east - west) / (nblon - 1);
    double lat = (north - south) / (nblat - 1);

    raster.setXResolution(lon);
    raster.setYResolution(lat);

    raster.setColumns(nblon);
    raster.setRows(nblat);
    //
    raster.setProjection(new TeLatLong(TeDatum()));

    raster.reserve(nblon * nblat);

    for (int j = nblat - 1; j >= 0; j--)
        for (int i = 0; i < nblon; i++)
            raster.push_back(box(j, i));
}

void GribReducedGaussianInterpretor::interpretAsRaster(const GribDecoder& grib, RasterData& raster,
                                                       const Transformation& transformation) const {
    MagLog::dev() << "GribRegularInterpretor::interpretAsRaster"
                  << "\n";
    Timer timer("grib api", "read grib");
    BoxMatrixHandler box(const_cast<GribDecoder*>(&grib)->matrix(), transformation);

    int nblon    = box.columns();
    int nblat    = box.rows();
    double east  = box.column(0, nblon - 1);
    double west  = box.column(0, 0);
    double south = box.row(0, 0);
    double north = box.row(nblat - 1, 0);

    raster.setUpperRightCorner(east, north);
    raster.setLowerLeftCorner(west, south);

    double lon = (east - west) / (nblon - 1);
    double lat = (north - south) / (nblat - 1);

    raster.setXResolution(lon);
    raster.setYResolution(lat);

    raster.setColumns(nblon);
    raster.setRows(nblat);
    //
    raster.setProjection(new TeLatLong(TeDatum()));

    raster.reserve(nblon * nblat);

    for (int j = nblat - 1; j >= 0; j--)
        for (int i = 0; i < nblon; i++)
            raster.push_back(box(j, i));
}

void GribReducedGaussianInterpretor::print(ostream& out) const {
    out << "GribRegularInterpretor[";
    out << "]";
}

void GribReducedGaussianInterpretor::interpretAsMatrix(GribDecoder& grib, const Transformation& transformation) const {
    interpretAsMatrix(grib);
}
double GribReducedLatLonInterpretor::XResolution(const GribDecoder& grib) const {
    long res = grib.getLong("Nj");

    double west = grib.getDouble("longitudeOfFirstGridPointInDegrees");
    double east = grib.getDouble("longitudeOfLastGridPointInDegrees");
    ;

    longitudesSanityCheck(west, east);

    return (east - west) / (2 * res);
}
double GribReducedGaussianInterpretor::XResolution(const GribDecoder& grib) const {
    long res    = grib.getLong("numberOfParallelsBetweenAPoleAndTheEquator");
    double west = grib.getDouble("longitudeOfFirstGridPointInDegrees");

    double east = grib.getDouble("longitudeOfLastGridPointInDegrees");
    ;

    longitudesSanityCheck(west, east);
    return (east - west) / (4 * res);
}

bool greaterThan(double a, double b) {
    if (a > b)
        return true;
    if (same(a, b))
        return true;
    return false;
}

bool lessThan(double a, double b) {
    if (a < b)
        return true;
    if (same(a, b))
        return true;
    return false;
}

void GribReducedGaussianInterpretor::interpretAsMatrix(GribDecoder& grib) const {
    Timer timer("gribapi", " read grib");

    Matrix* u = grib.u(new Matrix());
    Matrix* v = grib.v(new Matrix());
    Matrix* c = grib.colour(new Matrix());

    size_t nb;
    grib_get_size(grib.uHandle(), "values", &nb);
    GribDecoder::InterpolateMethod interpolate = grib.interpolateMethod();
    MagLog::dev() << "numberOfFieldValues[" << nb << "]"
                  << "\n";
    double missing = std::numeric_limits<double>::max();

    grib.setDouble("missingValue", missing);

    u->missing(missing);
    u->akimaEnabled();

    double north = grib.getDouble("latitudeOfFirstGridPointInDegrees");
    double west  = grib.getDouble("longitudeOfFirstGridPointInDegrees");
    double south = grib.getDouble("latitudeOfLastGridPointInDegrees");
    double east  = grib.getDouble("longitudeOfLastGridPointInDegrees");
    double plp   = grib.getDouble("PLPresent");
    long res     = grib.getLong("numberOfParallelsBetweenAPoleAndTheEquator");
    long nblat   = grib.getLong("Nj");
    longitudesSanityCheck(west, east);
    MagLog::dev() << "NewAPI---> area[" << west << ", " << north << ", " << east << ", " << south << "]"
                  << "\n";
    MagLog::dev() << "PLPresent---> " << plp << "\n";
    MagLog::dev() << "Res---> " << res << "\n";

    double* pl = new double[2 * res];
    size_t aux = 2 * res;
    grib_get_double_array(grib.uHandle(), "pl", pl, &aux);
    int nblon = 0;
    int ii    = 0;

    long global = grib.getLong("global");

    // We have to determine if the field is global!
    if (global) {
        east = west + 360.;
    }
    vector<vector<double> > rows;

    for (int i = 0; i < nblat; i++) {
        long numPts               = 0;
        int globalPointsInThisRow = pl[i];
        double dx                 = 360. / pl[i];

        // find which points are in this row; a sub-area of a reduced Gaussian grid
        // contains a subset of the points from the global grid, with the points
        // in exactly the same positions as the corresponding ones in the
        // global grid.

        double add = 0;

        double lonfirst = west, lonlast = east;
        if (global) {
            numPts = globalPointsInThisRow;
        }
        else {
            grib_get_reduced_row_p(globalPointsInThisRow, west, east, &numPts, &lonfirst, &lonlast);
            add = 2 * dx;
        }

        rows.push_back(vector<double>());

        double thisLon = lonfirst;
        int n          = 0;
        while (n < numPts) {
            if (thisLon > east + add)
                thisLon -= 360.;

            rows.back().push_back(thisLon);
            n++;
            thisLon = lonfirst + (n * dx);
        }

        if (rows.back().size() > nblon)
            nblon = rows.back().size();
        ii += rows.back().size();
    }
    delete[] pl;

    // Find the latitude
    // map<double, map<double, int> > index;
    double* array = new double[2 * res];
    grib_get_gaussian_latitudes(res, array);
    int j = 0;
    for (int i = 0; i < 2 * res; i++) {
        if (global) {
            u->rowsAxis().push_back(array[i]);
            u->yIndex_.insert(make_pair(array[i], i));
        }
        else {
            if (lessThan(array[i], north) && greaterThan(array[i], south)) {
                u->rowsAxis().push_back(array[i]);
                u->yIndex_.insert(make_pair(array[i], j));
                j++;
            }
        }
    }
    delete[] array;
    if (v) {
        u->xIndex_.reserve(u->rowsAxis().size());
    }

    // compute the number of points we'll be adding to the matrix so that we can
    // allocate them in one go, rather than allowing the STL to re-allocate
    // when we reach the capacity

    double* data1 = new double[nb];
    double* data2 = new double[nb];
    double* data3 = new double[nb];

    u->reserve(aux * nblon);
    if (v) {
        v->reserve(aux * nblon);
        u->data_.reserve(nb);
        v->data_.reserve(nb);

        if (c) {
            c->reserve(aux * nblon);
            c->data_.reserve(nb);
        }
    }

    double width = east - west;
    double step  = (width) / (nblon);

    if (v) {
        grib_get_double_array(grib.uHandle(), "values", data1, &nb);
        grib_get_double_array(grib.vHandle(), "values", data2, &nb);
        if (c)
            grib_get_double_array(grib.cHandle(), "values", data3, &nb);
    }
    else
        grib_get_double_array(grib.id(), "values", data1, &nb);

    int d = 0;

    vector<double> missingLon;

    d = 0;
    {
        Timer timer("map2", " pair");
        bool first = true;
        for (vector<vector<double> >::iterator row = rows.begin(); row != rows.end(); ++row) {
            vector<double> p;
            p.reserve(row->size());
            if (v) {
                u->xIndex_.push_back(InfoIndex(row->front(), row->back(), row->size(), d));
            }

            for (int ii = 0; ii < row->size(); ii++) {
                p.push_back(data1[d]);
                if (v) {
                    u->data_.push_back(data1[d]);
                    v->data_.push_back(data2[d]);

                    if (c) {
                        c->data_.push_back(data3[d]);
                        // cout << data1[d] << " " << data2[d] << data3[d] << endl;
                    }
                }
                d++;
            }

            if (interpolate == GribDecoder::nearest_valid) {
                // Fill left vector
                vector<double> left;
                int last   = 0;
                int c      = 0;
                bool print = false;
                for (vector<double>::iterator val = p.begin(); val != p.end(); ++val) {
                    if (*val != missing) {
                        last = c;
                    }
                    else {
                        print = true;
                    }
                    left.push_back(last);
                    c++;
                }
                // Fill right vector
                vector<double> right(p.size(), 0);
                last = 0;
                c    = p.size() - 1;
                for (vector<double>::reverse_iterator val = p.rbegin(); val != p.rend(); ++val) {
                    if (*val != missing) {
                        last = c;
                    }
                    right[c] = last;
                    c--;
                }

                int fill = grib.missingFill();

                int todo = 0;
                for (int ii = 0; ii < p.size(); ii++) {
                    double keep = p[ii];
                    if (p[ii] == missing && todo < fill) {
                        double lon  = (*row)[ii];
                        double lon1 = (*row)[left[ii]];
                        double lon2 = (*row)[right[ii]];
                        double d1   = (lon2 - lon) / (lon2 - lon1);
                        double d2   = 1 - d1;
                        p[ii]       = (d2 < 0.5) ? p[left[ii]] : p[right[ii]];
                        todo++;
                    }
                    if (keep != missing) {
                        todo = 0;
                    }
                }

                todo = 0;
                for (int ii = p.size() - 1; ii >= 0; ii--) {
                    double keep = p[ii];
                    if (p[ii] == missing && todo < fill) {
                        double lon  = (*row)[ii];
                        double lon1 = (*row)[left[ii]];
                        double lon2 = (*row)[right[ii]];
                        double d1   = (lon2 - lon) / (lon2 - lon1);
                        double d2   = 1 - d1;
                        p[ii]       = (d2 < 0.5) ? p[left[ii]] : p[right[ii]];
                        todo++;
                    }
                    if (keep != missing) {
                        todo = 0;
                    }
                }
            }

            double lon      = west;
            unsigned int p1 = 0;
            unsigned int p2 = 1;

            vector<double>::iterator r = row->begin();
            double lon1                = *r;
            r++;
            double lon2 = *r;

            int x = 0;

            while (x < nblon) {
                if (lon < lon1) {
                    u->push_back(p[0]);
                    x++;
                    lon = west + (x * step);
                    continue;
                }
                if (lon >= row->back()) {
                    if (global) {
                        p2   = 0;
                        lon2 = 360.;
                        lon1 = row->back();
                        p1   = p.size() - 1;
                    }
                    else {
                        u->push_back(p.back());
                        x++;
                        lon = west + (x * step);
                        continue;
                    }
                }
                if (lon > lon2) {
                    p1++;
                    p2++;

                    lon1 = lon2;

                    r++;
                    if (r == row->end())
                        r--;
                    lon2 = (*r);
                }

                double d1 = (lon2 - lon) / (lon2 - lon1);
                double d2 = 1 - d1;
                double val;

                if (interpolate == GribDecoder::interpolate) {
                    if (p[p1] == missing)
                        val = p[p2];
                    else if (p[p2] != missing)
                        val = (p[p1] * d1) + (p[p2] * d2);

                    u->push_back(val);
                }
                else {
                    val = (d2 < 0.5) ? p[p1] : p[p2];
                    u->push_back(val);
                }

                x++;
                lon = west + (x * step);
            }
        }
       
    }
    // delete[] data;
    delete[] data1;
    delete[] data2;
    delete[] data3;

    for (int x = 0; x < nblon; x++) {
        u->columnsAxis().push_back(west + (x * step));
    }

    u->setMapsAxis();
}

void GribReducedLatLonInterpretor::print(ostream& out) const {
    out << "GribReducedLatLonInterpretor[";
    out << "]";
}

void GribReducedGaussianInterpretor::index(GribDecoder const& grib) {}
void GribReducedLatLonInterpretor::interpretAsMatrix(GribDecoder& grib) const {
    Timer timer("gribapi", " read grib");
    Matrix* matrix = grib.u(new Matrix());
    Matrix* v      = grib.v(new Matrix());
    Matrix* c      = grib.v(new Matrix());
    size_t nb;
    grib_get_size(grib.id(), "values", &nb);
    GribDecoder::InterpolateMethod interpolate = grib.interpolateMethod();

    double missing = std::numeric_limits<double>::max();
    grib.setDouble("missingValue", missing);

    matrix->missing(missing);
    matrix->akimaEnabled();

    double north = grib.getDouble("latitudeOfFirstGridPointInDegrees");
    double west  = grib.getDouble("longitudeOfFirstGridPointInDegrees");
    double south = grib.getDouble("latitudeOfLastGridPointInDegrees");
    double east  = grib.getDouble("longitudeOfLastGridPointInDegrees");

    longitudesSanityCheck(west, east);
    size_t res = grib.getLong("Nj");

    MagLog::dev() << "NewAPI---> area[" << west << ", " << north << ", " << east << ", " << south << "]"
                  << "\n";
    MagLog::dev() << "Res---> " << res << "\n";

    double* pl = new double[res];

    long nblat   = grib.getLong("numberOfPointsAlongAMeridian");
    int scanning = grib.getLong("jScansPositively") ? 1 : -1;
    double lat   = scanning * grib.getDouble("jDirectionIncrementInDegrees");

    grib_get_double_array(grib.id(), "pl", pl, &res);

    double* data  = new double[nb];
    double* data2 = new double[nb];

    size_t aux = size_t(nb);

    int nblon = 0;
    for (int i = 0; i < res; i++)
        if (nblon < pl[i])
            nblon = pl[i];

    double width = east - west;
    double step  = width / nblon;

    // We have to determine if the field is global!
    // We have to determine if the field is global!
    bool global = east - west > 360 - 5 * step;

    if (global) {
        east  = west + 360;
        width = east - west;
        step  = width / nblon;
    }

    map<double, map<double, int> > index;
    for (int x = 0; x < nblon; x++) {
        matrix->columnsAxis().push_back(west + (x * step));
    }

    double y = north;
    for (long i = 0; i < nblat; i++) {
        matrix->rowsAxis().push_back(y);
        if (v)
            matrix->index_.insert(make_pair(y, map<double, pair<double, double> >()));

        y += lat;
    }

    if (v) {
        v->reserve(aux * nblon);
        v->data_.reserve(nb);
        matrix->data_.reserve(nb);
    }

    if (v) {
        grib_get_double_array(grib.uHandle(), "values", data, &aux);
        grib_get_double_array(grib.uHandle(), "values", data2, &aux);
    }
    else
        grib_get_double_array(grib.id(), "values", data, &aux);
    int d = 0;

    for (size_t i = 0; i < res; i++) {
        double lat = matrix->rowsAxis()[i];
        matrix->yIndex_.insert(make_pair(lat, i));
        if (v)
            matrix->xIndex_.push_back(InfoIndex(west, east, pl[i], d));

        if (pl[i] == 0) {
            // add missing data
            for (int x = 0; x < nblon; x++)
                matrix->push_back(missing);
        }
        else {
            vector<double> p;
            vector<double> lons;

            double datastep = width / pl[i];
            {
                Timer timer("map", "pair");

                for (int ii = 0; ii < pl[i]; ii++) {
                    p.push_back(data[d]);
                    double lon = west + (ii * datastep);
                    lons.push_back(lon);
                    if (v) {
                        matrix->data_.push_back(data[d]);
                        v->data_.push_back(data2[d]);
                    }

                    d++;
                }
            }

            vector<double>::iterator val  = p.begin();
            vector<double>::iterator lval = lons.begin();
            vector<double>::iterator nval = p.begin();
            nval++;
            vector<double>::iterator nlval = lons.begin();
            nlval++;

            for (int x = 0; x < nblon; x++) {
                double lon = west + (x * step);

                if (lon > *nlval) {
                    lval++;
                    val++;

                    if (lval == lons.end()) {
                        val  = p.begin();
                        lval = lons.begin();
                    }
                    nval++;
                    nlval++;
                    if (nlval == lons.end()) {
                        nval  = p.begin();
                        nlval = lons.begin();
                    }
                }

                double d1 = (*nlval - lon) / (datastep);
                if (d1 < 0)
                    d1 = 1;
                double d2 = 1 - d1;
                double value;

                if (interpolate == GribDecoder::interpolate) {
                    if (*val == missing || *nval == missing)
                        value = missing;
                    else
                        value = (d1 * (*val)) + (d2 * (*nval));
                }
                else {
                    value = (d2 < 0.5) ? *val : *nval;
                }
                matrix->push_back(value);
            }
        }
    }

    delete[] pl;
    delete[] data;
    delete[] data2;

    matrix->setMapsAxis();
}

/*
 * Imported from Metview MvGrid...
 */

void GribRotatedInterpretor::print(ostream& out) const {
    out << "GribRotatedInterpretor[]";
}

UserPoint GribLambertAzimutalInterpretor::unrotate(double lat, double lon) const {
    return UserPoint(lon, lat);
}

double GribLambertAzimutalInterpretor::XResolution(const GribDecoder& grib) const {
    return 0.0125;
}

pair<double, double> GribRotatedInterpretor::unrotate(double lat_y, double lon_x) const {
    const double cToRadians = M_PI / 180.0;
    double ZRADI            = 1. / cToRadians;
    double ZSYCEN           = sin(cToRadians * (southPoleLat_ + 90.));
    double ZCYCEN           = cos(cToRadians * (southPoleLat_ + 90.));
    double ZSXROT           = sin(cToRadians * lon_x);
    double ZCXROT           = cos(cToRadians * lon_x);
    double ZSYROT           = sin(cToRadians * lat_y);
    double ZCYROT           = cos(cToRadians * lat_y);
    double ZSYREG           = ZCYCEN * ZSYROT + ZSYCEN * ZCYROT * ZCXROT;
    ZSYREG                  = MAX(MIN(ZSYREG, +1.0), -1.0);
    double PYREG            = asin(ZSYREG) * ZRADI;
    double ZCYREG           = cos(PYREG * cToRadians);
    double ZCXMXC           = (ZCYCEN * ZCYROT * ZCXROT - ZSYCEN * ZSYROT) / ZCYREG;
    ZCXMXC                  = MAX(MIN(ZCXMXC, +1.0), -1.0);
    double ZSXMXC           = ZCYROT * ZSXROT / ZCYREG;
    double ZXMXC            = acos(ZCXMXC) * ZRADI;
    if (ZSXMXC < 0.0)
        ZXMXC = -ZXMXC;
    double PXREG = ZXMXC + southPoleLon_;
    return std::make_pair(PYREG, PXREG);
}

PaperPoint GribRotatedInterpretor::reference(const GribDecoder& grib, const Transformation& transformation) {
    double lon              = grib.getDouble("longitudeOfFirstGridPointInDegrees");
    double lat              = grib.getDouble("latitudeOfFirstGridPointInDegrees");
    pair<double, double> xy = unrotate(lat, lon);
    UserPoint point(xy.second, xy.first);
    if (transformation.in(point))
        return transformation(point);

    int err;
    grib_iterator* iter = grib_iterator_new(grib.handle(), 0, &err);

    double value;
    /* Loop on all the lat/lon/values. */
    while (grib_iterator_next(iter, &lat, &lon, &value)) {
        pair<double, double> xy = unrotate(lat, lon);
        point                   = UserPoint(xy.second, xy.first);
        if (transformation.in(point)) {
            grib_iterator_delete(iter);
            return transformation(point);
        }
    }

    /* At the end the iterator is deleted to free memory. */
    grib_iterator_delete(iter);
    return PaperPoint(transformation.getMinPCX(), transformation.getMinPCY());
}
pair<double, double> GribRotatedInterpretor::rotate(double lat_y, double lon_x) const {
    const double cToRadians = M_PI / 180.0;
    double ZRADI            = 1. / cToRadians;
    double ZSYCEN           = sin(cToRadians * (southPoleLat_ + 90.));
    double ZCYCEN           = cos(cToRadians * (southPoleLat_ + 90.));

    double ZXMXC  = cToRadians * (lon_x - southPoleLon_);
    double ZSXMXC = sin(ZXMXC);
    double ZCXMXC = cos(ZXMXC);
    double ZSYREG = sin(cToRadians * lat_y);
    double ZCYREG = cos(cToRadians * lat_y);
    double ZSYROT = ZCYCEN * ZSYREG - ZSYCEN * ZCYREG * ZCXMXC;
    ZSYROT        = MAX(MIN(ZSYROT, +1.0), -1.0);

    double PYROT = asin(ZSYROT) * ZRADI;

    double ZCYROT = cos(PYROT * cToRadians);
    double ZCXROT = (ZCYCEN * ZCYREG * ZCXMXC + ZSYCEN * ZSYREG) / ZCYROT;
    ZCXROT        = MAX(MIN(ZCXROT, +1.0), -1.0);
    double ZSXROT = ZCYREG * ZSXMXC / ZCYROT;

    double PXROT = acos(ZCXROT) * ZRADI;

    if (ZSXROT < 0.0)
        PXROT = -PXROT;

    return std::make_pair(PYROT, PXROT);
}

void GribLambertAzimutalInterpretor::interpretAsMatrix(GribDecoder& grib) const {
    // do not support non-spherical Earths
    if (grib.isEarthOblate()) {
        std::string msg("Lambert Azimuthal Equal Area only supported for spherical earth");
        MagLog::error() << msg << std::endl;
        throw MagicsException(msg);
    }

    long im = grib.getLong("numberOfPointsAlongXAxis");
    long jm = grib.getLong("numberOfPointsAlongYAxis");

    Matrix* rotated = grib.u(new Matrix(jm, im));

    size_t nb;
    grib_get_size(grib.id(), "values", &nb);

    MagLog::dev() << "numberOfFieldValues[" << nb << "]"
                  << "\n";

    double missing = -std::numeric_limits<double>::max();

    missing = grib.getDouble("missingValue");
    rotated->missing(missing);

    double north = grib.getDouble("latitudeOfFirstGridPointInDegrees");
    double west  = grib.getDouble("longitudeOfFirstGridPointInDegrees");

    MagLog::dev() << "NewAPI---> area[" << west << ", " << north << "]"
                  << "\n";

    try {
        Timer time("Grib", "lambert");

        MagLog::debug() << "Version" << grib_get_api_version() << endl;

        size_t aux = size_t(nb);

        // grib_get_double_array(grib.id(), "latitudes", &(latPDumpS.front()),
        // &aux);

        grib_get_double_array(grib.id(), "values", &(rotated->front()), &aux);

        // grib_get_double_array(grib.id(), "longitudes", &(lonm.front()), &aux);

        vector<double> rows, columns;
        for (int i = 0; i < im; i++)
            columns.push_back(i);
        for (int i = 0; i < jm; i++)
            rows.push_back(i);

        rotated->setRowsAxis(rows);
        rotated->setColumnsAxis(columns);
        rotated->setMapsAxis();
    }
    catch (MagicsException& e) {
        if (MagicsGlobal::strict()) {
            throw;
        }
        MagLog::error() << e << "\n";
    }
}

void GribLambertAzimutalInterpretor::print(ostream& out) const {
    out << "GribLambertAzimutalInterpretor[]";
}

void GribRotatedInterpretor::interpretAsMatrix(GribDecoder& grib) const {
    southPoleLat_     = grib.getDouble("latitudeOfSouthernPoleInDegrees");
    southPoleLon_     = grib.getDouble("longitudeOfSouthernPoleInDegrees");
    angle_            = grib.getDouble("angleOfRotationInDegrees") * 180.0 / M_PI;
    uvRelativeToGrid_ = grib.getLong("uvRelativeToGrid");

    Timer timer("gribapi", " read grib");
    MagLog::dev() << "GribRegularInterpretor::interpretAsMatrix"
                  << "\n";
    long nblon = grib.getLong("numberOfPointsAlongAParallel");
    long nblat = grib.getLong("numberOfPointsAlongAMeridian");

    Matrix* u = grib.u(new RotatedMatrix(nblat, nblon, southPoleLat_, southPoleLon_));
    Matrix* v = grib.v(new RotatedMatrix(nblat, nblon, southPoleLat_, southPoleLon_));

    size_t nb;
    grib_get_size(grib.id(), "values", &nb);

    MagLog::dev() << "numberOfFieldValues[" << nb << "]"
                  << "\n";
    double missing = INT_MAX;
    grib.setDouble("missingValue", missing);
    u->missing(missing);
    if (v)
        v->missing(missing);

    double north = grib.getDouble("latitudeOfFirstGridPointInDegrees");
    double west  = grib.getDouble("longitudeOfFirstGridPointInDegrees");
    double south = grib.getDouble("latitudeOfLastGridPointInDegrees");
    double east  = grib.getDouble("longitudeOfLastGridPointInDegrees");

    longitudesSanityCheck(west, east);


    double loni = longitudeIncrement(grib);

    double lon = (east - west) / (nblon - 1);

    MagLog::dev() << "increment -->" << loni << " (from->" << west << " to-->" << west + (nblon - 1) * loni << ")"
                  << endl;
    MagLog::dev() << "calcul -->" << lon << " (from->" << west << " to-->" << west + (nblon - 1) * lon << ")" << endl;

    latitudes(grib, u->rowsAxis());

    if (v != NULL) {
        u->xIndex_.reserve(u->rowsAxis().size());
        int offset = 0;
        int l      = 0;
        for (vector<double>::iterator lat = u->rowsAxis().begin(); lat != u->rowsAxis().end(); ++lat) {
            u->yIndex_.insert(make_pair(*lat, l));
            l++;
            u->xIndex_.push_back(InfoIndex(west, east, nblon, offset));
            offset += nblon;
        }
        u->data_.resize(nb);
        v->data_.resize(nb);
    }
    double x = west;
    for (int i = 0; i < nblon; i++) {
        u->columnsAxis().push_back(x);
        x = west + (i + 1) * lon;
    }

    u->setMapsAxis();

    long jPointsAreConsecutive = grib.getLong("jPointsAreConsecutive");

    try {
        u->resize(nb);
        size_t aux = size_t(nb);

        // if jPointsAreConsecutive=1 then the values represent columns of data
        // instead of rows, so we have to 'reshape' the array so that it is
        // reorganised into rows.

        if (jPointsAreConsecutive) {
            vector<double>* d = new vector<double>(nb);  // temporary array
            double* d1        = &d->front();             // temporary array pointer
            double* d2        = &u->front();             // final array

            grib_get_double_array(grib.id(), "values", d1, &aux);

            for (int i = 0; i < nblon; i++) {
                for (int j = 0; j < nblat; j++) {
                    d2[j * nblon + i] = d1[i * nblat + j];
                }
            }
            delete d;
        }
        else  // otherwise, just copy the array of values as they are
        {
            if (v != NULL) {
                grib_get_double_array(grib.uHandle(), "values", &u->front(), &aux);
                grib_get_double_array(grib.uHandle(), "values", &u->data_.front(), &aux);
                grib_get_double_array(grib.vHandle(), "values", &v->front(), &aux);
                grib_get_double_array(grib.vHandle(), "values", &v->data_.front(), &aux);
            }
            else
                grib_get_double_array(grib.handle(), "values", &u->front(), &aux);
        }
        for (int i = 0; i < nblon; i++) {
            double lon = u->columnsAxis()[i];
            for (int j = 0; j < nblat; j++) {
                double lat = u->rowsAxis()[j];
            }
        }
        u->missing(missing);
    }
    catch (...) {
        if (MagicsGlobal::strict()) {
            throw;
        }
        throw MagicsException("GribRegularInterpretor - Not enough memory");
    }
}

void GribRotatedInterpretor::interpret2D(double& lat, double& lon, double& uc, double& vc) const {
    if (!uvRelativeToGrid_)
        return;
    double speed = sqrt((uc * uc) + (vc * vc));
    double angle = atan2(vc, uc);

    std::pair<double, double> pt = rotate(lat, lon);
    std::pair<double, double> pv = unrotate(pt.first, pt.second + 1);

    double rangle = atan2(pv.first - lat, pv.second - lon) + angle;
    // components.second *= f;
    // we the angle and the spped we compute u/v...
    uc = speed * cos(rangle);
    vc = speed * sin(rangle);
}

void GribRotatedInterpretor::raw(GribDecoder& grib, const Transformation& transformation, const string& key,
                                 map<double, map<double, CustomisedPoint*> >& points) const {
    double factor, offset;
    scaling(grib, factor, offset);
    int err;
    southPoleLat_ = grib.getDouble("latitudeOfSouthernPoleInDegrees");
    southPoleLon_ = grib.getDouble("longitudeOfSouthernPoleInDegrees");
    angle_        = grib.getDouble("angleOfRotationInDegrees") * 180.0 / M_PI;

    grib_iterator* iter = grib_iterator_new(grib.handle(), 0, &err);
    double missing      = grib.getDouble("missingValue");

    double lat, lon, value;
    /* Loop on all the lat/lon/values. */
    while (grib_iterator_next(iter, &lat, &lon, &value)) {
        pair<double, double> coords = unrotate(lat, lon);
        lat                         = coords.first;
        lon                         = coords.second;
        if (value != missing) {
            /*
             std::stack<UserPoint>   duplicates;
             UserPoint geo(lon, lat);
             value = (value*factor)+offset;
             transformation.populate(lon, lat, value, points);
             */
        }
    }
    /* At the end the iterator is deleted to free memory. */
    grib_iterator_delete(iter);
}

void GribLambertInterpretor::interpretAsMatrix(GribDecoder& grib) const {
    long im = grib.getLong("numberOfPointsAlongXAxis");
    long jm = grib.getLong("numberOfPointsAlongYAxis");

    Matrix* matrix = grib.u(new Matrix());

    size_t nb;
    grib_get_size(grib.id(), "values", &nb);

    MagLog::dev() << "numberOfFieldValues[" << nb << "]"
                  << "\n";
    double missing = -std::numeric_limits<double>::max();
    missing        = grib.getDouble("missingValue");

    double north = grib.getDouble("latitudeOfFirstGridPointInDegrees");
    double west  = grib.getDouble("longitudeOfFirstGridPointInDegrees");

    MagLog::dev() << "NewAPI---> area[" << west << ", " << north << "]"
                  << "\n";

    try {
        Timer time("Grib", "lambert");

        MagLog::debug() << "Version" << grib_get_api_version() << endl;

        vector<double> latm;
        vector<double> lonm;
        vector<double> data;
        latm.reserve(nb);
        lonm.reserve(nb);
        data.reserve(nb);

        size_t aux = size_t(nb);

        grib_get_double_array(grib.id(), "latitudes", &(latm.front()), &aux);

        double minlat = *min_element(latm.begin(), latm.end());
        double maxlat = *max_element(latm.begin(), latm.end());

        grib_get_double_array(grib.id(), "longitudes", &(lonm.front()), &aux);

        double minlon = *min_element(lonm.begin(), lonm.end());
        double maxlon = *max_element(lonm.begin(), lonm.end());

        // This test needs to be improved ...

        if (minlon > 50.)
            minlon -= 360;
        if (maxlon > 50.)
            maxlon -= 360;

        grib_get_double_array(grib.id(), "values", &(data.front()), &aux);

        for (int i = 0; i < nb; i++) {
            if (lonm[i] > 50.)
                lonm[i] = lonm[i] - 360;

            if (minlat > latm[i])
                minlat = latm[i];
            if (maxlat < latm[i])
                maxlat = latm[i];
            if (minlon > lonm[i])
                minlon = lonm[i];
            if (maxlon < lonm[i])
                maxlon = lonm[i];
        }

        MagLog::debug() << "lat [" << minlat << ", " << maxlat << "]" << std::endl;
        MagLog::debug() << "lon [" << minlon << ", " << maxlon << "]" << std::endl;

        vector<double>& lon = matrix->columnsAxis();
        vector<double>& lat = matrix->rowsAxis();

        // for the lon we take the fisrt line :
        double inci = (maxlon - minlon) / ((im)-1);
        double incj = (maxlat - minlat) / ((jm)-1);
        for (int i = 0; i < im; i++)
            lon.push_back(minlon + (i * inci));
        // for the lon we take the fisrt column :
        for (int i = 0; i < jm; i++)
            lat.push_back(minlat + (i * incj));

        typedef map<double, map<double, pair<int, int> > > Helper;

        // typedef map<double, double> Helper;
        Helper helper;
        int row = 0;
        for (vector<double>::iterator y = lat.begin(); y != lat.end(); ++y) {
            helper.insert(make_pair(*y, map<double, pair<int, int> >()));

            Helper::iterator h = helper.find(*y);

            int column = 0;
            for (vector<double>::iterator x = lon.begin(); x != lon.end(); ++x) {
                h->second.insert(make_pair(*x, std::make_pair(row, column)));

                matrix->push_back(missing);
                column++;
            }
            row++;
        }

        double lat11, lat12, lat21, lat22;
        double lon11, lon12, lon21, lon22;
        double val11, val12, val21, val22;

        for (int r = 0; r < jm - 1; r++) {
            for (int c = 0; c < im - 1; c++) {
                lat11 = latm[c + (im * r)];

                lat12  = latm[(c + 1) + (im * r)];
                minlat = std::min(lat11, lat12);
                maxlat = std::max(lat11, lat12);
                lat21  = latm[c + (im * (r + 1))];
                minlat = std::min(minlat, lat21);
                maxlat = std::max(maxlat, lat21);
                lat22  = latm[(c + 1) + (im * (r + 1))];
                minlat = std::min(minlat, lat22);
                maxlat = std::max(maxlat, lat22);

                lon11 = lonm[c + (im * r)];
                lon12 = lonm[(c + 1) + (im * r)];
                if (lon12 < lon11)
                    lon12 += 360.;
                minlon = std::min(lon11, lon12);
                maxlon = std::max(lon11, lon12);
                lon21  = lonm[c + (im * (r + 1))];
                minlon = std::min(minlon, lon21);
                maxlon = std::max(maxlon, lon21);
                lon22  = lonm[(c + 1) + (im * (r + 1))];
                if (lon22 < lon21)
                    lon22 += 360.;
                minlon = std::min(minlon, lon22);
                maxlon = std::max(maxlon, lon22);

                val11 = data[c + (im * r)];
                val12 = data[(c + 1) + (im * r)];
                val21 = data[c + (im * (r + 1))];
                val22 = data[(c + 1) + (im * (r + 1))];

                // find the points from the helper!
                Helper::iterator low, up;
                low = helper.lower_bound(minlat);
                up  = helper.lower_bound(maxlat);
                if (low == helper.end() || up == helper.end())
                    break;
                for (Helper::iterator it = low; it != up; ++it) {
                    if (it == helper.end())
                        break;
                    map<double, pair<int, int> >& lons          = it->second;
                    map<double, pair<int, int> >::iterator llow = lons.lower_bound(minlon);
                    map<double, pair<int, int> >::iterator lup  = lons.lower_bound(maxlon);
                    if (llow == lons.end() || lup == lons.end())
                        break;
                    ;
                    for (map<double, pair<int, int> >::iterator lit = llow; lit != lup; ++lit) {
                        double lat                = it->first;
                        double lon                = lit->first;
                        std::pair<int, int> index = lit->second;

                        // we interpolate at the point using the 4 points found!
                        double val = missing;
                        if (val11 != missing && val12 != missing && val21 != missing && val22 != missing) {
                            double val1 =
                                ((lon12 - lon) / (lon12 - lon11)) * val11 + ((lon - lon11) / (lon12 - lon11)) * val12;

                            double val2 =
                                ((lon22 - lon) / (lon22 - lon21)) * val21 + ((lon - lon21) / (lon22 - lon21)) * val22;
                            if (std::isnan(val1)) {
                                if (std::isnan(val2)) {
                                    val = missing;
                                }
                                else
                                    val = ((lat - lat11) / (lat22 - lat11)) * val2;
                            }
                            else {
                                if (std::isnan(val2)) {
                                    val = ((lat22 - lat) / (lat22 - lat11)) * val1;
                                }
                                else {
                                    val = ((lat22 - lat) / (lat22 - lat11)) * val1 +
                                          ((lat - lat11) / (lat22 - lat11)) * val2;
                                }
                            }

                            if (std::isnan(val) || std::isinf(val) || std::isinf(-val)) {
                                val = missing;
                            }
                        }
                        if (std::isnan(val))
                            val = missing;
                        if ((*matrix)[index.second + (index.first * im)] == missing) {
                            (*matrix)[index.second + (index.first * im)] = val;
                        }
                    }
                }
            }
            matrix->setMapsAxis();
            matrix->missing(missing);
        }

        MagLog::dev() << *matrix << "\n";
    }

    catch (MagicsException& e) {
        if (MagicsGlobal::strict()) {
            throw;
        }
        MagLog::error() << e << "\n";
    }
}

void GribLambertInterpretor::print(ostream& out) const {
    out << "GribLambertInterpretor[";
    out << "]";
}

PaperPoint GribLambertAzimutalInterpretor::reference(const GribDecoder&, const Transformation&) {
    return PaperPoint();
}

void GribPolarStereoInterpretor::print(ostream& out) const {
    out << "GribLambertInterpretor[";
    out << "]";
}

void GribPolarStereoInterpretor::interpretAsMatrix(GribDecoder& grib) const {
    long im = 3600;
    long jm = 1800;

    Matrix* matrix = grib.u(new Matrix(im, jm));
    double steplon = 0.1;
    double steplat = 0.1;

    double missing = grib.getDouble("missingValue");
    for (int i = 0; i < im; i++) {
        double x = 0 + (i * steplon);
        matrix->columnsAxis().push_back(x);
    }

    for (int i = 0; i < jm; i++) {
        double y = -90 + (i * steplat);
        matrix->rowsAxis().push_back(y);
    }
    vector<double> distance(im * jm, 999999);

    for (int i = 0; i < im; i++) {
        for (int j = 0; j < jm; j++)
            (*matrix)[i + (j * im)] = missing;
    }
    matrix->missing(missing);
    matrix->setMapsAxis();

    interpolate(grib, *matrix);

    MagLog::dev() << *matrix << "\n";
}


void GribInterpretor::interpolate(const GribDecoder& grib, Matrix& matrix) const {
    int err;

    grib_iterator* iter = grib_iterator_new(grib.handle(), 0, &err);
    if (err) {
        MagLog::warning() << "Grib Iterator not available : Chech Grib Api Version " << grib_get_api_version() << endl;
        return;
    }
    double xres = matrix.XResolution();
    double yres = matrix.YResolution();

    double xbase   = matrix.left() - (xres / 2);
    double ybase   = matrix.bottom() - (yres / 2);
    double columns = matrix.columns();
    double rows    = matrix.rows();

    vector<double> distance(columns * rows, 999999);

    double lat, lon, value;
    /* Loop on all the lat/lon/values. */
    while (grib_iterator_next(iter, &lat, &lon, &value)) {
        int ix      = (lon - xbase) / xres;
        double x    = (ix * xres) + xbase;
        int iy      = (lat - ybase) / yres;
        double y    = (iy * yres) + ybase;
        double dist = ((lon - x) * (lon - x)) + ((lat - y) * (lat - y));
        if (dist < distance[ix + (iy * columns)]) {
            matrix[ix + (iy * columns)]   = value;
            distance[ix + (iy * columns)] = dist;
        }
    }
    /* At the end the iterator is deleted to free memory. */
    grib_iterator_delete(iter);
}

void GribProjInterpretor::interpretAsMatrix(GribDecoder& grib) const {
    Timer timer("gribapi", " read grib");
    MagLog::dev() << "GribProjInterpretor::interpretAsMatrix"
                  << "\n";

    string proj = grib.projString();


    // 0 Resolved u- and v- components of vector quantities relative to easterly and northerly directions
    // 1 Resolved u- and v- components of vector quantities relative to the defined grid in the direction of increasing
    // x and y (or i and j) coordinates, respectively


    

    Matrix* u = grib.u(new Proj4Matrix(proj));
    Matrix* v = grib.v(new Proj4Matrix(proj));
    Matrix* c = grib.colour(new Proj4Matrix(proj));



    size_t nb;
    grib_get_size(grib.id(), "values", &nb);

    MagLog::dev() << "numberOfFieldValues[" << nb << "]"
                  << "\n";
    double missing = INT_MAX;
    grib.setDouble("missingValue", missing);
    u->missing(missing);


    long nblon = grib.getLong("numberOfPointsAlongXAxis");
    long nblat = grib.getLong("numberOfPointsAlongYAxis");

    double y = grib.getDouble("latitudeOfFirstGridPointInDegrees");
    double x = grib.getDouble("longitudeOfFirstGridPointInDegrees");


    projHelper_ = new LatLonProjP(proj);

    projHelper_->convert(x, y);


    double rx = x;
    double ry = y;

    projHelper_->revert(rx, ry);


    double dx = grib.getDouble("DxInMetres");
    double dy = grib.getDouble("DyInMetres");


    dx *= grib.getLong("iScansPositively") ? 1 : -1;
    dy *= grib.getLong("jScansPositively") ? 1 : -1;


    u->columnsAxis().reserve(nb);
    u->rowsAxis().reserve(nb);


    double xx = x;
    for (int i = 0; i < nblon; i++) {
        u->columnsAxis().push_back(xx);
        xx = x + (i + 1) * dx;
    }
    double yy = y;
    for (int i = 0; i < nblat; i++) {
        u->rowsAxis().push_back(yy);
        yy = y + (i + 1) * dy;
    }


    if (v != NULL) {
        u->xIndex_.reserve(u->rowsAxis().size());
        int offset = 0;
        int l      = 0;

        for (vector<double>::iterator lat = u->rowsAxis().begin(); lat != u->rowsAxis().end(); ++lat) {
            u->yIndex_.insert(make_pair(*lat, l));
            l++;
            u->xIndex_.push_back(InfoIndex(u->columnsAxis().front(), u->columnsAxis().back(), nblon, offset));
            offset += nblon;
        }
        u->data_.resize(nb);
        v->data_.resize(nb);
    }


    long jPointsAreConsecutive = grib.getLong("jPointsAreConsecutive");
    
    if (jPointsAreConsecutive)
        MagLog::error() << "method not yet implemented" << endl;

    try {
        u->resize(nb);
        size_t aux = size_t(nb);
       
        if (v != NULL) {
            v->resize(nb);
            grib_get_double_array(grib.uHandle(), "values", &u->front(), &aux);     
            grib_get_double_array(grib.uHandle(), "values", &u->data_.front(), &aux);     
            grib_get_double_array(grib.vHandle(), "values", &v->front(), &aux);
            grib_get_double_array(grib.vHandle(), "values", &v->data_.front(), &aux);
            if (c) {
                c->resize(nb);
                c->data_.resize(nb);
                grib_get_double_array(grib.cHandle(), "values", &c->front(), &aux);
                grib_get_double_array(grib.cHandle(), "values", &c->data_.front(), &aux);
            }
        }
        else {
            grib_get_double_array(grib.handle(), "values", &u->front(), &aux);
        }
        

        u->missing(missing);
        u->setMapsAxis();
    }
    catch (...) {
        if (MagicsGlobal::strict()) {
            throw;
        }
        throw MagicsException("GribProjInterpretor - Not enough memory");
    }
}

void GribProjInterpretor::print(ostream& out) const {
    out << "GribRegularInterpretor[";
    out << "]";
}

double GribProjInterpretor::XResolution(const GribDecoder& grib) const {
    // WE have to read DxInMetres and convert back to degrees .. 
    double y1 = grib.getDouble("latitudeOfFirstGridPointInDegrees");
    double y2 = grib.getDouble("latitudeOfFirstGridPointInDegrees");
    double x1 = grib.getDouble("longitudeOfFirstGridPointInDegrees");
    double x2 = x1 + grib.getDouble("DxInMetres");
    
    projHelper_->revert(x1, y1);
    projHelper_->revert(x2, y2);

    return x2-x1;
}
 
