/******************************** 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 GribDecoder.cc
    \brief Implementation of the Template class GribDecoder.
    
    Magics Team - ECMWF 2004
    
    Started: Tue 16-Mar-2004
    
    Changes:
    
*/
 
#include "GribDecoder.h"
#include "Factory.h"
#include <limits>
#include "TitleTemplate.h"
#include "LocalTable.h"
#include "DateTime.h"
#include "GribTables.h"
#include "GribInterpretor.h"
#include "XmlReader.h"
#include "TextVisitor.h"
#include "Timer.h"
#include "VisualAction.h"
#include "AnimationRules.h"
#include "Transformation.h"


using namespace magics;
grib_context* GribDecoder::context_ = 0;
int  GribDecoder::count_ = 0;

GribDecoder::GribDecoder() :  matrix_(0),  xComponent_(0), yComponent_(0),
		colourComponent_(0), handle_(0), interpretor_(0)
{
	count_++;
	title_ = "grib_" + tostring(count_);
	version();
}

void GribDecoder::version()
{
	static bool done = false;
	if ( done ) return;
	done = true;
	MagLog::info() << "GribAPI Version :" << grib_get_api_version() << endl;
}

GribDecoder::~GribDecoder() 
{
	if ( matrix_) delete matrix_;
	if ( xComponent_ ) delete xComponent_;
	if ( yComponent_ ) delete yComponent_;
	if ( handle_ ) grib_handle_delete (handle_);
	for (PointsList::iterator point = points_.begin(); point != points_.end(); ++point) {
		delete *point;
		*point = 0;
	}
	if ( interpretor_ ) delete interpretor_;

	//context is a reference to a global context and should not be deleted
}

void GribDecoder::set(const GribLoop& loop, int index)
{
	// Copy the information about scaling...
	scaling_         = loop.scaling_;
	derived_scaling_ = loop.derived_scaling_;
	scaling_offset_  = loop.scaling_offset_;
	scaling_factor_  = loop.scaling_factor_;
	index_           = loop.uniqueId_;
	wind_mode_       = auto_ptr<WindMode>(loop.wind_mode_->clone());
	internalIndex_ = index;

}

long GribDecoder::getLong(const string& key, bool warnIfKeyAbsent) const
{
	assert (context_);
	long val;
	int err = grib_get_long(handle_, key.c_str(), &val);
	if ( err )
	{
		if (warnIfKeyAbsent)
		{
			MagLog::warning() << "Grib API: can not find key [" << key << "]\n"
			               << grib_get_error_message(err) <<"\n";
		}
		return 0;
	}
	return val;
}

string GribDecoder::getstring(const string& key, bool warnIfKeyAbsent) const
{
	assert (context_); 
	char val[1024];
	size_t length = 1024;
	int err = grib_get_string(handle_, key.c_str(), val, &length);
	if ( err )
	{
		if (warnIfKeyAbsent)
		{
				MagLog::warning() << "Grib API: can not find key [" << key << "]\n"
				               << grib_get_error_message(err) <<"\n";
			}
			return "";
		}
		return string(val);
}

string GribDecoder::getString(const string& key, bool warnIfKeyAbsent) const
{

	if ( Data::dimension_ == 1 )
		return getstring(key, warnIfKeyAbsent);

	string value;
	grib_handle* handle = handle_;
	// otherwise we build a name...

	GribDecoder* grib = const_cast<GribDecoder*>(this);
	grib->openFirstComponent();
	string name1 = getstring(key, warnIfKeyAbsent);
	grib->openSecondComponent();
	string name2 = getstring(key, warnIfKeyAbsent);
	grib->handle_ = handle;
	value = (name1==name2) ? name1 : name1 + "/" + name2;

	if ( Data::dimension_ == 3 ) {
		grib->openThirdComponent();
		string name3 = getstring(key, warnIfKeyAbsent);
		grib->handle_ = handle;
		if ( value != name3 )
			value = value + "/" + name3;
	}
	return value;
}


double GribDecoder::getDouble(const string& key, bool warnIfKeyAbsent) const
{
	assert (context_); 
	double val;
	int err = grib_get_double(handle_, key.c_str(), &val);
	if ( err )
	{
		if (warnIfKeyAbsent)
		{
			MagLog::warning() << "Grib API: can not find key [" << key << "]\n"
			               << grib_get_error_message(err) <<"\n";
		}
		return 0;
	}
	return val;
}


void   GribDecoder::setDouble(const string& key, double val) const
{
	assert (context_);
	int err = grib_set_double(handle_, key.c_str(), val);
	if ( err )
	{
		MagLog::warning() << "Grib API: can not find key [" << key << "]\n"
		               << grib_get_error_message(err) <<"\n";
	}
}


void GribDecoder::read(Matrix **matrix)
{

	if ( handle_ <=0 ) {
		*matrix = 0;
		return;
	}
	long repres;
	grib_get_long(handle_,"dataRepresentationType",&repres);
	const string representation = getString("typeOfGrid");

	try {
		if ( !interpretor_ ) {
			interpretor_ = SimpleObjectMaker<GribInterpretor>::create(representation);
		}
		interpretor_->interpretAsMatrix(*this, matrix);
		interpretor_->scaling(*this, matrix);
	}
	catch (NoFactoryException&)
	{
		MagLog::error() << "Grib Decoder: Representation [" << representation << "] not yet supported.\n"<<endl;;
		throw MagicsException("Grib Decoder: Representation [] not yet supported.");
	}
}


void GribDecoder::read(Matrix **matrix, const Transformation&  transformation)
{
	if ( handle_ <=0 ) {
		*matrix = 0;
		return;
	}
	long repres;    
	grib_get_long(handle_,"dataRepresentationType",&repres);
	const string representation = getString("typeOfGrid");

	try {
		if ( !interpretor_ ) {
					interpretor_ = SimpleObjectMaker<GribInterpretor>::create(representation);
				}
		interpretor_->interpretAsMatrix(*this, matrix, transformation);
		interpretor_->scaling(*this, matrix);
	}
	catch (NoFactoryException&)
	{
		MagLog::error() << "Grib Decoder: Representation [" << representation << "] not yet supported.\n"<<endl;;
		throw MagicsException("Grib Decoder: Representation [] not yet supported.");
	}
}


/*!
 Class information are given to the output-stream.
*/		
void GribDecoder::print(ostream& out)  const
{
	out << "GribDecoder[";
	GribDecoderAttributes::print(out);
	out << "] ";
}

void GribDecoder::release()
{
	if ( matrix_ ) {
		delete matrix_;
		matrix_ = 0;
	}
	if ( xComponent_ ) {
		delete xComponent_;
		xComponent_ = 0;
	}
	if ( yComponent_ ) {
		delete yComponent_;
		yComponent_ = 0;
	}
	if ( colourComponent_ ) {
			delete colourComponent_;
			colourComponent_ = 0;
		}

	for (PointsList::iterator point = points_.begin(); point != points_.end(); ++point) {
				delete *point;
				*point = 0;
		}
	points_.clear();
}

void GribDecoder::decode2D() 
{

	if (xComponent_) return;
	Matrix     *w1 = 0;
	Matrix     *w2 = 0;
	colourComponent_ = 0;
	const string representation = getString("typeOfGrid");
	try {

		if ( !interpretor_ ) {
					interpretor_ = SimpleObjectMaker<GribInterpretor>::create(representation);
		}
		interpretor_->keepOriginal(true);

	}
	catch (NoFactoryException&)
	{
		MagLog::warning() << "Grib Decoder: Representation [" << representation << "] not yet supported.\n"<<endl;;

	}
	readColourComponent();
	openFirstComponent();
	read(&w1);
	openSecondComponent();
	read(&w2);
    Data::dimension_ = ( colourComponent_ ) ? 3 : 2;



	wind_mode_->x(&xComponent_, &yComponent_, w1, w2);
	interpretor_->keepOriginal(false);
}


