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

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

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

#include <iostream>
#include <sstream>
#include <fstream>
#include <cstdio>
#include <stdio.h>
#include <algorithm>

#include "GribMetaData.h"
#include "LogHandler.h"
#include "MvKeyProfile.h"
#include "MvMessageMetaData.h"
#include "MvMiscelaneous.h"
#include "MvTmpFile.h"

#include "eccodes.h"

static std::string GRIB_DEFINITION_PATH_ENV;
static std::string GRIB_DUMP_EXE;

std::map<int,std::string> GribMvDump::keyMap_;

#ifdef ECCODES_UI
#ifndef ABS
#define ABS(x)    ( ((x) >= 0) ? (x) : -(x) )
#endif
#endif

static bool shellCommand(const std::string &command, std::stringstream& out,std::stringstream& err,
                         std::stringstream& otherErr, int& exitCode)
{
    FILE *in;
    char cbuf[1024];
    MvTmpFile tmpFile;

    exitCode=-1;

    std::string cmd;
    if(!GRIB_DEFINITION_PATH_ENV.empty())
    {
        cmd+="export GRIB_DEFINITION_PATH=" + GRIB_DEFINITION_PATH_ENV + ";";
    }
    cmd+=command + " 2>" + tmpFile.path();

    if (!(in = popen(cmd.c_str() ,"r")) )
    {
        otherErr << "Failed to execute command: " << command;
        return false;
    }

    while(fgets(cbuf, sizeof(cbuf), in) != NULL)
    {
        out << cbuf;
    }

    int ret=pclose(in);
    exitCode=WEXITSTATUS(ret);

    if (!(in = fopen(tmpFile.path().c_str() ,"r")) )
    {
        otherErr << "Failed to read file " << tmpFile.path() << " containing STDERR of command";
        return false;
    }

    while(fgets(cbuf, sizeof(cbuf), in) != NULL)
    {
        err << cbuf;
    }

    fclose(in);

    return true;
}

#if 0
static void shellCommand(string &command, stringstream& out,stringstream& err, string &ftmp)
{
	FILE *in;
	char cbuf[512];

	string cmd;
	if(!GRIB_DEFINITION_PATH_ENV.empty())
	{
		cmd+="export GRIB_DEFINITION_PATH=" + GRIB_DEFINITION_PATH_ENV + ";";
	}
	
	cmd+=command + " 2>" + ftmp;
		
	if (!(in = popen(cmd.c_str() ,"r")) )
	{
		return;
	}

	while(fgets(cbuf, sizeof(cbuf), in) != NULL)
	{
		out << cbuf;		
	}	
	
	pclose(in);


	if (!(in = fopen(ftmp.c_str() ,"r")) )
	{
		return;
	}

	while(fgets(cbuf, sizeof(cbuf), in) != NULL)
	{
		err << cbuf;		
	}	
	
	fclose(in);

}
#endif



GribSection::~GribSection()
{
    for(std::vector<GribItem*>::iterator it=item_.begin(); it != item_.end(); it++)
	{
		delete *it;
	}
}


//===================================================
//
//  GribWmoDump
//
//===================================================

GribWmoDump::~GribWmoDump()
{
	clear();
}

void GribWmoDump::clear()
{
    for(std::vector<GribSection*>::iterator it=section_.begin(); it != section_.end(); it++)
	{
		delete *it;
	}
	section_.clear();
	text_.clear();
}

//msgCnt starts from 1!!!
bool GribWmoDump::read(const std::string& fgrib, int msgCnt)
{
 	//Get grib dump for the given field
	//int count=1;
    std::string errOut;

    std::string cmd=GRIB_DUMP_EXE + " -O -w count=" + metview::toString(msgCnt) + " " + "\"" +fgrib +"\"";

    GuiLog().task() << "Generating WMO-style dump for message: " << msgCnt  <<
                       GuiLog::commandKey() << cmd;
	
    std::stringstream in, err;
    bool hasError=false;
    int exitCode;
    std::stringstream shellErr;
    bool ret=shellCommand(cmd,in,err,shellErr,exitCode);

    if(exitCode > 0)
    {
        hasError=true;
        GuiLog().error() << "Command exited with code: "  << exitCode;
        if(err.str().empty())
            errOut+="<b>Command</b>" + cmd + " exited with <b>code:</b> " + metview::toString<int>(exitCode) + " ";
    }

    if(!ret)
    {
        hasError=true;
        GuiLog().error() << shellErr.str();
        errOut+=shellErr.str();
    }

    //Get stderr of command
    if(!err.str().empty())
    {
        hasError=true;
        GuiLog().error() << err.str();
        errOut="<b>Command </b>" + cmd + " <b>failed.</b> <br>" + err.str();
    }

    if(hasError)
        return false;

    //Run dump command
    //shellCommand(cmd,in,err,tmpFile);
	
	//Get stdout of command
	text_=in.str();

	//Get stderr of command
    //if(!err.str().empty())
    //{
    //    GuiLog().error() << err.str();
     //   return false;
    //}

    GuiLog().task() << "Parsing default dump for message: " << msgCnt;
    parse(in);

    return (section_.size() > 0)?true:false;
}

