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

 Copyright 2012 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 "inc_iostream.h"
#include "Metview.h"

#include "MvDate.h"
#include "Tokenizer.h"
#include "MvMiscelaneous.h"

#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

using namespace std;

#define FLEXTRA_CHK(str) if(!(str)) return false

class Base : public MvService { 
protected:
	Base(char* a) : MvService(a) {};
};

class FlextraRun: public Base {	
public:
	FlextraRun() : Base("FLEXTRA_RUN") {initIds();}
	void serve(MvRequest&,MvRequest&);

  protected:	
	void initIds();
	bool getMvDate(const string& dd,const string& tt,MvDate&);
	bool getDate(const string& dd,string& res, const string&);
	bool getTime(const string& dd,string& res, const string&);
	bool getTimeLen(const string& tt,string& res, const string&,bool);
	bool getParamValueId(string&,MvRequest&,string,const map<string,string>&);
	bool getParamValue(string&,MvRequest&,string,bool);
	bool getParamValue(vector<string>&,MvRequest&,string);
	
	bool generatePathnamesFile(const string&,MvRequest&,const string&,const string&);
	bool generateCommandFile(const string&,MvRequest&);
	bool generateStartPointsFile(const string&,MvRequest&);
	bool generateStartCetFile(const string&,MvRequest&);
	bool generateStartFlightFile(const string& fStart,MvRequest& in);
	bool getDateRange(MvDate& rangeStart,MvDate& rangeEnd,MvRequest& in);
	bool getPredefInputPath(const string&,const MvDate&,const MvDate&,string &);
	
	
	map<string,string> directionIds_;
	map<string,string> modeIds_;
	map<string,string> intervalIds_;
	map<string,string> cetTypeIds_;
	map<string,string> cetLevelUnitsIds_;
	map<string,string> flightTypeIds_;
	map<string,string> flightLevelUnitsIds_;
};

void FlextraRun::initIds()
{
	directionIds_["FORWARD"]="1";
        directionIds_["BACKWARD"]="-1";
                
        modeIds_["NORMAL"]="1"; 
        modeIds_["CET"]="2";
	modeIds_["FLIGHT"]="3";
	
	intervalIds_["ORIGINAL"]="0";
        intervalIds_["INTERVAL"]="1";
	intervalIds_["BOTH"]="2";	
	
	cetTypeIds_["3D"]="1"; 
        cetTypeIds_["MODEL LEVEL"]="2";
	cetTypeIds_["ISOBARIC"]="4";
	cetTypeIds_["ISENTROPIC"]="5";
		
	cetLevelUnitsIds_["METRES ASL"]="1"; 
        cetLevelUnitsIds_["METRES AGL"]="2";
	cetLevelUnitsIds_["HPA"]="3";
	
	flightTypeIds_["3D"]="1"; 
        flightTypeIds_["MODEL LEVEL"]="2";
	flightTypeIds_["ISOBARIC"]="4";
	flightTypeIds_["ISENTROPIC"]="5";
	
	flightLevelUnitsIds_["METRES ASL"]="1"; 
        flightLevelUnitsIds_["METRES AGL"]="2";
	flightLevelUnitsIds_["HPA"]="3";
}	

bool FlextraRun::getMvDate(const string& dd,const string& tt,MvDate& res)
{
	if(dd.size() != 8 || tt.size() != 6)
	  	return false;
  
  	string sDate=dd.substr(0,4) + "-" + dd.substr(4,2) + "-" + dd.substr(6,2);
	string sTime=tt.substr(0,2) + ":" + tt.substr(2,2) + ":" + dd.substr(4,2);

	string s=sDate + " " + sTime;

	res=MvDate(s.c_str());  

	return true;
}  


bool FlextraRun::getDate(const string& dd,string& res,const string& parName)
{ 
  	res=dd;
	
	if(dd.size() < 8)
	{
		istringstream iss(dd);
 		double d;
		iss >> d; 
		char buf[9];
		MvDate md(julian_to_date(today() + d,1));
		md.Format("yyyymmdd", buf);
		res=string(buf);
	}
	
	bool retVal=(res.size() == 8)?true:false;
	
	if(!retVal)
	{
		marslog(LOG_EROR,"Invalid date format used for paramater: %s",parName.c_str());
		setError(13);
	}	
		
	return retVal;
}  

bool FlextraRun::getTime(const string& tt,string& res,const string& parName)
{ 
  	string hh, mm="00", ss="00";
	bool retVal=true;
	
	vector<string> tok;
	Tokenizer parse(":");
	parse(tt,tok);
		
	if(tok.size() > 0)
	{	
		if(tok[0].size() == 1)	
	  		hh = "0" + tok[0];
		else
		  	hh=tok[0];
		
		if(hh.size() !=  2)
			retVal=false;
	}
	if(tok.size() > 1)
	{	
		mm = tok[1];
		if(mm.size() !=  2)
			retVal=false;
	}

	if(tok.size() > 2)
	{	
		ss = tok[2];
		if(ss.size() !=  2)
			retVal=false;
	}	
	
	res=hh + mm + ss;
	
	retVal=(res.size() == 6)?true:false;

	if(!retVal)
	{
		marslog(LOG_EROR,"Invalid time format used for paramater: %s",parName.c_str());
		setError(13);
	}	
		
	return retVal;
}  