void GribDecoder::customisedPoints(const AutomaticThinningMethod& thinning, const Transformation& transformation, const std::set<string>& request, CustomisedPointsList& points)
{
	if ( !context_ ) return;
	int factor = thinning.points();
	
	openFirstComponent();
	long repres;    
	grib_get_long(handle_,"dataRepresentationType",&repres);
	const string representation = getString("typeOfGrid");
	try {
		if ( !interpretor_ ) {
					interpretor_ = SimpleObjectMaker<GribInterpretor>::create(representation);
				}
		// Try to find the XResolution ...
		// Compute the thinning factor...
		double yres = interpretor_->XResolution(*this);
		double height = transformation.height();
		factor = maground((height/factor) / yres);
		if (factor < 1) factor = 1;
		
		thinning.units(yres*factor);

		if ( factor == 1)
		{
			CustomisedPointsList xvalues, yvalues, cvalues;
			string xc = "x_component";
			string yc = "y_component";
			string cc = "colour_component";
			interpretor_->raw(*this, transformation, xc, xvalues);
			readColourComponent();
			if ( colourComponent_ )
				interpretor_->raw(*this, transformation, cc, cvalues);

			openSecondComponent();
			interpretor_->raw(*this, transformation, yc, yvalues);

			CustomisedPointsList::iterator x = xvalues.begin();
			CustomisedPointsList::iterator y = yvalues.begin();
			CustomisedPointsList::iterator c = cvalues.begin();
			while ( x != xvalues.end() && y!= yvalues.end() )
			{
				CustomisedPoint* point = new CustomisedPoint((*x)->longitude(), (*x)->latitude(), "");
				pair<double, double> val = (*wind_mode_)( (**x)[xc], (**y)[yc]);
				point->insert(make_pair(xc, val.first));
				point->insert(make_pair(yc, val.second));
				if ( c != cvalues.end() ) {
					point->insert(make_pair(cc, (**c)[cc]));
					c++;
				}
				
				points.push_back(point);
				x++;
				y++;
			}
		}
		else {
			if ( thinning.rawOnly() == false ) {
				BasicThinningMethod basic;
				basic.factor(factor);
				thinning.units(yres*factor);
				customisedPoints(basic, transformation, request, points);
			}
		}
	}
	catch (NoFactoryException&)
	{
		MagLog::error() << "Grib Decoder: Representation [" << representation << "] not yet supported.\n"<<endl;;
		throw MagicsException("Grib Decoder: Representation [] not yet supported.");
	}
}


void newPoint(const Transformation& transformation, double lat, double lon, double uc, double vc, double cc, vector<CustomisedPoint*>& points)
{




	  std::stack<UserPoint>   duplicates;
  	  UserPoint geo(lon, lat);


  	 transformation.wraparound(geo, duplicates);
		 while (duplicates.empty() == false) {
			UserPoint pt = duplicates.top();

			CustomisedPoint *point = new CustomisedPoint(pt.x(), pt.y(), "");

			point->insert(make_pair("x_component", uc));
            point->insert(make_pair("y_component", vc));
		    points.push_back(point);
		    if ( cc != -9999 ) {
				point->insert(make_pair("colour_component", cc));

			}

			duplicates.pop();
		 }
}

void GribDecoder::customisedPoints(const BasicThinningMethod& thinning, const Transformation& transformation, const std::set<string>&, CustomisedPointsList& points)
{
	if ( !context_ ) return;

	decode2D();
	MatrixHandler* inx = new MatrixHandler(*xComponent_);
	MatrixHandler* iny = new MatrixHandler(*yComponent_);
	MatrixHandler* inc = 0;
	if ( colourComponent_ )
		inc = new MatrixHandler(*colourComponent_);
	vector<UserPoint> thinx;
	vector<UserPoint> thiny;
	vector<UserPoint> thinc;
		
	int factor = thinning.factor();
	
	double xres = inx->XResolution();
	

	thinning.units(xres*factor);
	
	transformation.thin(*inx, factor, factor, thinx);
	transformation.thin(*iny, factor, factor, thiny);
	if ( inc )
		transformation.thin(*inc, factor, factor, thinc);

	vector<UserPoint>::const_iterator x = thinx.begin();
	vector<UserPoint>::const_iterator y = thiny.begin();
	vector<UserPoint>::const_iterator c = thinc.begin();
	if (thinx.empty()) return;
	while (x->value() == inx->missing() ||   y->value() == iny->missing()) {
		++x;
		++y;
		if ( c != thinc.end() ) ++c;
		if (x == thinx.end() || y == thiny.end())
			return;
	}
	double lat = x->y();
	double lon = x->x();
	double uc= x->value();
	double vc = y->value();
	double cc =  c != thinc.end() ? c->value() : -9999;
	interpretor_->interpret2D(lat, lon, uc, vc);
	newPoint(transformation, lat, lon, uc, vc, cc, points);

	
	double lastx = lon;

	x++;
	y++;
	while ( x != thinx.end() && y != thiny.end() )
	{
		double lat = x->y();
		double lon = x->x();
		if ( x->value() != inx->missing() &&  y->value() != iny->missing() )
		{

			double uc= x->value();
			double vc = y->value();
			double cc =  c != thinc.end() ? c->value() : -9999;
			interpretor_->interpret2D(lat, lon, uc, vc);

			newPoint(transformation, lat, lon, uc, vc, cc, points);


		}
		lastx = lon;
		x++;
		y++;
		if ( !thinc.empty() && c != thinc.end() ) ++c;
	} 

}

void GribDecoder::decode2D(const Transformation&) 
{	
	Data::dimension_ = 2;
	if (xComponent_) return;
	Matrix     *w1 = 0;
	Matrix     *w2 = 0;



	const string representation = getString("typeOfGrid");

	try {
		if ( !interpretor_ ) {
					interpretor_ = SimpleObjectMaker<GribInterpretor>::create(representation);
				}
		readColourComponent();

			openFirstComponent();
			read(&w1);
			openSecondComponent();
			read(&w2);

	}
	catch (NoFactoryException&)
	{
		MagLog::warning() << "Grib Decoder: Representation [" << representation << "] not yet supported.\n"<<endl;;

	}

	wind_mode_->x(&xComponent_, &yComponent_, w1, w2);
}


void GribDecoder::openFirstComponent() 
{
	grib_field_position_ =  position_1_;
	open();
}


void GribDecoder::openSecondComponent() 
{
	grib_field_position_ =  position_2_;
	open();
}
void GribDecoder::openThirdComponent()
{
	grib_field_position_ =  colour_position_;
	open(false);
}
void GribDecoder::readColourComponent()
{
	grib_field_position_ =  colour_position_;
	try {
		open(false);
		read(&colourComponent_);
	}

	catch (...) {
		colourComponent_ = 0;
	}



}

void  GribDecoder::open(bool sendmsg)
{
	FILE* file = fopen(file_name_.c_str(),"r");
	handle_ = 0;
	if (!file)
	{
		MagLog::error() << "file can not be opened [" << file_name_ << "]" <<endl;
		//throw GribFileMagException(file_name_, grib_field_position_);
		//throw exception();
		return;
	}
	if (!context_) 
		context_ =  grib_context_get_default();

	handle_ = (*address_mode_)(context_, file, grib_field_position_);

	if (handle_<=0 )
	{
		if (sendmsg)
			MagLog::error() << "can not access position [" << grib_field_position_<<" in " << file_name_ << "]" <<endl;
	//	throw GribFileMagException(file_name_, grib_field_position_);
	}
}