void GribWmoDump::parse(std::stringstream& in)
{
    std::string stmp;
    char c[1024];
    GribSection *asec=0;

    while(in.getline(c,1024))
    {
        std::string::size_type pos, pos1;

        std::string buf; // Have a buffer string
        stmp=c;
        std::stringstream ss(stmp); // Insert the string into a stream

        if( stmp.find("MESSAGE") != std::string::npos)
        {

        }
        else if((pos=stmp.find("SECTION_")) != std::string::npos ||
                (pos=stmp.find("SECTION")) != std::string::npos)
        {
            GribSection *sec = new GribSection;
            std::string::size_type posNumEnd=stmp.find(" ",pos+8);
            if(posNumEnd != std::string::npos)
            {
                sec->name("Section " + stmp.substr(pos+8,posNumEnd-pos-8));
            }
            else
            {
                sec->name("Section ??");
            }

            asec=sec;
            section_.push_back(sec);
        }

        else if(asec != 0)
        {
            int i=0;
            GribItem *item = new GribItem;
            while (ss >> buf)
            {
                if(i==0)
                {
                    item->pos(buf);
                }
                else if(i==1)
                {
                    item->name(buf);
                }
                else if(i== 2 && buf == "=")
                {
                    pos=stmp.find("=");
                    pos1=stmp.rfind("{");
                    if(pos1 != std::string::npos)
                    {
                        item->value(stmp.substr(pos,pos1-pos+1));

                        in.getline(c,256);
                        stmp=c;
                        while(stmp.find("}") == std::string::npos)
                        {
                            if(stmp.find("}") == std::string::npos)
                            {
                                if(stmp.find("...") == std::string::npos)
                                {
                                    std::stringstream ssdata(stmp);
                                    while(ssdata >> buf)
                                    {
                                        item->addArrayData(buf);
                                    }
                                }
                                else
                                {
                                    item->addArrayData(stmp);
                                }
                            }

                            if(! in.getline(c,256))
                            {
                                GuiLog().error() << "GribWmoDump::read() ---> Fatal formatting error in parsing WMO style dump!\n";
                                return;
                            }
                            stmp=c;
                        }
                        if(stmp.find("}") != std::string::npos)
                        {
                            item->addArrayData(stmp);
                        }
                        //stmp now contains the "}" character
                    }

                    else //if(buf.find("[") == string::npos || buf.find("]") == string::npos )
                    {
                        item->value(stmp.substr(pos+1));
                        //item->value(buf);
                    }


                    break;
                }
                i++;
            }
            asec->addItem(item);
        }
    //GribFieldMetaData* field = new GribFieldMetaData;
    }
}

//===================================================
//
//  GribStdDump
//
//===================================================

GribStdDump::~GribStdDump()
{
	clear();
}

void GribStdDump::clear()
{
    for(std::vector<GribItem*>::iterator it=item_.begin(); it != item_.end(); it++)
	{
		delete *it;
	}
	item_.clear();
	text_.clear();
}

//msgCnt starts from 1!!!
bool GribStdDump::read(const std::string& fname, int  msgCnt)
{
    std::string errOut;
    std::string cmd=GRIB_DUMP_EXE + " -w count=" + metview::toString(msgCnt) + " " + "\"" + fname + "\"" ; //+ "  > " + tmpFile_;

    GuiLog().task() << "Generating default dump for message: " << msgCnt <<
                       GuiLog::commandKey() << cmd;

    //Run the command
    std::stringstream in, err;
    bool hasError=false;
    int exitCode;
    std::stringstream shellErr;
    bool ret=shellCommand(cmd,in,err,shellErr,exitCode);

    if(exitCode > 0)
    {
        hasError=true;
        GuiLog().error() << "Command exited with code: "  << exitCode;
        if(err.str().empty())
            errOut+="<b>Command</b>" + cmd + " exited with <b>code:</b> " + metview::toString<int>(exitCode) + " ";
    }

    if(!ret)
    {
        hasError=true;
        GuiLog().error() << shellErr.str();
        errOut+=shellErr.str();
    }

    //Get stderr of command
    if(!err.str().empty())
    {
        hasError=true;
        GuiLog().error() << err.str();
        errOut="<b>Command </b>" + cmd + " <b>failed.</b> <br>" + err.str();
    }

    if(hasError)
        return false;

	//Get stdout of command
    text_=in.str();

    GuiLog().task() << "Parsing default dump for message: " << msgCnt;
    parse(in);

    return (item_.size() > 0)?true:false;
}