bool FlextraRun::getTimeLen(const string& tt,string& res,const string& parName,bool inSeconds)
{ 
  	string hh, mm="00", ss="00";
	bool retVal=true;
	
	vector<string> tok;
	Tokenizer parse(":");
	parse(tt,tok);
	
	if(tok.size() > 0)
	{	
		if(tok[0].size() == 1)
			hh = "00" + tok[0];
		else if(tok[0].size() == 2)	
	  		hh = "0" + tok[0];
		else
		  	hh=tok[0];
		
		if(hh.size() !=  3)
			retVal=false;
	}
	if(tok.size() > 1)
	{	
		mm = tok[1];
		if(mm.size() !=  2)
			retVal=false;
	}

	if(tok.size() > 2)
	{	
		ss = tok[2];
		if(ss.size() !=  2)
			retVal=false;
	}	
	
	if(inSeconds)
	{
	  	int sec,i;
		istringstream ish(hh);
		ish >> i;
		sec=i*3600;
		istringstream ism(mm);
		ism >> i;
		sec+=i*60;
		istringstream iss(ss);
		iss >> i;
		sec+=i;
		
		stringstream sst;
		sst << sec;	
		res=sst.str();
		retVal=true;		
	}	
	else
	{  	
		res=hh + mm + ss;
		retVal=(res.size() == 7)?true:false;
	}

	if(!retVal)
	{
		marslog(LOG_EROR,"Invalid time lenght format used for paramater: %s",parName.c_str());
		setError(13);
	}	

	return retVal;
}  

bool FlextraRun::getParamValueId(string& value,MvRequest& in,string parName,const map<string,string>& idMap)
{	
	bool canBeEmpty=true;
	string errTxt;
  	if(!in.getValueId(parName.c_str(),value,idMap,canBeEmpty,errTxt))
	{
	  	marslog(LOG_EROR,"%s",errTxt.c_str());
		setError(13);
		return false;
	}
	
	return true;
}

bool FlextraRun::getParamValue(string& value,MvRequest& in,string parName,bool canBeEmpty=false)
{
	string errTxt;
  	if(!in.getValue(parName.c_str(),value,canBeEmpty,errTxt))
	{
	  	marslog(LOG_EROR,"%s",errTxt.c_str());
		setError(13);
		return false;
	}
	
	return true;
}

bool FlextraRun::getParamValue(vector<string>& value,MvRequest& in,string parName)
{
	string errTxt;
	if(!in.getValue(parName.c_str(),value,false,errTxt))
	{
	  	marslog(LOG_EROR,"%s",errTxt.c_str());
		setError(13);
		return false;
	}
	
	return true;
}