bool GribDecoder::id(const string& id) const 
{
	return magCompare(id_, id);
}


void GribDecoder::decodePoints()
{
	if ( !points_.empty() ) return;
	unsigned long flags=0;
	int error;

	if ( Data::dimension_ == 1 ) {
		double scaling;
		double offset;
		open();
		const string representation = getString("typeOfGrid");
		double missing = getDouble("missingValue");
		try {
			if ( !interpretor_ ) {
						interpretor_ = SimpleObjectMaker<GribInterpretor>::create(representation);
					}
			interpretor_->scaling(*this, scaling, offset);
		}
		catch (NoFactoryException&)
		{
			MagLog::warning() << "Grib Decoder: Representation [" << representation << "] not yet supported.\n"<<endl;;
			scaling =1 ; offset =0;
		}

		grib_iterator* iter = grib_iterator_new(handle_, flags, &error);



		if (!iter)
		{
			MagLog::error() << "Grib Iterator not yet supported on this kind of grib\n";
			throw MagicsException("Grib Iterator not yet supported.");
		}

		double lat;
		double lon;
		double val;
		while (grib_iterator_next(iter,&lat,&lon,&val))
		{
			if ( val != missing)
				points_.push_back(new UserPoint(lon, lat, (val*scaling) + offset));
		}
		return;
	}

	openFirstComponent();
	const string representation = getString("typeOfGrid");
	double missing = getDouble("missingValue");

	grib_iterator* iter1 = grib_iterator_new(handle_, flags, &error);
	openSecondComponent();
	grib_iterator* iter2 = grib_iterator_new(handle_, flags, &error);
	if (!iter1 || !iter2)
	{
		MagLog::error() << "Grib Iterator not yet supported on this kind of grib\n";
		throw MagicsException("Grib Iterator not yet supported.");
	}

	double lat1, lat2;
	double lon1, lon2;
	double val1, val2, norm;
	while ( grib_iterator_next(iter1,&lat1,&lon1,&val1) && grib_iterator_next(iter2,&lat2,&lon2,&val2) )
	{
		if ( lat1 == lat2 && lon1 == lon2 )
			if ( val1 != missing && val2 != missing) {
				norm=wind_mode_->norm(val1,val2);
				points_.push_back(new UserPoint(lon1, lat1, norm));
			}
	}	
}


grib_context* GribDecoder::gribContext() 
{
	if (!context_)
		context_ = grib_context_get_default();
	return context_;
}


GribLoop::~GribLoop()
{
    for (vector<GribDecoder*>::const_iterator g = gribs_.begin(); g != gribs_.end(); ++g)
    {
        delete *g;
    }
}


Data* GribLoop::current()
{
	return currentgrib_;
}


map<string, string> GribLoop::ids_;
int GribLoop::index_ = 0;
void GribLoop::next() {}


GribLoop::GribLoop():  currentgrib_(0), file_(0)
{
	currentDim_ = dimension_.begin();
	currentPos_ = dim_.begin();
	gribs_.clear();
	uniqueId_ = Data::getUniqueOwnerId();
	counter_ = 0;
}


void GribLoop::setToFirst() 
{
	currentDim_ = dimension_.begin();
	currentPos_ = dim_.begin();

}


bool  GribLoop::hasMore()
{

	if (file_ == 0 ) {
		file_ = fopen(path_.c_str(),"r");
		if (!file_) {
			MagLog::error() << "file can not be opened [" << path_ <<  "]" <<endl;
			throw GribFileMagException(path_, 0);
		}	
	}
	
    grib_context* context = GribDecoder::gribContext();
    
    // Now we have to find the right Entry!!! 
    

   if ( currentDim_ == dimension_.end() )
	   return false;
    
   
    if ( *currentDim_ == 1 ) {
    	if (  dim_.empty() ) {
    		 // case 1 dimension= 1 and loop on all the fields! 
    		int error;
    		grib_handle* handle = grib_handle_new_from_file(context, file_, &error) ; 
    		if (handle <=0)
    			return false;
    		currentgrib_ = new GribEntryDecoder(handle);

    		currentgrib_->set(*this, counter_++);

            gribs_.push_back(currentgrib_);
    	}
    	else {
    		if ( currentPos_ == dim_.end() )
    			return false;
    		 // Case 3 Dimension = 1 but we only used  a subset of fields

    		grib_handle* handle =  (*address_mode_)(context, file_, *currentPos_);
    		currentPos_++;
    		if (handle <=0)  
    			return false;
    		currentgrib_ = new GribEntryDecoder(handle);
    		currentgrib_->set(*this, counter_++);
            gribs_.push_back(currentgrib_);
    	}
    }
    
   
    if ( *currentDim_  == 2)
    {
    	if ( dim_.empty()  )
    	{
    		// case 2 Dimension = 2 and loop on all the field!
    		int error;
    		grib_handle* handle1 = grib_handle_new_from_file(context, file_, &error) ;
    		if (handle1 <=0)  return false;
    		grib_handle* handle2 = grib_handle_new_from_file(context, file_, &error) ;
    		if (handle2 <=0)  return false;
    		currentgrib_ = new GribEntryDecoder(handle1, handle2);
    		currentgrib_->set(*this, counter_++);
    		gribs_.push_back(currentgrib_);
    	}
    	else {
    		// Case 4 Dimesnion = 2 and we only used a subset of fields!
    		vector<int>::iterator dim1 =  currentPos_;
    		if ( currentPos_ ==  dim_.end() )
    			return false;
    		currentPos_++;
    		vector<int>::iterator dim2 =  currentPos_;
    		if ( currentPos_ ==  dim_.end() )
    			return false;
    		currentPos_++;


    		grib_handle* handle1 =  (*address_mode_)(context, file_, *dim1);
    		grib_handle* handle2 =  (*address_mode_)(context, file_, *dim2);
    		if ( handle1 <=0 )
    			return false;
    		if ( handle2 <=0 )
    			return false;
    		currentgrib_ = new GribEntryDecoder(handle1, handle2);
    		currentgrib_->set(*this, counter_++);

    		gribs_.push_back(currentgrib_);

    	}
    }

	if ( *currentDim_  == 3)
		{
			if ( dim_.empty()  )
			{
				// case 2 Dimension = 2 and loop on all the field!
	    			int error;
	       			grib_handle* handle1 = grib_handle_new_from_file(context, file_, &error) ;
	       			if (handle1 <=0)  return false;
	       			grib_handle* handle2 = grib_handle_new_from_file(context, file_, &error) ;
	        		if (handle2 <=0)  return false;
	        		grib_handle* handle3 = grib_handle_new_from_file(context, file_, &error) ;
	        		if (handle3 <=0)  return false;
	        		currentgrib_ = new GribEntryDecoder(handle1, handle2, handle3);
	        		currentgrib_->set(*this, counter_++);
	        		gribs_.push_back(currentgrib_);
			}
			else {
	        		// Case 4 Dimesnion = 2 and we only used a subset of fields!
					vector<int>::iterator dim1 =  currentPos_;
					if ( currentPos_ ==  dim_.end() )
						return false;
					currentPos_++;
					vector<int>::iterator dim2 =  currentPos_;
					if ( currentPos_ ==  dim_.end() )
						return false;
					currentPos_++;
					vector<int>::iterator dim3 =  currentPos_;
										if ( currentPos_ ==  dim_.end() )
											return false;
										currentPos_++;

	        		grib_handle* handle1 =  (*address_mode_)(context, file_, *dim1);
	        		grib_handle* handle2 =  (*address_mode_)(context, file_, *dim2);
	        		grib_handle* handle3 =  (*address_mode_)(context, file_, *dim3);

	        		if ( handle1 <=0 )
	        			return false;
	        		if ( handle2 <=0 )
	        		       return false;
	        		if ( handle3 <=0 )
	        			        		       return false;
	        		currentgrib_ = new GribEntryDecoder(handle1, handle2, handle3);
	        		currentgrib_->set(*this, counter_++);

	        		gribs_.push_back(currentgrib_);

			}
		}
	currentDim_++;
	currentgrib_->setPath(path_);
	if ( iconName_.empty() )
	{
    		map<string, string>::iterator id = ids_.find(path_);
    		if ( id == ids_.end() )
		{
    		    	iconName_ = "Grib" + tostring(index_);
    		    	index_++;
    		    	ids_.insert(make_pair(path_, iconName_));  		
    		 }
    		 else 
    		    	iconName_ = id->second;
	}
	currentgrib_->icon(*this);
	return true;
}