void GribStdDump::parse(std::stringstream& in)
{
    std::string stmp, descText;
    char c[512];
    while(in.getline(c,512))
	{
		stmp=c;
        if(stmp.find("GRIB {") != std::string::npos)
		{
			break;
		}
	}

    while(in.getline(c,512))
	{
		//cout << c << endl ;

        std::string buf; // Have a buffer string
    		stmp=c;
        std::stringstream ss(stmp); // Insert the string into a stream

        if(stmp.find("#") != std::string::npos && stmp.find("#-READ ONLY-") == std::string::npos)
		{
			descText= stmp;	
		}	
		else
		{		
			//vector<string> sv;
    			int i=0;
			
			GribItem *item = new GribItem;
			item_.push_back(item);

			while (ss >> buf)
			{
                if(buf.find("#-READ") != std::string::npos ||
                   buf.find("ONLY-") != std::string::npos)
				{
					continue;
				}

                if(stmp.find("=") == std::string::npos)
				{
					return;
				}

				if(i==0)
				{
					item->name(buf);
				}
				else if(i==2 && buf != "{")
				{
					item->value(buf.substr(0,buf.size()-1));

					if(descText.size() > 0)
					{
						item->description(descText);
						descText.clear();
					}

				}

				else if(i == 2)
				{			                   
					in.getline(c,512);
					stmp=c;	
                    while(stmp.find("}") == std::string::npos)
					{
                        if(stmp.find("}") == std::string::npos)
						{
                            if(stmp.find("...") == std::string::npos)
							{
								stringstream ssdata(stmp);
								while(ssdata >> buf)
								{
									item->addArrayData(buf);
								}
							}
							else
							{	
								item->addArrayData(stmp);
							}
						}

                        if(!in.getline(c,512))
						{
                            GuiLog().error() << "Fatal formatting error in parsing default style dump!";
                            return;
						}
						stmp=c;
					}
                    if(stmp.find("}") != std::string::npos)
					{
						item->addArrayData(stmp);
					}
                    //stmp now contains the "}" character
				}	

			i++;	
			}
		}		
	}
}

//===================================================
//
//  GribMvDump
//
//===================================================

GribMvDump::GribMvDump()
{
    //tmpfilename_ = tmpfilename;

	if(keyMap_.empty())
	{
	  	keyMap_[GRIB_TYPE_STRING]="string";
		keyMap_[GRIB_TYPE_LONG]="long";
		keyMap_[GRIB_TYPE_DOUBLE]="double";
	}
}	
	
GribMvDump::~GribMvDump()
{
	clear();
}

void GribMvDump::clear()
{
    for(std::vector<GribItem*>::iterator it=item_.begin(); it != item_.end(); it++)
	{
		delete *it;
	}
	item_.clear();
}