bool FlextraRun::generatePathnamesFile(const string& fPathnames,MvRequest& in,const string& optionsPath,const string& outPath)
{
	string inPath,fAvailable;
    string errTxt;
		
	const char *input_mode=in("FLEXTRA_INPUT_MODE");	
	if(!input_mode)
	{
	  	marslog(LOG_EROR,"No parameter FLEXTRA_INPUT_MODE is defined!");
		setError(13);	
		return false;
	}
	  
	if(strcmp(input_mode,"ICON") == 0)
	{
		MvRequest dataR=in("FLEXTRA_INPUT_DATA");
		const char* iconType=dataR.getVerb();
		//If no icon is specified
		if(!iconType || strcmp(iconType,"FLEXTRA_INPUT") != 0)
		{
			marslog(LOG_EROR,"No FLEXTRA_INPUT icon is specified!");
			setError(13);
			return false;
		}

		FLEXTRA_CHK(getParamValue(inPath,dataR,"INPUT_DATA_PATH"));
		FLEXTRA_CHK(getParamValue(fAvailable,dataR,"AVAILABLE_FILE_PATH"));
	}	
	else if(strcmp(input_mode,"PATH") == 0) 
	{
        if(!in.getPath("FLEXTRA_INPUT_PATH",inPath,false,errTxt))
        {
            marslog(LOG_EROR,"No parameter FLEXTRA_INPUT_PATH is specified!");
            setError(13);
            return false;
        }

        if(!in.getPath("FLEXTRA_AVAILABLE_FILE_PATH",fAvailable,false,errTxt))
        {
            marslog(LOG_EROR,"No parameter FLEXTRA_AVAILABLE_FILE_PATH is specified!");
            setError(13);
            return false;
        }

        if(fAvailable.find("SAME_AS_INPUT_PATH") != string::npos)
		{
			fAvailable = inPath + "/AVAILABLE";
		}	
	}
	else if(strcmp(input_mode,"EC_EUROPE") == 0 || strcmp(input_mode,"EC_GLOBAL") == 0)
	{
		string envVar=(strcmp(input_mode,"EC_EUROPE") == 0)?"MV_FLEXTRA_INPUT_EUROPE":"MV_FLEXTRA_INPUT_GLOBAL";
		  
	  	char *predefPath=getenv(envVar.c_str());
		if(predefPath == 0)  
		{	
			marslog(LOG_EROR,"No %s env variable is defined! Cannot find data for input mode \"%s\"!",envVar.c_str(),input_mode);
			setError(13);
			return false;			
		}
		
		string rootPath(predefPath);
	  	MvDate startDate,endDate;
		if(!getDateRange(startDate,endDate,in))
		{  	
		  	marslog(LOG_EROR,"No data is found for input mode \"%s\"!",input_mode);
			setError(13);
			return false;
		}
		if(!getPredefInputPath(rootPath,startDate,endDate,inPath))
		{
		  	marslog(LOG_EROR,"No data is found for input mode \"%s\"!",input_mode);
			setError(13);
			return false;
		}	
		fAvailable = inPath + "/AVAILABLE";
		marslog(LOG_INFO,"Input data path set to: %s",inPath.c_str());
		
	}
	else
	{
		marslog(LOG_EROR,"Invalid value for parameter FLEXTRA_INPUT_MODE: %s",input_mode);
		setError(13);
		return false;
	}
	
	//Pathnames file	
	ofstream out(fPathnames.c_str());	
	out << optionsPath << "/" << endl;
	out << outPath << "/" << endl;
	out << inPath << "/" << endl;
	out << fAvailable << endl;
	out << "================================================" << endl;
	out << " ";
	out.close();
	
	return true;
}
	
bool FlextraRun::generateCommandFile(const string& fCmd,MvRequest& in)
{
	ofstream out(fCmd.c_str());	
	
	string str,str2,trMode;
		
	//Find outrun mode. It is needed for the starting dates
	FLEXTRA_CHK(getParamValueId(trMode,in,"FLEXTRA_RUN_MODE",modeIds_));
	
out << "**********************************************\n\
					     \n\
  Input file for the trajectory model FLEXTRA\n\
 					     \n\
*********************************************\n\n";

	FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_RUN_LABEL"));
	out << str << endl;
	
	FLEXTRA_CHK(getParamValueId(str,in,"FLEXTRA_TRAJECTORY_DIRECTION",directionIds_));
	out << str << endl;
		
	FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_TRAJECTORY_LENGTH"));
	FLEXTRA_CHK(getTimeLen(str,str2,"FLEXTRA_TRAJECTORY_LENGTH",false));
	out << str2 << endl;
	
	if(trMode != "3")
	{
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_FIRST_STARTING_DATE"));
		FLEXTRA_CHK(getDate(str,str2,"FLEXTRA_FIRST_STARTING_DATE"));	
		out << str2 << " ";
		
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_FIRST_STARTING_TIME"));
		FLEXTRA_CHK(getTime(str,str2,"FLEXTRA_FIRST_STARTING_TIME"));
		out << str2 << endl;
		
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_LAST_STARTING_DATE"));
		FLEXTRA_CHK(getDate(str,str2,"FLEXTRA_LAST_STARTING_DATE"));	
		out << str2 << " ";
	
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_LAST_STARTING_TIME"));
		FLEXTRA_CHK(getTime(str,str2,"FLEXTRA_LAST_STARTING_TIME"));
		out << str2 << endl;
	
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_STARTING_TIME_INTERVAL"));
		FLEXTRA_CHK(getTimeLen(str,str2,"FLEXTRA_STARTING_TIME_INTERVAL",false));
		out << str2 << endl;
	}

	//In FLIGHT mode the starting date,time and interval are disabled in the 
	//user interface beause FLEXTRA does not use them for the trajectory 
	//computations. However FLEXTRA does need a proper date here, otherwise it crashes.
	//So we use the first date and time from the FLIGHT starting points here.
	else
	{
	  	vector<string> startDate,startTime;		
		FLEXTRA_CHK(getParamValue(startDate,in,"FLEXTRA_FLIGHT_STARTING_DATES"));
		FLEXTRA_CHK(getParamValue(startTime,in,"FLEXTRA_FLIGHT_STARTING_TIMES"));
		int cnt=startDate.size();
		
		if(cnt > 0 && static_cast<int>(startTime.size()) == cnt)
		{
			string dStr,tStr;
			FLEXTRA_CHK(getDate(startDate[0],dStr,"FLEXTRA_FLIGHT_STARTING_DATES"));
			FLEXTRA_CHK(getTime(startTime[0],tStr,"FLEXTRA_FLIGHT_STARTING_TIMES"));
		  	out << dStr << " " << tStr << endl;
			
			FLEXTRA_CHK(getDate(startDate[cnt-1],dStr,"FLEXTRA_FLIGHT_STARTING_DATES"));
			FLEXTRA_CHK(getTime(startTime[cnt-1],tStr,"FLEXTRA_FLIGHT_STARTING_TIMES"));
		  	out << dStr << " " << tStr << endl;
			
	 	 	out << "030000" << endl;
		}	
	}	 
	
	FLEXTRA_CHK(getParamValueId(str,in,"FLEXTRA_OUTPUT_INTERVAL_MODE",intervalIds_));
	out << str << " ";
	FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_OUTPUT_INTERVAL_VALUE"));
	
	if(str.find("0") != string::npos &&  str.find_first_not_of("0") == string::npos)
	{
		marslog(LOG_EROR,"Invalid value specified for FLEXTRA_OUTPUT_INTERVAL_VALUE: %s",str.c_str());		
		marslog(LOG_EROR,"It has to be greater than 0!");
		setError(13);
		return false;	
	}  
	
	FLEXTRA_CHK(getTimeLen(str,str2,"FLEXTRA_OUTPUT_INTERVAL_VALUE",true));
	out << str2 << endl;
	
	//It might be disabled in the inteface (for CET and FLIGHT modes) 
	getParamValue(str,in,"FLEXTRA_UNCERTAINTY_TRAJECTORIES",true);
	if(str == "ON")
	{	
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_UNCERTAINTY_TRAJECTORY_NUMBER"));
		out << str << " ";
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_UNCERTAINTY_TRAJECTORY_DISTANCE"));
		out << str << " ";	
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_UNCERTAINTY_TRAJECTORY_TIME_CONSTANT"));
		out << str << " ";
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_U_RANDOM_ERROR"));
		out << str << " ";
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_V_RANDOM_ERROR"));
		out << str << " ";
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_W_RANDOM_ERROR"));
		out << str << endl;
	}
	else
	{
	   out << "0 0.5 2.0 0.08 0.08 0.08" << endl;
	  
	}  
	
	
	FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_INTERPOLATION_TYPE"));
	out << str << endl;	
	
	FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_CFL_SPATIAL"));
	out << str << endl;	
	
	FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_CFL_TEMPORAL"));
	out << str << endl;
	
	//FLEXTRA_CHK(getParamValueId(str,in,"FLEXTRA_RUN_MODE",modeIds_));
	out << trMode << endl;
	
	out.close();
	
	return true;
}	
	
