/******************************** LICENSE ********************************

 Copyright 2007 European Centre for Medium-Range Weather Forecasts (ECMWF)

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at 

    http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 ******************************** LICENSE ********************************/

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

    Started: Mon 18-Apr-2005

    Changes:
*/

#include <limits>

#include "CustomisedPoint.h"
#include "TeProjection.h"
#include "GribRegularInterpretor.h"
#include "GribDecoder.h"
#include "Matrix.h"
#include "LocalTable.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(const GribDecoder& grib, double& scaling, double& offset) const
{
	string originalUnits,derivedUnits;
	this->scaling(grib,scaling,offset,originalUnits,derivedUnits);
}	
  
  
void GribInterpretor::scaling(const GribDecoder& grib, double& scaling, double& offset,
			      string& originalUnits, string& derivedUnits) const
{
	scaling = 1;
	offset  = 0;

	// First check that they are not derived fields! 

	if (grib.scaling_ || grib.derived_scaling_)
	{
		long derived = grib.getLong("generatingProcessIdentifier");

		if ( (derived != 254 && grib.scaling_) || (derived == 254 && grib.derived_scaling_) )
		{
			// The key 'paramId' embodies a number of features such as centre, parameter,
			// level, etc. This means that we should not need to worry about table
			// numbers, etc as we did with GRIBEX. However, it's not entirely clear
			// what we can do with non-ECMWF data, as we don't seem to have tables
			// giving scaling factors & offsets for such data. The code, as originally
			// written here takes a table-based approach, but this will probably
			// become redundant. What we really need is something more based on paramIds.
			// In the meantime, we will take the paramIds as if they are from ECMWF data.
			// In practice, this means pretending that all data is from
			// centre=98,table=128.

			long table   = 128; // hard-coded in case of GRIB 2
			long centre  = 98;  // hard-coded in case of GRIB 2

			//long edition = grib.getLong("edition");
			//
			//if (edition == 1)
			//{
			//	table  = grib.getLong("table2Version");
			//	centre = grib.getLong("centre");
			//}
			//else
			//{
			//	// GRIB 2 does not use the table-based approach, so we just hope that this will
			//	// work with most data if we use the standard ECMWF tables...
			//}


			long id = grib.getLong("paramId");

			try {
				const ParamDef& paramdef = LocalTable::localInfo(id, table, centre);
				scaling = paramdef.scaling();
				offset = paramdef.offset();
				originalUnits =paramdef.originalUnit();
				derivedUnits =paramdef.derivedUnit();
			}
			catch (...) {
				MagLog::warning() << " Can not find information for the parameter [" << id << "." << table << "]\n";
			}
            		}
	}
	else {
		scaling = grib.scaling_factor_;
		offset  = grib.scaling_offset_;
	}
	// Add a sanity check : the factor can not be 0..
	if (scaling == 0)
		scaling = 1;
}


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

	scaling(grib, factor, offset);

	(*matrix)->multiply(factor);
	(*matrix)->plus(offset);
}