void GribLoop::print(ostream&) const {}



class GribTag: public XmlNodeVisitor
{
public:
	GribTag(GribDecoder& grib, TagHandler& title) : grib_(grib), title_(title) {
	}
	
	~GribTag() {}
	string baseDate(const XmlNode& node)
	{
		string format= node.getAttribute("format");
		if ( format.empty() )  
			format =  "%A %d %B %Y at %H UTC";
		const long day  = grib_.getLong("date");  
		const long hour = grib_.getLong("hour");  
		const long mn   = grib_.getLong("minute"); 
		MagDate part1 = MagDate(day);
		MagTime part2 = MagTime(hour, mn, 0);
		DateTime full(part1, part2);
	    
		return full.tostring(format);
		
	}

	string startDate(const XmlNode& node)
	{
			string format= node.getAttribute("format");
			if ( format.empty() )  
				format =  "%A %d %B %Y at %H UTC";
			const long day  = grib_.getLong("date");  
			const long hour = grib_.getLong("hour");  
			const long mn   = grib_.getLong("minute");
			const long step = grib_.getLong("startStep");  // default is in hours. Set 'stepUnits' to change.

			MagDate part1 = MagDate(day);
			MagTime part2 = MagTime(hour, mn, 0);
			DateTime full(part1, part2);	
			full = full + (step*3600);
			    
			return full.tostring(format);
	}

	string validDate(const XmlNode& node)
	{
			string format= node.getAttribute("format");
			if ( format.empty() )  
				format =  "%A %d %B %Y at %H UTC";
			const long day =  grib_.getLong("date");  
			const long hour = grib_.getLong("hour");  
			const long mn =  grib_.getLong("minute");
			const long step =  grib_.getLong("stepRange");  // default is in hours. Set 'stepUnits' to change.
			

						
			MagDate part1 = MagDate(day);
			MagTime part2 = MagTime(hour, mn, 0);
			DateTime full(part1, part2);	
			full = full + (step*3600);

			return full.tostring(format);
			
	}

	string endDate(const XmlNode& node)
	{
		string format=  node.getAttribute("format");
		if ( format.empty() )  
			format =  "%A %d %B %Y at %H UTC";
		const long day =  grib_.getLong("date");  
		const long hour = grib_.getLong("hour");  
		const long mn =  grib_.getLong("minute");
		const long step =  grib_.getLong("endStep");  // default is in hours. Set 'stepUnits' to change.

		MagDate part1 = MagDate(day);
		MagTime part2 = MagTime(hour, mn, 0);
		DateTime full(part1, part2);	
		full = full + ( step*3600 );
		    
		return full.tostring(format);
		
	}

	void visit(const XmlNode& node)
	{
		if ( magCompare(node.name(), "grib_info") )
		{
			string grib = node.getAttribute("id");
					if ( !grib_.id(grib)) return;
			string def = node.getAttribute("key");
			if (def.empty()) {
				 def = node.getAttribute("definition");
				 // for backward compatibility with the first version! 
			}
			if ( def== "valid-date") {
							title_.update("grib"+grib, def, validDate(node));
							return;
						}
			
			if ( def == "base-date") {
				title_.update("grib"+grib, def, baseDate(node));
				return;
			}
			if ( def == "MV_Format" ) {
				title_.update("grib"+grib, def, "grib");
				return;
			}
			if ( def == "MV_Index"  || def == "MV_Frame" ||  def == "MV_Value" ) {
				// this is handled by Metview, so we just ignore it here
				return;
			}
			if ( def == "start-date" ) {
				title_.update("grib"+grib, def, startDate(node));
				return;
			}
			if ( def == "end-date" ) {
				title_.update("grib"+grib, def, endDate(node));
				return;
			}	
			string val; 
			string readAsLong = node.getAttribute("readAsLong");
			if(readAsLong != "yes")
			{
				val =  grib_.getString(def);
				string format = node.getAttribute("format"); 
				if ( !format.empty() ) {
					char tmp[256];
					sprintf(tmp, format.c_str(), val.c_str());
					val = tmp;
					
				}
			}
			else
			{
				long longVal = grib_.getLong(def);
				std::stringstream sst ;
				sst << longVal;
				val=sst.str(); 
			}
	
			if ( val.empty() ) 
				val =  node.getAttribute("default");
			title_.update("grib"+grib, def, val);
		}		
		
		if ( magCompare(node.name(), "magics_title") )
		{
			string grib = node.getAttribute("id");
			if ( !grib_.id(grib)) return;

			vector<string> lines;
			TitleTemplate::title(lines, grib_);
			for (unsigned int i = 0; i < lines.size(); i++)
			{
				string id = grib_.title() + "_" + tostring(i);
				title_.update("grib"+grib, id, lines[i]);		
				string entry = "<grib_info definition=\'" + id + "\'/>";
				title_.addToTags("<magics_title/>", entry);
			}
			//title_.addLines(lines.size());
		}
		node.visit(*this);	
	}
	
	void decode(const string& line)
	{
		XmlReader parser;
		XmlTree tree;
	
		ostringstream xml;
		xml << "<?xml version='1.0' ?> \n";		
		xml << "<xml> \n";
		xml << line;
		xml << "\n</xml>";

		try {
			parser.decode(xml.str(), &tree);		
			tree.visit(*this);
		}
		catch (MagicsException& e) {
			MagLog::debug() << e.what() << endl;
		}	
     } 
     string str() const { return out.str(); }
protected :
	GribDecoder& grib_;
	TagHandler& title_;
	ostringstream out;
};

void GribDecoder::visit(AnimationRules& )
{
}