bool FlextraRun::generateStartPointsFile(const string& fStart,MvRequest& in)
{
	ofstream out(fStart.c_str());	
	
	string str;
	

out << "**********************************************************************\n\
*                                                                    *\n\
*                 TRAJECTORY MODEL                                   *\n\
*                 DEFINITION OF STARTING/ENDING POINTS               *\n\
*                                                                    *\n\
*  The first 7 characters of the comment are also used as filenames. *\n\
*  Therefore, they cannot be blank and they must be different for    *\n\
*  each starting point.                                              *\n\
*                                                                    *\n\
*  Kind of trajectory: 1 = 3 dimensional                             *\n\
*                      2 = on model layers                           *\n\
*                      3 = mixing layer                              *\n\
*                      4 = isobaric                                  *\n\
*                      5 = isentropic                                *\n\
*                                                                    *\n\
**********************************************************************\n\
*                                                                    *\n\
*  Unit of z coordinate: 1 = Meters above sea level                  *\n\
*                        2 = Meters above ground                     *\n\
*                        3 = Hectopascal                             *\n\
*                                                                    *\n\
*  For mixing layer trajectories (kind 3), the z coordinate must be  *\n\
*  given in m.a.g.l. (option 2)                                      *\n\
*                                                                    *\n\
**********************************************************************\n\
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";

	getParamValue(str,in,"FLEXTRA_RUN_MODE");

	vector<string> type,name,lat,lon,lev,levUnit;
	
	FLEXTRA_CHK(getParamValue(type,in,"FLEXTRA_NORMAL_TYPES"));
	FLEXTRA_CHK(getParamValue(name,in,"FLEXTRA_NORMAL_NAMES"));
	FLEXTRA_CHK(getParamValue(lat,in,"FLEXTRA_NORMAL_LATITUDES"));
	FLEXTRA_CHK(getParamValue(lon,in,"FLEXTRA_NORMAL_LONGITUDES"));
	FLEXTRA_CHK(getParamValue(lev,in,"FLEXTRA_NORMAL_LEVELS"));
	FLEXTRA_CHK(getParamValue(levUnit,in,"FLEXTRA_NORMAL_LEVEL_UNITS"));
	
	unsigned int cnt=type.size();	
	if(cnt ==0)
	{
		marslog(LOG_EROR,"No values specified for parameter FLEXTRA_NORMAL_TYPES!");
	 	setError(13);
		return false; 
	}
	
	string errTxt="Incosistent number of items specified for parameter: FLEXTRA_NORMAL_";
	if(name.size() != cnt)
		errTxt+="NAMES!";
	else if(lat.size() != cnt)
		errTxt+="LATITUDES!";	
	else if(lon.size() != cnt)
		errTxt+="LONGITUDES!";
	else if(lev.size() != cnt)
		errTxt+="LEVELS!";  
	else if(levUnit.size() != cnt)
		errTxt+="LEVEL_UNITS!"; 
	
	if(errTxt.find("!") != string::npos) 
	{
	  	marslog(LOG_EROR,"%s",errTxt.c_str());
		setError(13);
		return false; 
	}
	
	for(unsigned int i=0; i < cnt; i++)
	{	  
		out << lon[i] << endl;
		out << lat[i] << endl;
		out << type[i] << endl;
		out << levUnit[i] << endl;
		out << lev[i] << endl;
		out << name[i] << endl;
		out << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;

	}

	out.close();
	
	return true;
}	