void GribInterpretor::raw(const GribDecoder& grib, const Transformation& transformation, const string& key, CustomisedPointsList& points) const
{
	double factor, offset;
	scaling(grib, factor, offset);
	int err;

    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 (value != missing ) {
    	  std::stack<UserPoint>   duplicates;
    	  UserPoint geo(lon, lat);
    	  value = (value*factor)+offset;
    	  
    	 transformation.wraparound(geo, duplicates);
		 while (duplicates.empty() == false) {
			UserPoint pt = duplicates.top();
			CustomisedPoint *point = new CustomisedPoint(pt.x(), pt.y(), "");
			point->insert(make_pair(key, value));
			points.push_back(point);
			duplicates.pop();
		 }
    	
      }
       
    }

        /* 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(const GribDecoder& grib, Matrix** matrix, const Transformation& ) const
{
	interpretAsMatrix(grib, matrix);
}

void GribRegularInterpretor::interpretAsMatrix(const GribDecoder& grib, Matrix** matrix) const 
{
  Timer timer("gribapi", " read grib");
  MagLog::dev() << "GribRegularInterpretor::interpretAsMatrix" << "\n";
  long nblon = grib.getLong("numberOfPointsAlongAParallel");
  long nblat = grib.getLong("numberOfPointsAlongAMeridian");
  
  if ( *matrix == 0 ) *matrix = 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);    
  (*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);



  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, (*matrix)->rowsAxis());
 
  double x = west;
  for (int i = 0; i < nblon; i++)
  {
	(*matrix)->columnsAxis().push_back(x);
	x  = west + (i+1)*lon;
  }
	
  (*matrix)->setMapsAxis();


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

  try
  { 
	(*matrix)->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 = &(*matrix)->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
    {
        grib_get_double_array(grib.id(),"values", &(*matrix)->front(),&aux); 
    }

  	(*matrix)->missing(missing);


  } 
  catch (...) 
  {
	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[2 *res];
	grib_get_gaussian_latitudes(res, array);
	 double north = grib.getDouble("latitudeOfFirstGridPointInDegrees");
	 
	  double south = grib.getDouble("latitudeOfLastGridPointInDegrees");; 
	
	for ( int i = 0; i < 2*res; i++ )
	{
		if ( array[i] <= north && array[i] >= south)
			latitudes.push_back(array[i]);
	}
	
}


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




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 << "]";
}

#ifdef later
class GribMatrix : public Matrix
{
public:
	    GribMatrix(const GribDecoder& grib) : grib_(grib) {
	    	
	    }
	    
	    void prepare(double north = 90., double south = -90., double west = -180, double east = 180) 
	    {
	        Timer timer("gribapi", "subarea extraction");
	    	int error;
	    	box_ = grib_box_new(grib_.id(),&error);
	    	// deal with the error! 
	    	grib_points* points = grib_box_get_points(box_,north,west,south,east,&error);

	    	double value[points->n];
	    	grib_points_get_values(grib_.id(),points,value);
	    	
	    	map<double, vector<double> > positions;
	    	map<double, vector<double> > values;
	    	
	    	for (unsigned int i=0;i<points->n;i++) {
	    		map<double, vector<double> >::iterator pos = positions.find(points->latitudes[i]);
	    		map<double, vector<double> >::iterator val = values.find(points->latitudes[i]);
	    		if ( pos == positions.end() ) {
	    			positions.insert(make_pair(points->latitudes[i], vector<double>()) );
	    			values.insert(make_pair(points->latitudes[i], vector<double>()) );
	    			pos = positions.find(points->latitudes[i]);
	    			val = values.find(points->latitudes[i]);
	    		}
	    		pos->second.push_back(points->longitudes[i]);
	    		val->second.push_back(value[i]);	    		   
	    	}
	    	unsigned int max = 0;
	    	double lat = 0;
	    
	    	
	    	for (map<double, vector<double> >::iterator pos = positions.begin(); pos != positions.end(); ++pos) {
	    		if ( pos->second.size() > max ) {
	    			max = pos->second.size();
	    			lat = pos->first;
	    		}
	    		this->rowsAxis().push_back(pos->first);
	    	}
	    		vector<double>& longitudes = positions[lat];
	    		

	    		int nblon = longitudes.size();
	    		double width = longitudes.back() -  longitudes.front();
	    		double step = (width)/(nblon-1); 

	    		for (map<double, vector<double> >::iterator pos = positions.begin(); pos != positions.end(); ++pos) {
	    			
	    			map<double, vector<double> >::iterator val = values.find(pos->first);
	    			vector<double>& p =val->second;
	    			double lon = longitudes.front();
	    			unsigned int p1 = 0;
	    			unsigned int p2 = 1;
	    			double lon1 = lon;
	    			double lon2 = lon1 + (width/(p.size()-1));

	    			for ( unsigned int x = 0; x < longitudes.size(); x++ ) {
	    				if ( lon >= lon2 ) {
	    					p1++;
	    					p2++;
	    					lon1 = lon2;
	    					lon2 += (width)/(p.size()-1);
	    				}
	    				double d1 = (lon2 - lon)/(lon2-lon1);
	    				double d2 = 1-d1;
	    				double val;
	    				
	    				assert ( p1 < p.size() );
	    				if (p2 == p.size()) {
	    					this->push_back(p[p1]);
	    				}
	    				else {
	    					if (p[p1] == missing_ ||  p[p2] == missing_)
	    						val = missing_;
	    					else
	    						val = (p[p1] * d1) + (p[p2] * d2);
	    					this->push_back(val);
	    				}
	    				lon += step;
	    			}
	    		}
	    		
	    		for ( vector<double>::iterator lon = longitudes.begin(); lon != longitudes.end(); ++lon) 
	    				this->columnsAxis().push_back(*lon);
	    		
	    		setMapsAxis();
	    }
		  
protected:
	const GribDecoder& grib_;
	grib_box*                   box_;
};
#endif


void GribReducedGaussianInterpretor::interpretAsMatrix(const GribDecoder& grib, Matrix** matrix, const Transformation& transformation) const
{ 
	interpretAsMatrix(grib, matrix);
	//GribMatrix* helper = new GribMatrix(grib);
	//*matrix = helper;
	//double minlon, minlat, maxlon, maxlat;
    	      
	 //transformation.boundingBox(minlon, minlat, maxlon, maxlat);
	 
	// helper->prepare(maxlat, minlon, minlat, maxlon);
}

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);
}

void GribReducedGaussianInterpretor::interpretAsMatrix(const GribDecoder& grib, Matrix** matrix) const
{
	MagLog::dev() << "GribRegularInterpretor::interpretAsMatrix" << "\n";
	MagLog::dev() << "GribRegularInterpretor::interpretAsMatrix" << "\n";
 
	Timer timer("gribapi", " read grib");
	*matrix = new Matrix();
	size_t nb;
	grib_get_size(grib.id(), "values", &nb);

	MagLog::dev() << "numberOfFieldValues[" << nb << "]" << "\n";
	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");;
	double plp   = grib.getDouble("PLPresent");
	long res     = grib.getLong("numberOfParallelsBetweenAPoleAndTheEquator");
	longitudesSanityCheck(west, east);
	MagLog::dev() << "NewAPI---> area[" << west << ", " << north << ", " << east << ", " << south << "]" << "\n";
	MagLog::dev() << "PLPresent---> " << plp << "\n";
	MagLog::dev() << "Res---> " << res << "\n";

	double pl[2*res];

	size_t aux = 2*res;
	grib_get_double_array(grib.id(),"pl",pl,&aux);
	
    // We have to determine if the field is global! 
	if (north-south > 175.) {
        east = west + 360.;
	}

	// 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
	(*matrix)->reserve(aux * 4 * res);

	double *data = new double[nb];

	size_t aux2 = size_t(nb);
	int nblon = 4*res;
	double width = east-west;
	double step = (width)/(nblon);

	grib_get_double_array(grib.id(),"values",data,&aux2);
	int d = 0;
	for ( size_t i = 0; i < aux; i++)
	{
		vector<double> p;
		for ( int ii = 0; ii < pl[i]; ii++)
		{
			p.push_back(data[d]);
			d++;
		}
		
		double lon = west;
		unsigned int p1 = 0;
		unsigned int p2 = 1;
		double lon1 = west;
		double lon2 = lon1 + (width/(p.size()));

		for ( int x = 0; x < 4*res; x++ )
		{

			if ( lon >= lon2 )
			{
				p1++;
				p2++;
				lon1 = lon2;
				lon2 += (width)/(p.size());
			}
			double d1 = (lon2 - lon)/(lon2-lon1);
			double d2 = 1-d1;
			double val;
			
			assert ( p1 < p.size() );
			if (p2 == p.size()) {
				(*matrix)->push_back(p[p1]);
			}
			else {
				if (p[p1] == missing ||  p[p2] == missing)
					val = missing;
				else
					val = (p[p1] * d1) + (p[p2] * d2);
				(*matrix)->push_back(val);
			}
			lon += step;
		}

	}

	delete [] data;

	for (int x = 0; x < nblon; x++)
	{
		(*matrix)->columnsAxis().push_back(west+(x*step));
	}
		
	double array[2 *res];
	long par = grib.getLong("numberOfParallelsBetweenAPoleAndTheEquator");
	grib_get_gaussian_latitudes(par, array);

	for ( int i = 0; i < 2*res; i++ )
	{
		(*matrix)->rowsAxis().push_back(array[i]);
	}
	(*matrix)->setMapsAxis();
}

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

void GribReducedLatLonInterpretor::interpretAsMatrix(const GribDecoder& grib, Matrix** matrix) const
{
	
 
	Timer timer("gribapi", " read grib");
    *matrix = new Matrix();
	size_t nb;
	grib_get_size(grib.id(), "values", &nb);

	MagLog::dev() << "numberOfFieldValues[" << nb << "]" << "\n";
	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[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];

	size_t aux2 = size_t(nb);
	int nblon = 2*res;
	float width = east-west;
	float step = (width)/(nblon-1); 
	// We have to determine if the field is global! 
        bool global = east-west > 360 - 5*step;
        if (global) {
                MagLog::dev() << "YES THE FIELD IS GLOBAL" << endl;
                east = west + 360;
        }

	
	grib_get_double_array(grib.id(),"values",data,&aux2);
	int d = 0;
	for ( size_t i = 0; i < res; i++)
	{
		float lon = west;
	
		float lon1 = west;
		if ( pl[i] == 0 ) {
			// add missing data 
			for ( int x = 0; x < nblon; x++ ) 
				(*matrix)->push_back(missing);
		}
		else
		{
			unsigned int p1 = 0;
			unsigned int p2 = 1;
			vector<double> p;

			for ( int ii = 0; ii < pl[i]; ii++)
			{
				p.push_back(data[d]);
				d++;
			}
		
			float lon2 = lon1 + (width/(p.size()-1));

			for ( int x = 0; x < nblon; x++ )
			{
				float d1 = (lon2 - lon)/(lon2-lon1);
				float d2 = 1-d1;
				double val; 
				if (p[p1] == missing ||  p[p2] == missing)
					val = missing;
				else
					val = (p[p1] * d1) + (p[p2] * d2);
				(*matrix)->push_back(val);
				lon += step;
				if ( lon >= lon2 )
				{
					p1++;
					if ( p1==p.size() )
					{
						p1 = 0;
					}
					p2++;
					if ( p2 == p.size() )
					{
						p2 = 0;
					}
					lon1 = lon2;
					lon2 += (width)/(p.size()-1);
				}
			}
		}
	}
    
	delete [] data;

	float lon = (width) / (nblon-1);
//	float lat = (north - south) / (2 *res);
	for (int x = 0; x < nblon; x++)
	{
		(*matrix)->columnsAxis().push_back(west+(x*lon));
	}

	double y = north;
	for (long i = 0; i < nblat; i++)
	{
		(*matrix)->rowsAxis().push_back(y);
		y  += lat;
	}

	(*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);
}

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 make_pair( PYREG, PXREG );                                     
}               

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 make_pair( PYROT, PXROT );
}


void GribLambertAzimutalInterpretor::interpretAsMatrix(const GribDecoder& grib, Matrix** matrix) const
{
	  long im = grib.getLong("numberOfPointsAlongXAxis");
	  long jm = grib.getLong("numberOfPointsAlongYAxis");
	  
	  *matrix = new Matrix(im, jm);
	  
	
	  
	  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); 
            double min = *min_element(data.begin(), data.end());
	         double max =*max_element(data.begin(), data.end());;
    
     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 << "]" <<endl;
		MagLog::debug()	<< "lon [" << minlon << ", " << maxlon << "]" <<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, make_pair(row, column)));

							(*matrix)->push_back(missing);
							column++;

						}
						row++;
					}



					int r = 0;
					int c = 0;

					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;
									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 ( isnan(val1) ) {
											if ( isnan(val2) ) {
												val = missing;
											}
											else
												val = ((lat - lat11)/(lat22-lat11))*val2;
										}
										else {
											if ( isnan(val2) ) {
												val =  	((lat22 - lat )/(lat22-lat11))*val1;
											}
											else {
												val =  	((lat22 - lat )/(lat22-lat11))*val1 + ((lat - lat11)/(lat22-lat11))*val2;
											}
										}

										if (isnan(val) || isinf(val) || isinf(-val) ) {
											val = missing;
										}
									}
									if (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)
		{
			MagLog::error() << e << "\n";
		}
        
}



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

void GribRotatedInterpretor::interpretAsMatrix(const GribDecoder& grib, Matrix** matrix) const
{

	southPoleLat_  = grib.getDouble("latitudeOfSouthernPoleInDegrees");
	southPoleLon_  = grib.getDouble("longitudeOfSouthernPoleInDegrees");
	angle_  = grib.getDouble("angleOfRotationInDegrees") * 180.0/M_PI;
	uvRelativeToGrid_  = grib.getLong("uvRelativeToGrid");
	if ( original_ ) {
		long nblon = grib.getLong("numberOfPointsAlongAParallel");
		long nblat = grib.getLong("numberOfPointsAlongAMeridian");

		*matrix = new RotatedMatrix(nblat, nblon);
		size_t nb;
		grib_get_size(grib.id(), "values", &nb);

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

		(*matrix)->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 lon = (east-west)/(nblon-1);
		double lat =   (south-north)/(nblat-1);

		vector<double>& rows = static_cast<RotatedMatrix*>(*matrix)->rowsArray();
		vector<double>& columns = static_cast<RotatedMatrix*>(*matrix)->columnsArray();
		vector<double>& values = static_cast<RotatedMatrix*>(*matrix)->values();
		MagLog::dev() << "calcul -->" << lon << " (from->" << west << " to-->" << west + (nblon-1) *lon << ")" <<  endl;

		double y = north;
		for (int i = 0; i < nblat; i++)
		{
			double x = west;
			for (int  j = 0; j < nblon; j++)
			{
				if ( i == 0 )
					(*matrix)->columnsAxis().push_back(x);
				pair<double, double> point = unrotate(y, x);
				columns.push_back(point.second);
				rows.push_back(point.first);
				x  = west + (j+1)*lon;

			}

			y  = north + (i+1)*lat;

		}






		try {
			(*matrix)->resize(nb);
			size_t aux = size_t(nb);
			grib_get_double_array(grib.id(),"values", &values.front(),&aux);
		}
		catch (...)
		{
			throw MagicsException("Not enough memory");
		}


		return;
	}


	long nblon = grib.getLong("numberOfPointsAlongAParallel");
	long nblat = grib.getLong("numberOfPointsAlongAMeridian");
	*matrix = new Matrix(nblat, nblon);

	Matrix* helper = new Matrix(nblat, nblon); // settup as the equivalent regular matrix! 

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

	MagLog::dev() << "numberOfFieldValues[" << nb << "]" << "\n";
	double missing = -std::numeric_limits<double>::max();
	grib.setDouble("missingValue", missing);    
	helper->missing(missing);
	(*matrix)->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);

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

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

	MagLog::dev() << "calcul -->" << lon << " (from->" << west << " to-->" << west + (nblon-1) *lon << ")" <<  endl;
    
	double y = north;
    	for (int i = 0; i < nblat; i++)
    	{
    	    
    		helper->rowsAxis().push_back(y);
    		y  = north + (i+1)*lat;
    		
    	}
    	
   
   

   
	double x = west;
  	for (int i = 0; i < nblon; i++)
  	{
  	    
  		helper->columnsAxis().push_back(x);
  		x  = west + (i+1)*lon;
  		
  	}
  
	helper->setMapsAxis();


	try { 
		helper->resize(nb);
		size_t aux = size_t(nb);
		grib_get_double_array(grib.id(),"values", &helper->front(),&aux); 
	} 
	catch (...) 
	{
		throw MagicsException("Not enough memory");
	}
   
  
	


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

	// Fisrt try to find the bounding box 
	vector<double>  rows;
	rows.reserve(nb);
	vector<double>  columns;
	columns.reserve(nb);
	vector<double>  values;
	values.reserve(nb);

	for ( int j = 0; j < nblat; j++ )
	{
		lon = west;
		
		for ( int i = 0; i < nblon; i++ )
		{
			pair<double, double> point = unrotate(lat,lon);
			
			
			

			rows.push_back(point.first);   
			columns.push_back(point.second);
			
			lon += steplon;
		}
		lat += steplat;
	}

	double minx = *std::min_element(columns.begin(), columns.end());
	double maxx = *std::max_element(columns.begin(), columns.end());
	double miny = *std::min_element(rows.begin(), rows.end());
	double maxy = *std::max_element(rows.begin(), rows.end());
	
	
	

	double stepx = (maxx - minx)/(nblon-1);
	double stepy = (maxy - miny)/(nblat-1);
	x = minx;
	y = miny;
	// Create the Axis for Regular Matrix..
	for ( int i = 0; i < nblon; i++)
	{
		(*matrix)->columnsAxis().push_back(x); 
		x += stepx;
	}
	for ( int j = 0; j < nblat; j++)
	{
		(*matrix)->rowsAxis().push_back(y); 
		y +=  stepy;
	}

	miny = std::min(north, south);
	maxy = std::max(north, south);
	minx = std::min(west, east);
	maxx = std::max(west, east);
	
	(*matrix)->setMapsAxis();
	for ( int j = 0; j < nblat; j++)
	{
		for ( int i = 0; i < nblon; i++)
		{
			
		    pair<double, double> point = rotate( (*matrix)->row(j, i), (*matrix)->column(j, i) );
		    if ( point.first >  miny && point.first < maxy && point.second > minx && point.second < maxx )		    {	
		    	(*matrix)->push_back(helper->interpolate(point.first, point.second));
		    	
		    }
		    else {
		    	(*matrix)->push_back(missing);
		    	
		    }
		}
	}
}


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);

	pair<double, double> pt = rotate(lat, lon);
	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(const GribDecoder& grib, const Transformation& transformation, const string& key, CustomisedPointsList& 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.wraparound(geo, duplicates);
		 while (duplicates.empty() == false) {
			UserPoint pt = duplicates.top();
			CustomisedPoint *point = new CustomisedPoint(pt.x(), pt.y(), "");
			point->insert(make_pair(key, value));
			points.push_back(point);
			duplicates.pop();
		 }

      }

    }

        /* At the end the iterator is deleted to free memory. */
    grib_iterator_delete(iter);


}