//msgCnt starts from 1!!!
bool GribMvDump::read(const std::string& fname, int count)
{
    std::vector<std::string> name_space;
	name_space.push_back("Default");
	name_space.push_back("geography");
	name_space.push_back("ls");
	name_space.push_back("mars");
	name_space.push_back("parameter");
	name_space.push_back("statistics");
	name_space.push_back("time");
	name_space.push_back("vertical");

	// list of blacklisted keys - these can be very expensive to compute; we can
	// always put them back in if someone really really needs them...
	// to be considered: minimum, maximum
    std::vector<std::string> key_blacklist;
	key_blacklist.push_back("distinctLatitudes");
	key_blacklist.push_back("distinctLongitudes");
	key_blacklist.push_back("distinctLatitudes");

	// keys which are redundant or not properly defined
	key_blacklist.push_back("GRIBEditionNumber");

	//Other keys which define constant values and are not really useful for users
	key_blacklist.push_back("7777");
	key_blacklist.push_back("x");

  	FILE* fp;
  	grib_handle* gh=NULL;
  	grib_keys_iterator* kiter=NULL;
 	//const int MAX_KEY_LEN=255;
	const int MAX_VAL_LEN=1024;
	unsigned long key_iterator_filter_flags=GRIB_KEYS_ITERATOR_ALL_KEYS;

 	int err=0;
 	int grib_count=0;	

 	int keyType;
	//long longValue;
	string svalue;
 	char value[MAX_VAL_LEN];
  	size_t vlen=MAX_VAL_LEN;

    GuiLog().task() << "Generating namespace dump for message: " << count <<
                       GuiLog::methodKey() << "ecCodes C interface";

	//open grib file for reading
    fp = fopen(fname.c_str(),"r");
  	if(!fp) 
	{
        GuiLog().error() << "Cannot open grib file: \n        " << fname << "\n";
        return false;
  	}

	//Get messages form the file
  	while((gh = grib_handle_new_from_file(0,fp,&err)) != NULL || err != GRIB_SUCCESS)
	 {
		grib_count++;

		//if(!gh) 
		//{
      		//	log->error("GribMvDump::read() ---> Unable to create grib handle\n");
      		//	return;
    		//}

		if(grib_count == count)
		{
   			//cout << "-- GRIB N. " << grib_count << "---/n";
    			
			if(!gh) 
			{      			
                GuiLog().error() << "Unable to create grib handle for message no: " <<
                        grib_count << "\n";
                return false;
			}
				
			for(unsigned int ns = 0 ; ns  < name_space.size(); ns++)
			{
   				//Get key iterator
				if (name_space[ns]!="Default")
					kiter=grib_keys_iterator_new(gh,key_iterator_filter_flags,const_cast<char *>(name_space[ns].c_str()));
				else
					kiter=grib_keys_iterator_new(gh,key_iterator_filter_flags,NULL);

				if (!kiter)
				{					
                    GuiLog().error() << "Unable to create keys iterator for message no: " <<
                                                 grib_count << "\n";										
                    return false;
				}

				//Loop for the keys in the current message until all the keys are found
   				while(grib_keys_iterator_next(kiter))
   				{
					const char* name = grib_keys_iterator_get_name(kiter);

					if(!name)
					{
						continue;
					}	
					  
					string cbuff=name; 

					// if this key is in our blacklist, then do not use it
                    std::vector<string>::const_iterator i = find(key_blacklist.begin(), key_blacklist.end(), name);

					if (i != key_blacklist.end())
						continue;


					//Add namespace prefix to the key name (e.g. mars.param)
					if(name_space[ns] != "Default")
					{
						cbuff = name_space[ns] + "." + cbuff;				
					}
					
					//Get string value
					vlen=MAX_VAL_LEN;
					size_t  len;
					
					if(grib_get_native_type(gh,name,&keyType) == GRIB_SUCCESS &&				
					   grib_get_size(gh,name,&len) == GRIB_SUCCESS && 
					   (keyType == GRIB_TYPE_STRING || keyType == GRIB_TYPE_LONG ||
					    keyType == GRIB_TYPE_DOUBLE ))
					{					  
						GribItem *item = new GribItem;
						item_.push_back(item);
						item->name(cbuff);
					
						if(keyType == GRIB_TYPE_STRING || len == 1)
						{												  
							if(grib_get_string(gh,name,value,&vlen) == GRIB_SUCCESS)
      							{
								//cout << "   " << cbuff << " =  " << value << endl;
								//Create the grib item

								item->value(value);
							}
						}
						else if(strcmp(name,"paramId") == 0 && keyType == GRIB_TYPE_LONG)
						{
							if(grib_get_string(gh,name,value,&vlen) == GRIB_SUCCESS)
							{
								item->value(value);
							}						
						}  			
						else
						{
							stringstream s;
							s << len;
							svalue="Array (" + s.str() + ")";
							item->value(svalue);	
						}
						
						map<int,string>::const_iterator  it=keyMap_.find(keyType);
						if(it  != keyMap_.end())
						{
							item->type(it->second);
						}	
					}
					
				}	

				grib_keys_iterator_delete(kiter);
			}    

			fclose(fp);
			break;			
		}

		if(gh)
			grib_handle_delete(gh);
  	}

    return true;
}

//===================================================
//
//  GribValueDump
//
//===================================================

GribValueDump::GribValueDump()
{
	latitude_=0;
	longitude_=0;
	value_=0;
	num_=0;	
	decimalPlaces_=0;
}

GribValueDump::~GribValueDump()
{
	clear();
}

void GribValueDump::clear()
{
	if(latitude_)
		delete [] latitude_;
		
	if(longitude_)
		delete [] longitude_;
		
	if(value_)
		delete [] value_;
	
	latitude_=0;
	longitude_=0;
	value_=0;
	num_=0;	
	gridType_.clear();
				
}

void GribValueDump::readFailed(FILE* fp,grib_handle* gh)
{
	grib_handle_delete(gh);
	fclose(fp);
	clear();
}	