bool FlextraRun::generateStartCetFile(const string& fStart,MvRequest& in)
{
	ofstream out(fStart.c_str());	
	
	string str;
	

	
out << "**********************************************************************\n\
*                                                                    *\n\
*                 TRAJECTORY MODEL                                   *\n\
*                 DEFINITION OF THE CET DOMAIN                       *\n\
*  A CET STARTING DOMAIN IS DEFINED BY THE LOWER LEFT AND UPPER RIGHT*\n\
*  CORNER IN A LATITUDE/LONGITUDE COORDINATE SYSTEM, AND BY A LOWER  *\n\
*  AND UPPER LEVEL. TRAJECTORIES ARE STARTED AT DISTANCES DX, DY AND *\n\
*  DZ WITHIN THIS DOMAIN.                                            *\n\
*                                                                    *\n\
*  Kind of trajectory: 1 = 3 dimensional                             *\n\
*                      2 = on model layers                           *\n\
*                      3 = not allowed in CET mode                   *\n\
*                      4 = isobaric                                  *\n\
*                      5 = isentropic                                *\n\
*                                                                    *\n\
**********************************************************************\n\
*                                                                    *\n\
*  Unit of z coordinate: 1 = Meters above sea level                  *\n\
*                        2 = Meters above ground                     *\n\
*                        3 = Hectopascal                             *\n\
*                                                                    *\n\
*  The vertical distance DZ between the trajectories must be         *\n\
*  given in the same units.                                          *\n\
*                                                                    *\n\
**********************************************************************\n\
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";

	string type,name,levUnit,dx,dy,dz,levTop,levBottom;
	FLEXTRA_CHK(getParamValueId(type,in,"FLEXTRA_CET_TYPE",cetTypeIds_));
	FLEXTRA_CHK(getParamValue(name,in,"FLEXTRA_CET_NAME"));
	FLEXTRA_CHK(getParamValueId(levUnit,in,"FLEXTRA_CET_LEVEL_UNITS",cetLevelUnitsIds_));
	FLEXTRA_CHK(getParamValue(dx,in,"FLEXTRA_CET_DX"));
	FLEXTRA_CHK(getParamValue(dy,in,"FLEXTRA_CET_DY"));
	FLEXTRA_CHK(getParamValue(dz,in,"FLEXTRA_CET_DZ"));
	FLEXTRA_CHK(getParamValue(levTop,in,"FLEXTRA_CET_TOP_LEVEL"));
	FLEXTRA_CHK(getParamValue(levBottom,in,"FLEXTRA_CET_BOTTOM_LEVEL"));
	
	int cnt = in.countValues("FLEXTRA_CET_AREA");
	if(cnt != 4)
	{
	  	marslog(LOG_EROR,"No paramater FLEXTRA_CET_AREA is specified!");
		setError(13);
		return false;	
	}  
	  
	double dval; 
	vector<double> areaL;
	for(int i=0; i< cnt; i++)
	{
		in.getValue(dval,"FLEXTRA_CET_AREA",i);
	 	areaL.push_back(dval);
	}	
	out << areaL[1] << endl;
	out << areaL[0] << endl;
	out << areaL[3] << endl;
	out << areaL[2] << endl;
	out << dx << endl;
	out << dy << endl;
	out << type << endl;
	out << levUnit << endl;
	out << levBottom << endl;
	out << levTop << endl;
	out << dz << endl;
	out << name << endl;
	out << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
	
	out.close();
	
	return true;
}	

