/******************************** 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.h
    \brief Definition of the Template class GribDecoder.
    
    Magics Team - ECMWF 2004
    
    Started: Tue 16-Mar-2004
    
    Changes:
    
*/

#ifndef GribDecoder_H
#define GribDecoder_H

#include "magics.h"
#include "MagException.h"


#include "Decoder.h"
#include "Data.h"
#include "GribDecoderAttributes.h"
#include "GeoPoint.h"


#include "GribLoopAttributes.h"

#include "grib_api.h"

#include "GribInterpretor.h"


namespace magics {
	

class GribMagException : public MagicsException
{
public:
	 GribMagException( const string& why ):
		MagicsException("Grib API error message: " +  why){}
}; 
class GribFileMagException : public MagicsException
{
public:
	GribFileMagException(const string& file, int index)
	{ 
		ostringstream s;
		s << "Grib decoding failed: field " << index << " in " << file << endl;
		what_ = s.str();
	}
}; 

class GribLoop;

class GribDecoder: 
    public Decoder, 
    public Data<GeoPoint>, 
    public GribDecoderAttributes
{
public:
	GribDecoder();
	virtual ~GribDecoder();    

	// implements BaseSceneObject interface
	virtual void set(const map<string, string>& params) { GribDecoderAttributes::set(params); }
	virtual void set(const XmlNode& node) { GribDecoderAttributes::set(node); }
	void set(const GribLoop&);
	// implements Decoder interface
	void decode();
	void decode2D();
	void decode(const Transformation&);
		void decode2D(const Transformation&);
	void decodeRaster(const Transformation&);
	void decodePoints();
	void release();
	
	// Data Interface : info for the layer managment! 
	 string layerId()  { decode(); return layerId_; }
	 string name() { decode(); return name_; }
	 const DateTime& from() { decode(); return from_; }
	 const DateTime& to()  { decode(); return to_; }
	
	string title() {
		return title_;
	}
	
	void version();
	
	
	// implements Decoder
		void visit(AnimationRules&);
		void visit(MetaDataCollector&);
		void visit(MagnifierCollector&);
		void visit(ValuesCollector&);
		
	// implements Decoder
	void visit(TextVisitor&);
    
	PointsHandler<GeoPoint>& points()
	{
		decodePoints();
		pointsHandlers_.push_back(new PointsHandler<GeoPoint>(points_)); 
		return *(pointsHandlers_.back()); 
	} 
	PointsHandler<GeoPoint>& points(const Transformation& transformation)
		{
			decodePoints();
			pointsHandlers_.push_back(new BoxPointsHandler<GeoPoint>(points_, transformation)); 
			return *(pointsHandlers_.back()); 
		} 
    
	MatrixHandler<GeoPoint>& matrix()
	{
		decode();
		matrixHandlers_.push_back(new MatrixHandler<GeoPoint>(*matrix_));         
		return *(matrixHandlers_.back()); 
	}
	MatrixHandler<GeoPoint>& matrix(const Transformation& transformation)
		{
			decode(transformation);
			matrixHandlers_.push_back(new MatrixHandler<GeoPoint>(*matrix_));         
			return *(matrixHandlers_.back()); 
		}
	
	
	
	RasterData<GeoPoint>&  raster(const Transformation& transformation)
	{
		decodeRaster(transformation);
		return raster_; 
	} 

	void customisedPoints(const AutomaticThinningMethod&, const Transformation&, const std::set<string>&, CustomisedPointsList&);
	void customisedPoints(const BasicThinningMethod&, const Transformation&, const std::set<string>&, CustomisedPointsList& );


	virtual void open();
	virtual void openFirstComponent();
	virtual void openSecondComponent();

	grib_handle*  id() const { if (!handle_) const_cast<GribDecoder*>(this)->decode(); return handle_; }
	long      getLong(const string&,   bool warnIfKeyAbsent = true) const;
	string    getString(const string&, bool warnIfKeyAbsent = true) const;
	double    getDouble(const string&, bool warnIfKeyAbsent = true) const;
    void      setDouble(const string&, double) const;
	void      read(Matrix **matrix);
	void      read(Matrix **matrix, const Transformation&);
	bool      id(const string&) const;

	static grib_context* gribContext();
	static void releaseContext() {  
		if (context_)  
			grib_context_delete(context_); 
		context_ = 0;
	}
	grib_handle*  handle() const { return handle_; }
        void initInfo();

protected:
	//! Method to print string about this class on to a stream of type ostream (virtual).
	 virtual void print(ostream&) const; 
     
	mutable Matrix*     matrix_;
	mutable Matrix*     xComponent_;
	mutable Matrix*     yComponent_;     
	mutable RasterData<GeoPoint> raster_;
	mutable PointsList<GeoPoint> points_;

	
 
	static 	grib_context* context_;
	grib_handle*  handle_;
	
	string title_;
	static int count_;
     
private:
	//! Copy constructor - No copy allowed
	GribDecoder(const GribDecoder&);
	//! Overloaded << operator to copy - No copy allowed
	GribDecoder& operator=(const GribDecoder&);

// -- Friends
	//! Overloaded << operator to call print().
	friend ostream& operator<<(ostream& s,const GribDecoder& p)
		{ p.print(s); return s; }
};


class GribEntryDecoder: public GribDecoder
{
public:
	GribEntryDecoder(grib_handle* handle) { handle_ = handle; }
	GribEntryDecoder(grib_handle* handle1, grib_handle* handle2) { 
		handle_ = handle1;
		handle1_ = handle1; 
		handle2_ = handle2; }
	~GribEntryDecoder() {}

	void open() { assert(handle_); }
	
	void openFirstComponent() { 
		assert(handle1_);
		handle_ = handle1_;
	}
	
	void openSecondComponent() { 
		assert(handle2_);
		handle_ = handle2_;
	}
	
protected:
	grib_handle*  handle1_;
	grib_handle*  handle2_;
};

class GribLoop : public GribLoopAttributes, public DataLoop<GeoPoint>
{
public:
	GribLoop();
	virtual ~GribLoop();
	
	
	
	void set(const map<string, string>& map) { GribLoopAttributes::set(map); }
	void set(const XmlNode& node) { GribLoopAttributes::set(node); }
	
	
	Data<GeoPoint>* current();
	bool         hasMore();
	void         next();
	void setToFirst();
	
	
	

protected:
	virtual void print(ostream&) const; 
	vector<GribDecoder*> gribs_;
    GribDecoder* currentgrib_;
	
	vector<int>::iterator current_;
	vector<int>::iterator current1_;
	vector<int>::iterator current2_;
	
	FILE* file_;
	static map<string, string>  ids_;
	static int  index_;
	
	
private:
	//! Copy constructor - No copy allowed
	GribLoop(const GribLoop&);
	//! Overloaded << operator to copy - No copy allowed
	GribLoop& operator=(const GribLoop&);

// -- Friends
	//! Overloaded << operator to call print().
	friend ostream& operator<<(ostream& s,const GribLoop& p)
		{ p.print(s); return s; }
	
};




} // namespace magics
#endif