bool GribValueDump::read(const std::string& fgrib, int msgCnt)
{ 
 	//Get grib dump for the given field

  	FILE* fp;
  	grib_handle* gh=NULL;
  	
 	int err=0;
 	int grib_count=0;	

    GuiLog().task() << "Generating value dump for message: " << msgCnt <<
                       GuiLog::methodKey() << "ecCodes C interface";

	//Clear the currently allocated data
	clear();

	//open grib file for reading
  	fp = fopen(fgrib.c_str(),"r");
  	if(!fp) 
	{
        GuiLog().error() << "Cannot open grib file: \n        " << fgrib <<  "\n";
            return false;
  	}

	//Get messages form the file
    while(((gh = grib_handle_new_from_file(0,fp,&err)) != NULL || err != GRIB_SUCCESS) && grib_count < msgCnt)
	 {
		grib_count++;

		//if(!gh) 
		//{
      		//	log->error("GribValueDump::read() ---> Unable to create grib handle\n");
      		//	return;
    		//}

        if(grib_count == msgCnt)
		{

		  	if(!gh) 
			{      		 
                GuiLog().error() << "Unable to create grib handle for message no: " <<
                                                 grib_count;
                return false;
            }
		  
		  	size_t latNum, lonNum, valNum;
			string latName, lonName;
			
			//cout << "-- GRIB N. " << grib_count << "---/n";

			const int MAX_VAL_LEN=1024;				
			char cval[MAX_VAL_LEN];					
			size_t vlen=MAX_VAL_LEN;
			
			//Grid type
			if(grib_get_string(gh,"gridType",cval,&vlen) == GRIB_SUCCESS)
			{
				gridType_=string(cval);
			}
			else
			{
                GuiLog().error() << "No gridType data found";
				readFailed(fp,gh);
                return false;
			}		
			
			if(gridType_ == "sh")
			{
				latName="";
				lonName="";
                GuiLog().error() << "gridType \"sh\" is not supported";
				readFailed(fp,gh);
                return false;
			}
			else
			{
				latName="latitudes";
				lonName="longitudes";
			}			


			//Latitude
			grib_get_size(gh,latName.c_str(),&latNum);
			if(latNum >0)
			{
				latitude_ = new double[latNum]; 
			}
			else
			{	
                GuiLog().error() << "No latitude data found";
				readFailed(fp,gh);
                return false;
			}	
				
			if(grib_get_double_array(gh,latName.c_str(),latitude_,&latNum) != 0)
			{
                GuiLog().error() << "Failed to read latitude data";
				readFailed(fp,gh);
                return false;
	 		}
			
			//Longitude
			grib_get_size(gh,lonName.c_str(),&lonNum);
			if(lonNum != latNum)
			{
                GuiLog().error() << "No longitude data found";
				readFailed(fp,gh);
                return false;
			}	
			else
			{
				longitude_ = new double[lonNum]; 
			}	
				
			if(grib_get_double_array(gh,lonName.c_str(),longitude_,&lonNum) != 0)
			{
                GuiLog().error() << "Failed to read longitude data";
				readFailed(fp,gh);
                return false;
	 		}
			//Value
			grib_get_size(gh,"values",&valNum);
			if(valNum != latNum)
			{
                GuiLog().error() << "No value data found";
				readFailed(fp,gh);
                return false;
			}	
			else
			{
				value_ = new double[valNum]; 
			}
			
			if(grib_get_double_array(gh,"values",value_,&valNum) != 0)
			{
                GuiLog().error() << "Failed to read value data";
				readFailed(fp,gh);
                return false;
	 		}
			
			double max=-999999999.;
			double min=999999999.;
			for(unsigned int i=0; i < valNum; i++)
			{
			  	if(value_[i] > max)
				  	max=value_[i];
				if(value_[i] < min)
				  	min=value_[i];
			}
			
			double maxAbs=(fabs(max) > fabs(min))?fabs(max):fabs(min);
			decimalPlaces_=0;
			for(int i=0; i >-10; i--)
			{
			  	if(maxAbs > pow(10,static_cast<double>(i)))
				{
				  	decimalPlaces_=-i;
					break;
				}
			}	
			
					
			
			//Statistics
			/*double dval;
			if(grib_get_double(gh,"average",&dval) == GRIB_SUCCESS)
				average_=dval;
			if(grib_get_double(gh,"standardDeviation",&dval) == GRIB_SUCCESS)
				stdev_=dval;				
			if(grib_get_double(gh,"skewness",&dval) == GRIB_SUCCESS)
				skewness_=dval;
			if(grib_get_double(gh,"kurtosis",&dval) == GRIB_SUCCESS)
				kurtosis_=dval;*/
			num_=latNum;
				
		}
		
		if(gh)
			grib_handle_delete(gh);
  	}
	
	fclose(fp);

    return true;
}



//=========================================
//
//  GribMetaData
//
//=========================================

GribMetaData::GribMetaData() : hasMultiMessage_(false)
{		
    std::string cmd;
	
	GRIB_DEFINITION_PATH_ENV="";
	const char* grb_def_path=getenv("GRIB_DEFINITION_PATH");
	if(grb_def_path)
	{
		GRIB_DEFINITION_PATH_ENV=string(grb_def_path);
	}

    //Define the grib dump program name
#ifdef ECCODES_UI
    char *gdexe=getenv("CODES_UI_GRIB_DUMP");
#else
    char *gdexe=getenv("METVIEW_GRIB_DUMP");
#endif

    if (gdexe == 0)
	{	
		GRIB_DUMP_EXE="grib_dump";
	}
	else
	{
		GRIB_DUMP_EXE=gdexe;
	}

	grib_multi_support_on(0);
}

GribMetaData::~GribMetaData()
{
    clear();
}

void GribMetaData::clear()
{
    hasMultiMessage_=false;

    MvMessageMetaData::clear();
}