bool FlextraRun::generateStartFlightFile(const string& fStart,MvRequest& in)
{
	ofstream out(fStart.c_str());	
	
	string str;
	

out << "**********************************************************************\n\
*              TRAJECTORY MODEL                                      *\n\
*              DEFINITION OF STARTING/ENDING POINTS IN FLIGHT MODE   *\n\
*  This file defines starting points separated non-uniformly in      *\n\
*  space as well as in time. Thus, both starting times AND starting  *\n\
*  coordinates must be given.                                        *\n\
*  The starting times must be strictly in temporal order.            *\n\
*  For backward trajectories, the temporal order must be reversed.   *\n\
*  In line #28 of this file, the name of the output file must be     *\n\
*  indicated. Lines #29 and #30 must contain kind of trajectory and  *\n\
*  the unit of the z coordinate to be used. Line #31 is arbitrary,   *\n\
*  then follows a sequence of points.                                *\n\
*  Kind of trajectory: 1 = 3 dimensional                             *\n\
*                      2 = on model layers                           *\n\
*                      3 = mixing layer                              *\n\
*                      4 = isobaric                                  *\n\
*                      5 = isentropic                                *\n\
**********************************************************************\n\
*                                                                    *\n\
*  Unit of z coordinate: 1 = Meters above sea level                  *\n\
*                        2 = Meters above ground                     *\n\
*                        3 = Hectopascal                             *\n\
*                                                                    *\n\
*  For mixing layer trajectories (kind 3), the z coordinate must be  *\n\
*  given in m.a.g.l. (option 2)                                      *\n\
*                                                                    *\n\
**********************************************************************\n";

	string type,name,levUnit;
	FLEXTRA_CHK(getParamValueId(type,in,"FLEXTRA_FLIGHT_TYPE",flightTypeIds_));
	FLEXTRA_CHK(getParamValue(name,in,"FLEXTRA_FLIGHT_NAME"));
	FLEXTRA_CHK(getParamValueId(levUnit,in,"FLEXTRA_FLIGHT_LEVEL_UNITS",flightLevelUnitsIds_));


	vector<string> lat,lon,lev,startDate,startTime;	
	FLEXTRA_CHK(getParamValue(lat,in,"FLEXTRA_FLIGHT_LATITUDES"));
	FLEXTRA_CHK(getParamValue(lon,in,"FLEXTRA_FLIGHT_LONGITUDES"));
	FLEXTRA_CHK(getParamValue(lev,in,"FLEXTRA_FLIGHT_LEVELS"));
	FLEXTRA_CHK(getParamValue(startDate,in,"FLEXTRA_FLIGHT_STARTING_DATES"));
	FLEXTRA_CHK(getParamValue(startTime,in,"FLEXTRA_FLIGHT_STARTING_TIMES"));
	
	unsigned int cnt=lat.size();	
	if(cnt ==0)
	{
		marslog(LOG_EROR,"No values specified for parameter FLEXTRA_FLIGHT_LATITUDES!");
	 	setError(13);
		return false; 
	}
	
	string errTxt="Incosistent number of items specified for parameter: FLEXTRA_FLIGHT_";
	if(lon.size() != cnt)
		errTxt+="LONGITUDES!";
	else if(lev.size() != cnt)
		errTxt+="LEVELS!";  
	else if(startDate.size() != cnt)
		errTxt+="STARTING_DATES!";	
	else if(startTime.size() != cnt)
		errTxt+="STARTING_TIMES!";
	
	if(errTxt.find("!") != string::npos) 
	{
	  	marslog(LOG_EROR,"%s",errTxt.c_str());
		setError(13);
		return false; 
	}
		
	out << name << endl;
	out << type << endl;
	out << levUnit << endl;	
	out << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;

	for(unsigned int i=0; i < cnt; i++)
	{	  
		string tStr, dStr;
	  	FLEXTRA_CHK(getDate(startDate[i],dStr,"FLEXTRA_FLIGHT_STARTING_DATES"));
		FLEXTRA_CHK(getTime(startTime[i],tStr,"FLEXTRA_FLIGHT_STARTING_TIMES"));
		
		out << dStr << " " << tStr << endl;		
		out << lon[i] << endl;
		out << lat[i] << endl;
		out << lev[i] << endl;
		out << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;

	}

	out.close();
	
	return true;
}


//---------------------------------------------------
// Find out the time range the trajectories cover
//---------------------------------------------------