void GribDecoder::visit(ValuesCollector& points)
{
	open();	
	const Transformation& transformation = points.transformation();
		
	points.setCollected(true);

	int nb = points.size();
	double inlats[nb];
	double inlons[nb];
	double outlats[nb];
	double outlons[nb];
	double values[nb];
	double x[nb];
	double y[nb];
	double distances[nb];
	int indexes[nb];
	double scaling, offset;
	string oriUnits, derivedUnits;
	string representation = getString("typeOfGrid");
		
	//Scaling works only for scalar data!!!
	
	try 
	{
		if ( !interpretor_ ) {
					interpretor_ = SimpleObjectMaker<GribInterpretor>::create(representation);
				}
		interpretor_->scaling(*this, scaling, offset,oriUnits,derivedUnits);
	}      
	catch (NoFactoryException&)
	{
		MagLog::warning() << "Grib Decoder: Representation [" << representation << "] not yet supported.\n"<<endl;;
		scaling =1 ; offset =0;
	}
 	
	for (int i =0; i < nb; i++)
	{
		 
	  	 inlats[i] = points[i].y();
		 inlons[i] = std::fmod(points[i].x(),360.);
		 if(inlons[i] < 0.) inlons[i]+=360.;
		 i++;
	}
 
	double missing = getDouble("missingValue");
		
	if ( Data::dimension_ == 1 ) {
	  	bool scaled=(scaling==1 && offset == 0)?false:true;
		points.setScaled(scaled);
		points.setUnits(oriUnits);
		points.setScaledUnits(derivedUnits);
	  
		open();
		grib_nearest_find_multiple(handle_, 0, inlats, inlons, nb, outlats, outlons, values, distances, indexes);
		for (int i =0; i < nb; i++) 
		{
			points[i].push_back(new ValuesCollectorData(outlons[i],outlats[i],values[i],distances[i]));
			if(scaled)
				points[i].back()->setScaledValue(scaling*values[i] + offset);
			if(values[i] == missing)
			  	points[i].back()->setMissing(true);
		}
	}
	else if ( Data::dimension_ == 2  ) {
		bool scaled=(scaling==1 && offset == 0)?false:true;		
		oriUnits=getString("units",false);
		if(oriUnits.find("/") == string::npos)
		{
		  	oriUnits=oriUnits + "/" + oriUnits;
		}	
		points.setUnits(oriUnits);
		points.setScaledUnits("/");
				  			
		
		openFirstComponent();
		grib_nearest_find_multiple(handle_, 0, inlats, inlons, nb, outlats, outlons, x, distances, indexes);
		openSecondComponent();
		grib_nearest_find_multiple(handle_, 0, inlats, inlons, nb, outlats, outlons, y, distances, indexes);
		for (int i =0; i < nb; i++) 
		{
			points[i].push_back(wind_mode_->values(outlons[i],outlats[i],x[i],y[i], distances[i]));
			if(x[i] == missing || y[i] == missing)
				points[i].back()->setMissing(true);	  
		}
	}
		else   {
			bool scaled=(scaling==1 && offset == 0)?false:true;
					oriUnits=getString("units",false);
					if(oriUnits.find("/") == string::npos)
					{
					  	oriUnits=oriUnits + "/" + oriUnits;
					}
					points.setUnits(oriUnits);
					points.setScaledUnits("/");


					openFirstComponent();
					grib_nearest_find_multiple(handle_, 0, inlats, inlons, nb, outlats, outlons, x, distances, indexes);
					openSecondComponent();
					grib_nearest_find_multiple(handle_, 0, inlats, inlons, nb, outlats, outlons, y, distances, indexes);
					for (int i =0; i < nb; i++)
					{
						points[i].push_back(wind_mode_->values(outlons[i],outlats[i],x[i],y[i], distances[i]));
						if(x[i] == missing || y[i] == missing)
							points[i].back()->setMissing(true);
					}
		}

}

void GribDecoder::visit(MagnifierCollector& magnifier)
{
	const Transformation& transformation = magnifier.transformation(); 
	PointsHandler& points = this->points(transformation);
	points.setToFirst();

	while  ( points.more() )
	{
		magnifier.push_back(transformation(points.current()));
		points.advance();
	}
}
const DateDescription& GribDecoder::timeStamp()
{
	vector<string> need;
	need.push_back("<grib_info key='valid-date' format='%Y-%m-%d %H:%M:00'/>");
	need.push_back("<grib_info key='level'/>");
	need.push_back("<grib_info key='typeOfLevel'/>");

	TagHandler helper;
	GribTag tag1(*this, helper);
	for ( vector<string>::const_iterator t = need.begin(); t != need.end(); ++t )
			tag1.decode(*t);


	timeStamp_ = DateDescription(helper.get("grib", "valid-date"), index_, internalIndex_);
	dataLevel_ = LevelDescription::level(helper.get("grib", "typeOfLevel"), tonumber(helper.get("grib", "level")), index_, internalIndex_);


	return timeStamp_;

}

const LevelDescription& GribDecoder::level()
{
	timeStamp();
	return dataLevel_;
}

void GribDecoder::visit(MetaDataCollector& step)
{
	// Here we gather information for the label!
	const Transformation& transformation = step.transformation();
	
	open(); // just to be sure the file is opened!

	initInfo();

	 //Collect infos
	if(step.empty())
	{
		MetviewIcon::visit(step);
	   	return;
	}
	   
	try {	  
		bool members=false;
		vector<string> need;
		if(name_.empty())
		{
			members=true;
		  	need.push_back("<grib_info key='shortName'/>");
			need.push_back("<grib_info key='level'/>");
			need.push_back("<grib_info key='start-date' format='%Y-%m-%d %H:%M:00'/>");
			need.push_back("<grib_info key='end-date' format='%Y-%m-%d %H:%M:00'/>");
		}
		
		for(map<string, string>::iterator key = step.begin(); key != step.end(); ++key )
		{	    
			//If key is not found in information we use gribapi
		  	if(information_.find(key->first) == information_.end())
			{  
				//Compute stats
				if (step.attribute(key->first).group() == MetaDataAttribute::StatsGroup)
				{
					stats_.clear();
					

					PointsHandler& points = this->points(transformation, false);

					points.setToFirst();
	
					while( points.more() )
					{
						stats_["value"].push_back(points.current().value());						
						points.advance();
					}
					
					computeStats();
					

				}
				//We use gribapi
				else if(step.attribute(key->first).source() == MetaDataAttribute::AnySource ||
				   step.attribute(key->first).source() == MetaDataAttribute::GribApiSource)
				{  					  
					if(step.attribute(key->first).type() != MetaDataAttribute::NumberType)   
					{
						need.push_back("<grib_info key='"+key->first+ "'/>");
		   			}
					else
					{
						need.push_back("<grib_info key='"+key->first+ "' readAsLong='yes'/>");
					}

				}
				else if(key->first == "scaling_formula" ||key->first == "scaled_units" )
				{
					double scaling, offset;
					string oriUnits, derivedUnits;
					string representation = getString("typeOfGrid");
					try 
					{
						auto_ptr<GribInterpretor> interpretor_(SimpleObjectMaker<GribInterpretor>::create(representation));
						interpretor_->scaling(*this, scaling, offset,oriUnits,derivedUnits);
						if(scaling==1 && offset == 0)
						{
							information_["scaling_formula"]="";
							information_["scaled_units"]="";
						}
						else
						{  
							string offsetSign=(offset >=0)?"+":"-";
							information_["scaling_formula"]="(value * " + tostring(scaling) + ") " + offsetSign + " " + tostring(fabs(offset));
							information_["scaled_units"]= derivedUnits;
						}
					}	
					catch (NoFactoryException&)
					{
						MagLog::warning() << "Grib Decoder: Representation [" << representation << "] not yet supported.\n"<<endl;;
						information_[key->first]="N/A";
					}
					
				}       	
				
			}	
			//If key is found in information_ we copy it
			else
			{
				  	key->second=information_[key->first];
			}	
		}
		
		
		if(!need.empty())	
		{
			TagHandler helper;
			GribTag tag1(*this, helper);
			for ( vector<string>::const_iterator t = need.begin(); t != need.end(); ++t ) 
			{
	   			tag1.decode(*t);
			}	
				
	   		if(members)
			{  
	   			name_ = helper.get("grib", "shortName") +  " " +  helper.get("grib", "level");

	   			from_ = DateTime(helper.get("grib", "start-date"));
	   			to_ =  DateTime(helper.get("grib", "end-date"));
			}
			
			for(map<string, string>::iterator key = step.begin(); key != step.end(); ++key )
			{	    
				if(information_.find(key->first) == information_.end())
				{  
					if(step.attribute(key->first).source() == MetaDataAttribute::AnySource ||
				   	   step.attribute(key->first).source() == MetaDataAttribute::GribApiSource)
					{  					  
					 	key->second = helper.get("grib", key->first);
						setInfo(key->first,key->second);
					}	
				} 

			}	  
			
	   	}
	}

	catch (...) {}

}