void GribMetaData::setFilter(const std::vector<off_t>& offset, const std::vector<int>& len)
{
	filterOffset_=offset;
	filterLen_=len;
	filterCnt_.clear();
	for(unsigned int i=0; i < filterOffset_.size(); i++)
	{
		filterCnt_.push_back(-1);
	}

	if(offset.size() != 0 && 
	   offset.size() == len.size())
	{
		filterEnabled_=true;
	}
}

void GribMetaData::setFileName(string fname)
{
    clear();

    fileName_=fname;
	totalMessageNum_=computeTotalMessageNum();
}

int GribMetaData::computeTotalMessageNum()
{	
  	FILE* fp;
	fp = fopen(fileName_.c_str(),"r");
  	if(!fp) 
	{
    		//log->error("GribMetaData::readMessages() ---> Cannot open grib file: \n        " + fileName_ + "\n");
    		return 0;
  	}
  	
  	int res=0;
  	if(grib_count_in_file(0,fp,&res) != GRIB_SUCCESS)
		res=0;
	
	fclose(fp);
	return res;
	  	
	/*stringstream in, err;
	
	string buf=GRIB_COUNT_EXE + " " + "\"" + fileName_ + "\"" ; 
	shellCommand(buf,in,err,tmpfilename_);

	if(!err.str().empty())
		return -1;

	int res;
	std::istringstream iss(in.str());
  	iss >> res;
	
	return res;*/
}

int GribMetaData::getEstimatedMessageNum()
{
	if(filterEnabled_ == true)
	{
		return filterOffset_.size();
	}

	return totalMessageNum_;
}


void GribMetaData::loadKeyProfile(MvKeyProfile *prof)
{
	prof->clearKeyData();
	readMessages(prof);
}

void GribMetaData::readMessages(MvKeyProfile *prof)
{
	/*vector<char*> name_space;
	name_space.push_back(0);
	name_space.push_back("geography");
	name_space.push_back("ls");== GRIB_SUCCESS
	name_space.push_back("mars");
	name_space.push_back("parameter");
	name_space.push_back("time");
	name_space.push_back("vertical");*/

  	FILE* fp;
  	grib_handle* gh=NULL;
 	int err=0;
 	int grib_count=0;
	long longValue;
	string cbuff;

    GuiLog().task() << "Generating grib key list for all the messages" <<
                       GuiLog::methodKey() << "ecCodes C interface";
	messageNum_=0;

	//open grib file for reading
  	fp = fopen(fileName_.c_str(),"r");
  	if(!fp) 
	{
        GuiLog().error() << "GribMetaData::readMessages() ---> Cannot open grib file: \n        " << fileName_ << "\n";
        return;
  	}

	//For filtered datasets
	if(filterEnabled_==true)
	{					
		//At the first scan we need to find the message cnt for each filtered message.
		//The goal is to fill up the filterCnt_ vector.
		if(firstScan_)
		{
			//long currentOffset=0;
			int msgCnt=0;
			while((gh = grib_handle_new_from_file(0,fp,&err)) != NULL || err != GRIB_SUCCESS)
	 		{
                if(!gh)
				{      				
                    GuiLog().error() <<  "GribMetaData::readMessages() --->  Unable to create grib handle for message count: " << grib_count+1;
                }
				
				msgCnt++;

				if(gh)
				{  
					if(grib_get_long(gh,"offset",&longValue) == GRIB_SUCCESS)
					{
						for(unsigned int i=0; i < filterOffset_.size(); i++)
						{
							off_t offset_diff = longValue - filterOffset_[i];
							if(ABS(offset_diff) < 120)
							{
								filterCnt_[i] = msgCnt;
								break;
							}
						}
					}
					else
					{						
                        GuiLog().error() <<  "GribMetaData::readMessages() --->  Cannot get offset for message count: " << grib_count+1;
                        grib_handle_delete(gh);
						return;
					}

					//currentOffset+=longValue;
					grib_handle_delete(gh);
				}	
			}
		}
		
		//Read the filtered messages
		for(unsigned int i=0; i < filterOffset_.size(); i++)
		{
			grib_count++;

			fseek(fp,filterOffset_[i],SEEK_SET);
	
			int err;
			gh = grib_handle_new_from_file(0,fp,&err);
	
			if(!gh) 
			{		
                GuiLog().error() <<  "GribMetaData::readMessages() --->  Unable to create grib handle for offset: " << filterOffset_[i];
				//return;
			}
				
			readMessage(prof,gh);
	
            broadcast(&MvMessageMetaDataObserver::messageScanStepChanged,grib_count);
						
			if(gh)
				grib_handle_delete(gh);
		}	
	}	
	else
	{	  
		//Get messages form the file
  		while((gh = grib_handle_new_from_file(0,fp,&err)) != NULL || err != GRIB_SUCCESS)
	 	{
            if(!gh)
			{      		
                GuiLog().error() <<  "GribMetaData::readMessages() --->  Unable to create grib handle for message count: " << grib_count+1 << endl;
            }
		
			grib_count++;

			readMessage(prof,gh);

            broadcast(&MvMessageMetaDataObserver::messageScanStepChanged,grib_count);

			if(gh) 
				grib_handle_delete(gh);
		}

		if(firstScan_)
		{
			if(totalMessageNum_ != grib_count)
			{
				if(totalMessageNum_ != 0)
				  	hasMultiMessage_=true;
			  	
				totalMessageNum_=grib_count;			  	
			}
		}
	}
	
	fclose(fp);
	
	messageNum_=grib_count;
	firstScan_=false;


	//------------------------------
	// Format key "count"
	//------------------------------

	char c[20];
	int n,c_format_id;
	const char c_format[4][5]={"%02d","%03d","%04d","%05d"};

	int num=messageNum_;
	if(num < 100)
	{
		c_format_id=0;
	}
	else if(num < 1000)
	{
		c_format_id=1;		
	}
	else if(num < 10000)
	{
		c_format_id=2;	
	}	
	else
	{
		c_format_id=3;	
	}

	MvKey* key=prof->key("count");
	if(key)
	{
		for(unsigned int i=0; i< key->value().size(); i++)
		{
			string s=key->value()[i];
			if(s != "")
			{ 
				stringstream sst(s);
				sst >> n;
				sprintf(c,c_format[c_format_id],n);
				s=c;
				key->setValue(i,s);
			}
		}
	
	}

	key=prof->key("MV_Index");
	if(key)
	{
		for(unsigned int i=0; i< key->value().size(); i++)
		{
			sprintf(c,c_format[c_format_id],i+1);
			string s(c);
			key->setValue(i,s);
		}
	
	}
}