bool FlextraRun::getDateRange(MvDate& rangeStart,MvDate& rangeEnd,MvRequest& in)
{
	string str,dStr,tStr,trMode;
	int lenSec;
	MvDate startDate1,startDate2;
	
	//Find outrun mode. It is needed for the starting dates
	FLEXTRA_CHK(getParamValueId(trMode,in,"FLEXTRA_RUN_MODE",modeIds_));
		
	FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_TRAJECTORY_LENGTH"));
	FLEXTRA_CHK(getTimeLen(str,tStr,"FLEXTRA_TRAJECTORY_LENGTH",true));
	istringstream iss(tStr);
	iss >> lenSec;
	

	if(trMode != "3")
	{
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_FIRST_STARTING_DATE"));
		FLEXTRA_CHK(getDate(str,dStr,"FLEXTRA_FIRST_STARTING_DATE"));	
				
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_FIRST_STARTING_TIME"));
		FLEXTRA_CHK(getTime(str,tStr,"FLEXTRA_FIRST_STARTING_TIME"));
		
		if(!getMvDate(dStr,tStr,startDate1))
			return false;  
						
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_LAST_STARTING_DATE"));
		FLEXTRA_CHK(getDate(str,dStr,"FLEXTRA_LAST_STARTING_DATE"));	
			
		FLEXTRA_CHK(getParamValue(str,in,"FLEXTRA_LAST_STARTING_TIME"));
		FLEXTRA_CHK(getTime(str,tStr,"FLEXTRA_LAST_STARTING_TIME"));
		
		if(!getMvDate(dStr,tStr,startDate2))
			return false;  
				
	}

	//In FLIGHT mode the starting date,time and interval are disabled in the 
	//user interface beause FLEXTRA does not use them for the trajectory 
	//computations. However FLEXTRA does need a proper date here, otherwise it crashes.
	//So we use the first date and time from the FLIGHT starting points here.
	else
	{
	  	vector<string> startDate,startTime;		
		FLEXTRA_CHK(getParamValue(startDate,in,"FLEXTRA_FLIGHT_STARTING_DATES"));
		FLEXTRA_CHK(getParamValue(startTime,in,"FLEXTRA_FLIGHT_STARTING_TIMES"));
		int cnt=startDate.size();
		
		if(cnt > 0 && static_cast<int>(startTime.size()) == cnt)
		{
			FLEXTRA_CHK(getDate(startDate[0],dStr,"FLEXTRA_FLIGHT_STARTING_DATES"));
			FLEXTRA_CHK(getTime(startTime[0],tStr,"FLEXTRA_FLIGHT_STARTING_TIMES"));
		  	
			if(!getMvDate(dStr,tStr,startDate1))
				return false;  			
			
			FLEXTRA_CHK(getDate(startDate[cnt-1],dStr,"FLEXTRA_FLIGHT_STARTING_DATES"));
			FLEXTRA_CHK(getTime(startTime[cnt-1],tStr,"FLEXTRA_FLIGHT_STARTING_TIMES"));
		  	
			if(!getMvDate(dStr,tStr,startDate2))
				return false;  
		}	
	}
	
	
	rangeStart=startDate1;
	rangeEnd=startDate2 + static_cast<double>(lenSec)/86400.;
	
	return true;
}

bool FlextraRun::getPredefInputPath(const string& rootPath,const MvDate& startDate,const MvDate& /*endDate*/,string &resDir)
{
	//Scan rootpath for FLEXTRA input directories   
	DIR *dp;
    	struct dirent *dirp;
    	if((dp  = opendir(rootPath.c_str())) == NULL) 
    	{
        	marslog(LOG_EROR,"Failed to open directory: %s",rootPath.c_str());
        	return false;
    	}

	vector<MvDate> dateVec;
	vector<string> pathVec;

    	string::size_type pos;
    	while ((dirp = readdir(dp)) != NULL) 
    	{
		string name(dirp->d_name);
		if(name.size() == 13 && (pos=name.find("_")) != string::npos )
		{  
		  	string s;
			string sDate=name.substr(0,8);
			string sTime=name.substr(9,4);
		
			s=sDate;
			sDate=s.substr(0,4) + "-" + s.substr(4,2) + "-" + s.substr(6,2);
			
			s=sTime;
			sTime=s.substr(0,2) + ":" + s.substr(2,4) + ":00";
			
			s=sDate + " " + sTime;

			MvDate cDate(s.c_str());
			
			if(cDate <= startDate)
			{
			  	dateVec.push_back(cDate);
				pathVec.push_back(name);
			}	
		}	
	} 	

   	closedir(dp);
   
	if(pathVec.size() > 0)
	{
	  	resDir=rootPath + "/" + pathVec.back();
		return true;
	}	
	
	return false;
}  
	