void GribDecoder::decode(const Transformation& transformation) 
{
	if (matrix_) return;
	
	open();
	read(&matrix_, transformation);
	if (!matrix_) return;
	
	// here we build information for the layers!
	TagHandler helper; 
	vector<string> need;
	need.push_back("<grib_info id=\'" + id_ +"\' key='shortName'/>");
	need.push_back("<grib_info id=\'" + id_ +"\' key='level'/>");
	need.push_back("<grib_info id=\'" + id_ +"\'  key='start-date' format='%Y-%m-%d %H:%M:00'/>");
	need.push_back("<grib_info id=\'" + id_ +"\' key='end-date' format='%Y-%m-%d %H:%M:00'/>");
	GribTag tag1(*this, helper);

	for ( vector<string>::const_iterator t = need.begin(); t != need.end(); ++t )
	{
		tag1.decode(*t);
	}

	name_ = helper.get("grib"+id_, "shortName") +  "-" +  helper.get("grib"+id_, "level");
	name_ = iconName_;
	layerId_ = name_ + file_name_;
	from_ = DateTime(helper.get("grib"+id_, "start-date"));
	to_ =  DateTime(helper.get("grib"+id_, "end-date"));	
}

void GribDecoder::decode() 
{
	if (matrix_) return;
	
	open();
	
	read(&matrix_);
	if (!matrix_) return;
	
	// here we build information for the layers!
	TagHandler helper; 
	vector<string> need;
	need.push_back("<grib_info id=\'" + id_ +"\' key='shortName'/>");
	need.push_back("<grib_info id=\'" + id_ +"\' key='level'/>");
	need.push_back("<grib_info id=\'" + id_ +"\'  key='start-date' format='%Y-%m-%d %H:%M:00'/>");
	need.push_back("<grib_info id=\'" + id_ +"\' key='end-date' format='%Y-%m-%d %H:%M:00'/>");
	GribTag tag1(*this, helper);

	for ( vector<string>::const_iterator t = need.begin(); t != need.end(); ++t )
	{
		tag1.decode(*t);
	}

	name_ = helper.get("grib"+id_, "shortName") +  "-" +  helper.get("grib"+id_, "level");
	name_ = iconName_;
	layerId_ = name_ + file_name_;
	from_ = DateTime(helper.get("grib"+id_, "start-date"));
	to_ =  DateTime(helper.get("grib"+id_, "end-date"));	
}



void GribDecoder::visit(TextVisitor& title) 
{
	try {
		open();	
	}
	catch ( ... )
	{
		return;
	}
	
	vector<string> titles;

	title.titles(titles);
	GribTag tag(*this, title);
	
	for ( vector<string>::const_iterator t = titles.begin(); t != titles.end(); ++t ) {
		tag.decode(*t);
	}
}



void GribDecoder::decodeRaster(const Transformation& transformation) 
{
	open();
	
	string representation = getString("typeOfGrid");
	
	try {
		if ( !interpretor_ ) {
					interpretor_ = SimpleObjectMaker<GribInterpretor>::create(representation);
				}
		interpretor_->interpretAsRaster(*this, raster_, transformation);
	}
    
    catch (NoFactoryException&)
    {
    	MagLog::error() << "Grib Decoder: Representation [" << representation << "] not yet supported.\n";
    	throw MagicsException("Grib Decoder: Representation [] not yet supported.");
    }
}


void GribDecoder::initInfo()
{
	if(information_.find("_datatype") == information_.end())
	{
	  	setInfo("_datatype","GRIB");
		setInfo("path", file_name_);
		setInfo("MV_Format","GRIB");
	}
}



namespace magics {

class GribInfo
{
public:
	GribInfo() {}
	virtual ~GribInfo() {}
	virtual void operator()(ostream&, const GribDecoder&) = 0;
};

class GribParameter : public GribInfo
{
public: 
	GribParameter() {}
	~GribParameter() {}
	void operator()(ostream& out, const GribDecoder& grib)
	{
		string val = grib.getString("name");
 		out << val;
	}
};

class GribParamCriter : public MatchCriteria
{
public:
	GribParamCriter() {}
	~GribParamCriter() {}
	bool verify(const GribDecoder& grib, const string&, const string& val)
	{
		long param = grib.getLong("paramId");
		return (tostring(param) == val);
	}
};

class GribLocalCriter : public MatchCriteria
{
public:
	GribLocalCriter() {}
	~GribLocalCriter() {}
	bool verify(const GribDecoder& grib, const string& param, const string& val)
	{
		string key = param;
		string criter = grib.getString(key, false); // 'false' to avoid warnings if key is absent
		MagLog::debug() << "I am verifing " << param << " for a GribDecoder : " << criter << " ==  " << val << "???" << "\n";
		return (criter == val);
	}
};

class GribObsDiagCriter : public MatchCriteria
{
public:
	GribObsDiagCriter() {}
	~GribObsDiagCriter() {}
	bool verify(const GribDecoder& grib, const string&, const string& )
	{
		string param = grib.getString("observationDiagnostic", false); // do not warn if the key is absent
		return (param != "");
	}
};


class GribLocalDefHandler : public TitleFieldHandler
{
public:
	GribLocalDefHandler() {}
	~GribLocalDefHandler() {}

	void operator()(TitleField&, vector<string>& title, const GribDecoder& grib)
	{

		ostringstream out;
		string local = grib.getString("localDefinitionNumber");       
		out << "local definition =" << local << " ";        
		title.back() += out.str();
	}
};

class GribObsDiagHandler : public TitleFieldHandler
{
public:
	GribObsDiagHandler() {}
	~GribObsDiagHandler() {}

	void operator()(TitleField&, vector<string>& title, const GribDecoder& grib)
	{

        ostringstream out;
		string local = grib.getString("observationDiagnostic");       
        out << "diagnostic =" << local << " ";   
        title.back() += out.str();
	}
};

class GribObstatHandler : public TitleFieldHandler
{
public:
	GribObstatHandler() {}
	~GribObstatHandler() {}

	void operator()(TitleField&, vector<string>& /*title*/, const GribDecoder& grib)
	{

		/*string type = grib.getString("codeType"); 
		string platform = grib.getString("platform"); 
      		string instrument = grib.getString("instrument");
		string selection = grib.getString("dataSelection");
		string diag = grib.getString("observationDiagnostic");
		string channel = grib.getString("scaledValueOfFirstFixedSurface");
		string min = grib.getString("min");
		string max = grib.getString("max");
		string mean = grib.getString("average");
		string units= grib.getString("units");
		string exp= grib.getString("experimentVersionNumber");
		string date= grib.getString("mars.date");
		string time= grib.getString("mars.time");
 
       		out << "Statistics for " << type << " from " << platform << "/" << instrument << "<br/>" << 
       			diag << " [" << units << "] (" << selection << ")" <<  "<br/>" << 
       			"Date = " << date << "  Time = " << time << "<br/>" <<
       			"Exp = " << exp << " Channel = " << channel << "<br/>" <<
       			"Min: "  << min << "    Max: " << max << " Mean: " << "mean";         
	
	*/
	}
};

class GribLocalHandler : public TitleFieldHandler
{
public:
	GribLocalHandler(const string& local) : local_(local) {}
	~GribLocalHandler() {}