void GribMetaData::getKeyList(int count,string nameSpace,MvKeyProfile *prof)
{
	list<string> keys;
	getKeyList(count,nameSpace,keys);
	for(list<string>::iterator it=keys.begin(); it != keys.end(); it++)
	{
		prof->addKey(new MvKey((*it),(*it)));
	}
}


void GribMetaData::getKeyList(int count,string nameSpace,list<string> &keys)
{
    std::vector<char*> nameSpaceL;
	
	if(nameSpace=="Default")
	{
		nameSpaceL.push_back(0);
	}
	else
	{
		char* nsC = new char[nameSpace.size() + 1];
		std::copy(nameSpace.begin(),nameSpace.end(),nsC);
		nsC[nameSpace.size()] = '\0';
		nameSpaceL.push_back(nsC);
	}

	// list of blacklisted keys - these can be very expensive to compute; we can
	// always put them back in if someone really really needs them...
	// to be considered: minimum, maximum
    std::vector<string> key_blacklist;
	key_blacklist.push_back("distinctLatitudes");
	key_blacklist.push_back("distinctLongitudes");
	key_blacklist.push_back("distinctLatitudes");

	//Other keys which define constant values and are not really useful for users
	key_blacklist.push_back("7777");
	key_blacklist.push_back("x");


  	FILE* fp;
  	grib_handle* gh=NULL;
  	grib_keys_iterator* kiter=NULL;
 	//const int MAX_KEY_LEN=255;
	//const int MAX_VAL_LEN=1024;
	unsigned long key_iterator_filter_flags=GRIB_KEYS_ITERATOR_ALL_KEYS;

 	int err=0;
 	int grib_count=0;
	int keyType;

 	//char value[MAX_VAL_LEN];
  	//size_t vlen=MAX_VAL_LEN;
	string cbuff;
	   
    GuiLog().task() << "Generating grib key list for message: " << count <<
                    GuiLog::methodKey() << "ecCodes C interface";

	//open grib file for reading
  	fp = fopen(fileName_.c_str(),"r");
  	if(!fp) 
	{
        GuiLog().error() << "GribMetaData::getKeyList() ---> Cannot open grib file: \n        " <<
                         fileName_ <<  "\n";
        return;
  	}

	//Get messages form the file
  	while((gh = grib_handle_new_from_file(0,fp,&err)) != NULL || err != GRIB_SUCCESS)
	 {
		grib_count++;

		/*if(!gh) 
		{
      			log->error("GribMetaData::getKeyList() ---> Unable to create grib handle\n");
      			return;
    		}*/

		if(grib_count == count)
		{
   			//cout << "-- GRIB N. " << grib_count << "---/n";
    			
			if(!gh) 
			{      			
                GuiLog().error() << "GribMetaData::getKeyList() ---> Unable to create grib handle for message no: " <<
                                                 grib_count << "\n";

                for(std::vector<char*>::iterator it=nameSpaceL.begin(); it != nameSpaceL.end(); it++)
				{
					if((*it) != 0)
						delete [] *it;
				}
				
				return;
    			}
			
			for(unsigned int ns = 0 ; ns  < nameSpaceL.size(); ns++)
			{
   				//Get key iterator
				kiter=grib_keys_iterator_new(gh,key_iterator_filter_flags,nameSpaceL[ns]);
    				if (!kiter)
				{      				
                    GuiLog().error() << "GribMetaData::getKeyList() ---> Unable to create keys iterator for message no: " <<
                                                 grib_count << "\n";

                    for(std::vector<char*>::iterator it=nameSpaceL.begin(); it != nameSpaceL.end(); it++)
					{
						if((*it) != 0)
							delete [] *it;
					}
					
					return;
    				}

				//Loop for the keys in the current message until all the keys are found
   				while(grib_keys_iterator_next(kiter))
   				{
					const char* name = grib_keys_iterator_get_name(kiter);
				        if(!name)
					{
						continue;
					}	
					
					string cbuff=name; 
				
					// if this key is in our blacklist, then do not use it
                    std::vector<string>::const_iterator i = find(key_blacklist.begin(), key_blacklist.end(), name);

					if (i != key_blacklist.end())
						continue;

					//Add namespace prefix to the key name (e.g. mars.param)
					if(nameSpaceL[ns] != 0)
					{
                        cbuff = std::string(nameSpaceL[ns]) + "." + cbuff;
					}	
						
					if(grib_get_native_type(gh,cbuff.c_str(),&keyType) == GRIB_SUCCESS &&  
					   (keyType == GRIB_TYPE_STRING || keyType == GRIB_TYPE_LONG ||
		    		           keyType == GRIB_TYPE_DOUBLE))
					 {						
						keys.push_back(cbuff);	
					 }	
				}	

				grib_keys_iterator_delete(kiter);
    			}    

			grib_handle_delete(gh);
			break;			
		}

		grib_handle_delete(gh);
  	}

	keys.sort();

	fclose(fp);

	//This has to be called before calling return in this method!!
    for(std::vector<char*>::iterator it=nameSpaceL.begin(); it != nameSpaceL.end(); it++)
	{
		if((*it) != 0)
			delete [] *it;
	}
 

}