void FlextraRun::serve( MvRequest& in, MvRequest& out)
{
  	cout << "--------------FlextraRun::serve()--------------" << endl;
  	in.print();

	string errTxt;
	
	//Find out flextra script path
	string flextraScript;
	char *mvbin=getenv("METVIEW_BIN");
	if (mvbin == 0)  
	{	
		marslog(LOG_EROR,"No METVIEW_BIN env variable is defined. Cannot locate script mv_flextra_run!");
		setError(13);
		return;
	}
	else
	{
		flextraScript=string(mvbin) +"/mv_flextra_run";
	}
	

	//Create  tmp dir for the flextra run
	string tmpPath;
	if(!metview::createWorkDir("flextra",tmpPath,errTxt))
	{
		marslog(LOG_EROR,"%s",errTxt.c_str());
		setError(13);
		return;
	}
	
	//FlextraRun exe
	string exe;	
	if(!in.getPath("FLEXTRA_EXE_PATH",exe,true,errTxt))
	{
		marslog(LOG_EROR,"%s",errTxt.c_str());
		setError(13);
		return;
	}	
	if(exe.empty())
	{
	  	exe="_UNDEF_";
	}	
	
			
	//Genarate pathnames  file
	string fPathnames=tmpPath + "/pathnames";
	string optionsPath=tmpPath;
	string outPath=tmpPath;
	if(!generatePathnamesFile(fPathnames,in,optionsPath,outPath))
	{
	  	return;
	}	
	
	//Generate COMMAND file
	string fCmd=tmpPath + "/COMMAND";
	if(!generateCommandFile(fCmd,in))
	{
	  	return;
	}
	
	//Get FLEXTRA RUN MODE
	string trMode;
	getParamValueId(trMode,in,"FLEXTRA_RUN_MODE",modeIds_);	
	
	if(trMode  == "1")
	{
	  	string fStart=tmpPath + "/STARTPOINTS";
		if(!generateStartPointsFile(fStart,in))
		{
		  	return;
		}	
	}
	else if(trMode  == "2")
	{
	  	string fStart=tmpPath + "/STARTCET";
		if(!generateStartCetFile(fStart,in))
		{
		  	return;
		}	
	}
	else if(trMode  == "3")
	{
	  	string fStart=tmpPath + "/STARTFLIGHT";
		if(!generateStartFlightFile(fStart,in))
		{
		  	return;
		}	
	}

	//-----------------------
	// Run FLEXTRA
	//-----------------------
	
	//Find out the directory where the Flextra Run icon is located
	/*string callerDir;
	const char* callerIcon=in("_NAME");
	if(callerIcon)
	{
	  	callerDir=string(callerIcon);
	  	
		char *mvudir=getenv("METVIEW_USER_DIRECTORY");
		if(mvudir)  
		{
	  		callerDir=string(mvudir) + "/" + callerDir;
		}	
	  	
	  	string::size_type pos=callerDir.find_last_of("/");
	 	if(pos != string::npos)
		{
		 	callerDir=callerDir.substr(0,pos);
		}	
	}
	  
	//Set the output path where the resulting files will be coupied
	string userOutPath;
	if(!getParamValue(userOutPath,in,"OUTPUT_PATH") || userOutPath.empty())
	{
	  	marslog(LOG_EROR,"No parameter OUTPUT_PATH is defined!");
		setError(13);	
		return;
	}
	
	if(userOutPath == "CURRENT_FOLDER")
	{
	  	if(callerDir.empty()) 
		{
		  	marslog(LOG_EROR,"Could not set OUTPUT_PATH properly to \"CURRENT_DIR\"!");
			setError(13);	
			return;
		}  		  
		userOutPath=callerDir;		  
	}  
	*/

	string trModeName;
	if(trMode  == "1")
		trModeName="NORMAL";
	if(trMode  == "2")
		trModeName="CET";
	if(trMode  == "3")
		trModeName="FLIGHT";

	string resFileName="res.txt";
	string logFileName="log.txt";
	
	//Run the flextra script
	string cmd = flextraScript + " \"" + tmpPath + "\" \"" + exe + "\" \"" + trModeName + "\" \"" + resFileName + "\" \"" + logFileName + "\""; 

	//marslog(LOG_INFO,"Execute command: %s",cmd.c_str());
	cout << "Execute command: " << cmd << endl;
	
	int ret=system(cmd.c_str());	
	
	//If the script failed read log file and
	//write it into LOG_EROR
	if(ret == -1 || WEXITSTATUS(ret) != 0)
	{
		string logFile=tmpPath + "/" + logFileName;		
		ifstream in(logFile.c_str());
		string line;

		if(WEXITSTATUS(ret) == 255)
		{
		  	marslog(LOG_WARN,"Warnings generated during FLEXTRA run!");
			while(getline(in,line))
			{
				marslog(LOG_WARN,"%s",line.c_str());
			}
			in.close();
			//setError(13);
		}  		
		else if(WEXITSTATUS(ret) == 1)
		{		
			marslog(LOG_EROR,"Failed to perform FLEXTRA run!");
			while(getline(in,line))
			{
				marslog(LOG_EROR,"%s",line.c_str());
			}
			in.close();
			setError(13);
			return;
		}
		else if(WEXITSTATUS(ret) > 1)
		{		
			marslog(LOG_EROR,"FLEXTRA run failed with exit code: %d !",WEXITSTATUS(ret));
			while(getline(in,line))
			{
				marslog(LOG_EROR,"%s",line.c_str());
			}
			in.close();
			setError(13);
			return;
		}
		
	}
	
	string resFile= tmpPath + "/" + resFileName;
		
	out=MvRequest("FLEXTRA_FILE");
	out("PATH")=resFile.c_str();
	
	out.print();	
}

  	
int main( int argc, char** argv )
{
	MvApplication theApp( argc, argv,"FlextraRun");

    	FlextraRun flextraRun;	
	
    	theApp.run();
}