	void operator()(TitleField&, vector<string>& title, const GribDecoder& grib)
	{

		ostringstream out;
		string code = grib.getString(local_);
		out << local_ << "=" << code  << " ";
		title.back()+= out.str();
	}
protected :
	string local_;
};

class GribStreamHandler : public GribLocalHandler 
{
public:
	GribStreamHandler() : GribLocalHandler("marsStream") {}
	~GribStreamHandler() {}
};

class GribClassHandler : public GribLocalHandler 
{
public:
	GribClassHandler() : GribLocalHandler("marsClass") {}
	~GribClassHandler() {}
};

class GribTypeHandler : public GribLocalHandler 
{
public:
	GribTypeHandler() : GribLocalHandler("marsType") {}
	~GribTypeHandler() {}
};


class GribParamHandler : public TitleFieldHandler
{
public:
	GribParamHandler() {}
	~GribParamHandler() {}
	virtual void operator()(TitleField&, vector<string>& title, const GribDecoder& grib)
	{

		string param = grib.getString("name");
 		title.back()+=param;
	}
};


class GribKeyHandler : public TitleFieldHandler
{
public:
	GribKeyHandler() {}
	~GribKeyHandler() {}
	void operator()(TitleField& field, vector<string>& title, const GribDecoder& grib)
	{
		

		char x[256];
		
		string key = field.attribute("key", ""); 	
		string value  = grib.getString(key);       
		string format = field.attribute("format", "%s");
		sprintf(x, format.c_str(), value.c_str());

	    title.back() += string(x);

	}
};



class GribBaseDateHandler : public TitleFieldHandler
{
public:
	GribBaseDateHandler() {}
	~GribBaseDateHandler() {}
	void operator()(TitleField& field, vector<string>& title, const GribDecoder& grib)
	{

		ostringstream out;

		long date = grib.getLong("date");    
		long hour = grib.getLong("hour");  
		long mn =  grib.getLong("minute"); 
		MagDate part1 = MagDate(date);
		MagTime part2 = MagTime(hour, mn, 0);
		DateTime full(part1, part2);
	
		string format = field.attribute("format", "%A %d %B %Y at %H UTC");
	
		title.back() += full.tostring(format);
	}	
};

class GribValidDateHandler : public TitleFieldHandler
{
public:
    GribValidDateHandler() {}
    ~GribValidDateHandler() {}
    void operator()(TitleField& field, vector<string>& title, const GribDecoder& grib)
    {
	    ostringstream out;
        long date = grib.getLong("date");    
        long hour = grib.getLong("hour");  
        long mn   = grib.getLong("minute"); 
        long step = grib.getLong("step") * 3600; // needs steps in second!   // default is in hours. Set 'stepUnits' to change.
       
        MagDate part1 = MagDate(date);
        MagTime part2 = MagTime(hour, mn, 0);
        DateTime full(part1, part2);
        full = full + step;

        
	    string format = field.attribute("format", "%A %d %B %Y %H UTC");
	        
	    title.back() += full.tostring(format);
       

    }
};

class GribStepHandler : public TitleFieldHandler
{
public:
    GribStepHandler() {}
    ~GribStepHandler() {}
    void operator()(TitleField& field, vector<string>& title, const GribDecoder& grib)
    {

        ostringstream out;
        long istep = grib.getLong("startStep");


        ostringstream step;
        step << istep;
        string format = field.attribute("format", "t+%s");
        out << SimpleStringFormat(step.str(), format);
        title.back() += out.str();
	}
};


class GribLevelHandler : public TitleFieldHandler
{
public:
    GribLevelHandler() { 
    	if (map_.empty()) {
    		map_["Surface"] = &GribLevelHandler::surface;
    		map_["Unknown"] = &GribLevelHandler::surface;
    		map_["isobaricInhPa"] = &GribLevelHandler::isobaricInhPa;
    		map_["hybrid"] = &GribLevelHandler::hybrid;
    	}
    }
    
    ~GribLevelHandler() {}
    typedef string (GribLevelHandler::*Builder)(const string& def, const GribDecoder& grib) const;
    
    void operator()(TitleField&, vector<string>& title, const GribDecoder& grib)
    {
    	ostringstream out;

        string level = grib.getString("typeOfLevel");
        
        map<string,  GribLevelHandler::Builder>::iterator help = map_.find(level);
        if ( help != map_.end() ) out << (this->*help->second)(level, grib) << " ";
        else out << level << " ";    
        
        title.back() +=  out.str();
        
    }
    
protected: 
	static map<string, GribLevelHandler::Builder> map_;
	