long GribMetaData::gribapiVersionNumber()
{
	return grib_get_api_version();
}			

void GribMetaData::readMessage(MvKeyProfile* prof,grib_handle *gh)
{
	
  	if(!gh)
	{
		for(unsigned int i=0; i< prof->size() ; i++ )
		{	
			prof->at(i)->addValue("N/A");
		}
	  	return;
	}
  
  	const int MAX_VAL_LEN=1024;	
	long longValue;
	char value[MAX_VAL_LEN];
	string svalue;
	size_t vlen=MAX_VAL_LEN;

	for(unsigned int i=0; i< prof->size() ; i++ )
	{
		size_t len;
		int keyType;
		const string &name=prof->at(i)->name();
		bool foundValue=false;

		if(grib_get_native_type(gh,name.c_str(),&keyType) == GRIB_SUCCESS &&
		   grib_get_size(gh,name.c_str(),&len) == GRIB_SUCCESS &&
		   (keyType == GRIB_TYPE_STRING || keyType == GRIB_TYPE_LONG ||
		    keyType == GRIB_TYPE_DOUBLE))
		{
			if(keyType == GRIB_TYPE_STRING || len == 1)
			{						
				if(prof->at(i)->readIntAsString() == false && 
				   keyType ==GRIB_TYPE_LONG)			
				{					
				   	if(grib_get_long(gh,name.c_str(),&longValue) == GRIB_SUCCESS)
					{
						stringstream s;
						s << longValue;
						prof->at(i)->addValue(s.str().c_str());
						foundValue=true;
					}
				}
				else
				{
					vlen=MAX_VAL_LEN;
					if(grib_get_string(gh,name.c_str(),value,&vlen) == GRIB_SUCCESS)
					{						
						prof->at(i)->addValue(value);
						foundValue=true;
					}
				}
			}
			else if((strcmp(name.c_str(),"paramId") == 0 || strcmp(name.c_str(),"parameter.paramId") == 0) &&
			        keyType == GRIB_TYPE_LONG)
			{
				if(prof->at(i)->readIntAsString() == false)
				{  				  
					if(grib_get_long(gh,name.c_str(),&longValue) == GRIB_SUCCESS)
					{
						stringstream s;
						s << longValue;
						prof->at(i)->addValue(s.str().c_str());
						foundValue=true;
					}						
				}  
				else
				{
					vlen=MAX_VAL_LEN;
					if(grib_get_string(gh,name.c_str(),value,&vlen) == GRIB_SUCCESS)
					{						
						prof->at(i)->addValue(value);
						foundValue=true;
					}
				}					
			}  			
			else
			{		
				stringstream s;
				s << len;
				svalue="Array (" + s.str() + ")";	
				prof->at(i)->addValue(svalue);
				foundValue=true;
			}
		}


		if(!foundValue)
		{
			prof->at(i)->addValue("N/A");	
		}		
	}
}	