	string surface(const string&, const GribDecoder& ) const
	{
		return "";
	}	
	string unknown(const string& level, const GribDecoder& ) const
	{
		ostringstream out;
		out << "Unknown type of level[" << level << "]";
		return out.str();
	}	
	string isobaricInhPa(const string& , const GribDecoder& grib) const
	{
		ostringstream out;
		long level = grib.getLong("level");
		out  << level  <<  " " << "hPa";
		return out.str();
	}	
	string hybrid(const string& , const GribDecoder& grib) const
	{
		ostringstream out;
		long level = grib.getLong("level");
		out  << "Model level " << level;
		return out.str();
	}
};
map<string, GribLevelHandler::Builder> GribLevelHandler::map_;


class GribTimeHandler : public TitleFieldHandler
{
public:
    GribTimeHandler() {}
    ~GribTimeHandler() {}
    void operator()(TitleField&, vector<string>& title, const GribDecoder& grib)
    {
//        if (!grib.getText()) return;
//        ostringstream out;
//        grib_int_t idate;
//        grib_get(grib.id(),(grib_string_t*)"time","I",&idate);      
//     
//        out << "Time:" << idate;
//        title.add(out.str());

          title.back() +=  "Time? ";
    }
};

class GribCentreHandler : public TitleFieldHandler
{
public:
	GribCentreHandler() {}
	~GribCentreHandler() {}
	void operator()(TitleField& field, vector<string>& title, const GribDecoder& grib)
	{
		string format = field.attribute("format", "%s");
		string style = field.attribute("style", "short");
		long centre = grib.getLong("centre");	
		GeneralDef def = CentreTable::definition(centre);
		char x[256];
		string value = (style == "short") ?  def.shortTitle() : def.longTitle();
		sprintf(x, format.c_str(), value.c_str());

		title.back() += string(x);
	}
};

class GribProductHandler : public TitleFieldHandler
{
public:
    GribProductHandler() {}
    ~GribProductHandler() {}
    void operator()(TitleField&, vector<string>& title, const GribDecoder& grib)
    {

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

       GeneralDef def = TypeTable::definition(type);
       title.back() += def.longTitle();
    }
};


class GribPlotTypeHandler : public TitleFieldHandler
{
public:
    GribPlotTypeHandler() {}
    ~GribPlotTypeHandler() {}
    void operator()(TitleField&, vector<string>&,const GribDecoder&)
    {
         //MagLog::warning() << "Plot Type: not implemented--> wait for the specification." << "\n";
    }
};


class NewLineHandler : public TitleFieldHandler
{
public:
    NewLineHandler() {}
    ~NewLineHandler() {}
    void operator()(TitleField&, vector<string>& title,const GribDecoder&)
    {
        title.push_back("");
    }
};


class SatelliteHandler : public TitleFieldHandler
{
public:
    SatelliteHandler()  {}
    ~SatelliteHandler() {}
    void operator()(TitleField&, vector<string>& title,const GribDecoder& grib)
    {
       ostringstream out;
       long ident =  grib.getLong("ident");

       out << "Sat:" << ident << " ";
       title.back() += out.str();
    }
};

class ChannelHandler : public TitleFieldHandler
{
public:
    ChannelHandler()  {}
    ~ChannelHandler() {}
    void operator()(TitleField&, vector<string>& title,const GribDecoder& grib)
    {       
    	ostringstream out;
        long band = grib.getLong("obstype");
        out << "Band:" << band << " ";
        title.back() += out.str();
    }
};


class GribExpverHandler : public TitleFieldHandler
{
public:
    GribExpverHandler() {}
    ~GribExpverHandler() {}
    void operator()(TitleField& field, vector<string>& title, const GribDecoder& grib)
    {

       if ( !grib.getExpver() ) return; 
       ostringstream out;    
       string expver = grib.getString("mars.experimentVersionNumber");
       string format = field.attribute("format", "Expver=%s");          
       out << SimpleStringFormat(expver, format);
       title.back() += out.str();
   }
};


class GribEpsNumberInfoHandler : public TitleFieldHandler
{
public:
    GribEpsNumberInfoHandler() {}
    ~GribEpsNumberInfoHandler() {}
    void operator()(TitleField& field, vector<string>& title, const GribDecoder& grib)
    {
	    
//       if (!grib.getText()) return;
//        ostringstream out;
//       grib_int_t local;
//       grib_get(grib.id(),(grib_string_t*)"localDefinition","I","localDefinitionNumber",&local);
//       if (local != 1) return;
//
//       char number[1024];
//       grib_get(grib.id(),(grib_string_t*)"localDefinition","s","total",number);
//       string format = field.attribute("format", "(%s members)");     
//      
//      out << SimpleStringFormat(number, format);
//        title.add(out.str());

    	 title.back() +=  "epsnumber?";

    }
};


class GribUnitHandler : public TitleFieldHandler
{
public:
    GribUnitHandler() {}
    ~GribUnitHandler() {}
    void operator()(TitleField& field, vector<string>& title, const GribDecoder& grib)
    {
/*
		if (!grib.getText()) return;
        if ( !grib.getUnits() ) return; 
        ostringstream out;      
     
        double id   = grib.getDouble("paramId");
        long centre  = grib.getLong("centre");
        				
        long param = (long) id;
        long table   = (id - param )*100;

          
        const ParamDef& parameter = LocalTable::localInfo(param, table, centre);
           
        string format = field.attribute("format", "Units:%s");           
        string unit = (grib.getScaling()) ? parameter.derivedUnit() :  parameter.originalUnit();
        out << SimpleStringFormat(unit, format);
      
        
        title.back() += out.str();
   */
    }
};

}// end namespace magics



static SimpleObjectMaker<GribParamCriter, MatchCriteria > gribparamcriter("parameter");
static SimpleObjectMaker<GribParamHandler, TitleFieldHandler > gribparamhandler("parameter");
static SimpleObjectMaker<GribBaseDateHandler, TitleFieldHandler > gribbasedatehandler("base_date");
static SimpleObjectMaker<GribValidDateHandler, TitleFieldHandler > gribvaliddatehandler("valid_date");
static SimpleObjectMaker<GribStepHandler, TitleFieldHandler > gribstephandler("step");
static SimpleObjectMaker<GribEpsNumberInfoHandler, TitleFieldHandler > gribepsnumberhandler("eps_number_info");

static SimpleObjectMaker<GribTimeHandler, TitleFieldHandler > gribTimehandler("time");
static SimpleObjectMaker<GribLevelHandler, TitleFieldHandler > gribLevelhandler("level");
static SimpleObjectMaker<NewLineHandler, TitleFieldHandler > newlinehandler("newline");
static SimpleObjectMaker<GribKeyHandler, TitleFieldHandler > gribkeyhandler("gribapi");

static SimpleObjectMaker<GribStreamHandler, TitleFieldHandler > gribstreamhandler("stream");
static SimpleObjectMaker<GribClassHandler, TitleFieldHandler > gribclasshandler("class");
static SimpleObjectMaker<GribTypeHandler, TitleFieldHandler > gribtypehandler("type");
static SimpleObjectMaker<GribLocalDefHandler, TitleFieldHandler > griblocaldefhandler("localdef");
static SimpleObjectMaker<GribCentreHandler, TitleFieldHandler > gribcentrehandler("centre");
static SimpleObjectMaker<GribProductHandler, TitleFieldHandler > gribproducthandler("product");
static SimpleObjectMaker<GribUnitHandler, TitleFieldHandler > gribunithandler("units");
static SimpleObjectMaker<GribExpverHandler, TitleFieldHandler > gribexpverhandler("expver");
static SimpleObjectMaker<GribPlotTypeHandler, TitleFieldHandler > gribplottypehandler("plot_type");
static SimpleObjectMaker<SatelliteHandler, TitleFieldHandler > satellitehandler("satellite");
static SimpleObjectMaker<ChannelHandler, TitleFieldHandler > channelhandler("channel");
static SimpleObjectMaker<GribBaseDateHandler, TitleFieldHandler > datehandler("date");


static SimpleObjectMaker<GribObsDiagCriter, MatchCriteria > gribobsdiagriter("observationDiagnostic");
static SimpleObjectMaker<GribObsDiagHandler, TitleFieldHandler > gribobsdiaghandler("observationDiagnostic");
static SimpleObjectMaker<GribObstatHandler, TitleFieldHandler > gribobstathandler("obstat");


static SimpleObjectMaker<GribLocalCriter, MatchCriteria > gribstreamcriter("stream");
static SimpleObjectMaker<GribLocalCriter, MatchCriteria > gribtypecriter("type");
static SimpleObjectMaker<GribLocalCriter, MatchCriteria > gribclasscriter("class");
static SimpleObjectMaker<GribLocalCriter, MatchCriteria > typeOfgeneratingProcess("typeOfGeneratingProcess");
static SimpleObjectMaker<GribLocalCriter, MatchCriteria > timeRangeIndicator("timeRangeIndicator");

#include "GribRegularInterpretor.h"
static SimpleObjectMaker<GribRegularInterpretor, GribInterpretor> regular_ll("regular_ll");
static SimpleObjectMaker<GribReducedLatLonInterpretor, GribInterpretor> reduced_ll("reduced_ll");
static SimpleObjectMaker<GribRegularGaussianInterpretor, GribInterpretor> regular_gg("regular_gg");
static SimpleObjectMaker<GribReducedGaussianInterpretor, GribInterpretor> reduced_gg("reduced_gg");
static SimpleObjectMaker<GribRotatedInterpretor, GribInterpretor> rotated_ll("rotated_ll");
static SimpleObjectMaker<GribLambertAzimutalInterpretor, GribInterpretor> lambert_azimuthal_equal_area("lambert_azimuthal_equal_area");
static SimpleObjectMaker<GribLambertAzimutalInterpretor, GribInterpretor> lambert("lambert");


#include "GribSatelliteInterpretor.h"
static SimpleObjectMaker<GribSatelliteInterpretor, GribInterpretor> satellite("space_view");

