/***************************** 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 <fstream>
#include <iostream>
#include <map>
#include <string>
#include <vector>

#include "fstream_mars_fix.h"

#include "Metview.h"

#include "MvDate.h"
#include "Tokenizer.h"
#include "MvMiscellaneous.h"

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


//#define FLEXTRA_CHK(str) if(!(str)) return false
#define FLEXTRA_CHK(str) ({if(!(str)) {setError(13); return false;} })

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

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

protected:
    void initIds();

    bool getMvDate(const std::string& dd, const std::string& tt, MvDate&);

    bool generatePathnamesFile(const std::string&, MvRequest&, const std::string&, const std::string&);
    bool generateCommandFile(const std::string&, MvRequest&);
    bool generateStartPointsFile(const std::string&, MvRequest&);
    bool generateStartCetFile(const std::string&, MvRequest&);
    bool generateStartFlightFile(const std::string& fStart, MvRequest& in);
    bool getDateRange(MvDate& rangeStart, MvDate& rangeEnd, MvRequest& in);
    bool getPredefInputPath(const std::string&, const MvDate&, const MvDate&, std::string&);


    std::map<std::string, std::string> directionIds_;
    std::map<std::string, std::string> modeIds_;
    std::map<std::string, std::string> intervalIds_;
    std::map<std::string, std::string> cetTypeIds_;
    std::map<std::string, std::string> cetLevelUnitsIds_;
    std::map<std::string, std::string> flightTypeIds_;
    std::map<std::string, std::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 std::string& dd, const std::string& tt, MvDate& res)
{
    if (dd.size() != 8 || tt.size() != 6)
        return false;

    std::string sDate = dd.substr(0, 4) + "-" + dd.substr(4, 2) + "-" + dd.substr(6, 2);
    std::string sTime = tt.substr(0, 2) + ":" + tt.substr(2, 2) + ":" + dd.substr(4, 2);

    std::string s = sDate + " " + sTime;

    res = MvDate(s.c_str());

    return true;
}

bool FlextraRun::generatePathnamesFile(const std::string& fPathnames, MvRequest& in, const std::string& optionsPath, const std::string& outPath)
{
    std::string inPath, fAvailable;
    std::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;
        }

        // These must be absolute paths
        FLEXTRA_CHK(dataR.getValue("INPUT_DATA_PATH", inPath));
        FLEXTRA_CHK(dataR.getValue("AVAILABLE_FILE_PATH", fAvailable));
    }
    else if (strcmp(input_mode, "PATH") == 0) {
        // These can be relative paths
        FLEXTRA_CHK(in.getPath("FLEXTRA_INPUT_PATH", inPath, false));
        FLEXTRA_CHK(in.getValue("FLEXTRA_AVAILABLE_FILE_PATH", fAvailable));

        if (fAvailable.empty() || fAvailable == "SAME_AS_INPUT_PATH") {
            fAvailable = inPath + "/AVAILABLE";
        }
        else {
            fAvailable += "/AVAILABLE";
        }
    }
#if 0
	else if(strcmp(input_mode,"EC_EUROPE") == 0 || strcmp(input_mode,"EC_GLOBAL") == 0)
	{
		std::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;			
		}
		
		std::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());
		
	}
#endif
    else {
        marslog(LOG_EROR, "Invalid value for parameter FLEXTRA_INPUT_MODE: %s", input_mode);
        setError(13);
        return false;
    }

    // Pathnames file
    std::ofstream out(fPathnames.c_str());
    out << optionsPath << "/" << std::endl;
    out << outPath << "/" << std::endl;
    out << inPath << "/" << std::endl;
    out << fAvailable << std::endl;
    out << "================================================" << std::endl;
    out << " ";
    out.close();

    return true;
}

bool FlextraRun::generateCommandFile(const std::string& fCmd, MvRequest& in)
{
    std::ofstream out(fCmd.c_str());

    std::string str, str2, trMode;

    // Find outrun mode. It is needed for the starting dates
    FLEXTRA_CHK(in.getValueId("FLEXTRA_RUN_MODE", trMode, modeIds_));

    out << "**********************************************\n\
					     \n\
  Input file for the trajectory model FLEXTRA\n\
 					     \n\
*********************************************\n\n";

    FLEXTRA_CHK(in.getValue("FLEXTRA_RUN_LABEL", str, true));
    out << str << std::endl;

    FLEXTRA_CHK(in.getValueId("FLEXTRA_TRAJECTORY_DIRECTION", str, directionIds_));
    out << str << std::endl;

    // format is: hhhmmss
    FLEXTRA_CHK(in.getValue("FLEXTRA_TRAJECTORY_LENGTH", str));
    if (MvDate::timeToLenAsHHHMMSS(str, str2) == false) {
        marslog(LOG_EROR, "Invalid value red for FLEXTRA_TRAJECTORY_LENGTH=%s. Expected format is: HHH[:MM[:SS]]", str.c_str());
        setError(13);
        return false;
    }
    out << str2 << std::endl;

    if (trMode != "3") {
        FLEXTRA_CHK(in.getDate("FLEXTRA_FIRST_STARTING_DATE", str));
        out << str << " ";

        FLEXTRA_CHK(in.getTime("FLEXTRA_FIRST_STARTING_TIME", str));
        out << str << std::endl;

        FLEXTRA_CHK(in.getDate("FLEXTRA_LAST_STARTING_DATE", str));
        out << str << " ";

        FLEXTRA_CHK(in.getTime("FLEXTRA_LAST_STARTING_TIME", str));
        out << str << std::endl;

        // format is: hhhmmss
        FLEXTRA_CHK(in.getValue("FLEXTRA_STARTING_TIME_INTERVAL", str));
        if (MvDate::timeToLenAsHHHMMSS(str, str2) == false) {
            marslog(LOG_EROR, "Invalid value red for FLEXTRA_STARTING_TIME_INTERVAL=%s. Expected format is: HHH[:MM[:SS]]", str.c_str());
            setError(13);
            return false;
        }
        // FLEXTRA_CHK(in.getTimeLenInSec("FLEXTRA_STARTING_TIME_INTERVAL",str));
        out << str2 << std::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 {
        std::vector<std::string> startDate, startTime;
        FLEXTRA_CHK(in.getValue("FLEXTRA_FLIGHT_STARTING_DATES", startDate));
        FLEXTRA_CHK(in.getValue("FLEXTRA_FLIGHT_STARTING_TIMES", startTime));
        int cnt = startDate.size();

        if (cnt > 0 && static_cast<int>(startTime.size()) == cnt) {
            std::string dStr, tStr;
            FLEXTRA_CHK(MvRequest::getDate("FLEXTRA_FLIGHT_STARTING_DATES", startDate[0], dStr));
            FLEXTRA_CHK(MvRequest::getTime("FLEXTRA_FLIGHT_STARTING_TIMES", startTime[0], tStr));
            out << dStr << " " << tStr << std::endl;

            FLEXTRA_CHK(MvRequest::getDate("FLEXTRA_FLIGHT_STARTING_DATES", startDate[cnt - 1], dStr));
            FLEXTRA_CHK(MvRequest::getTime("FLEXTRA_FLIGHT_STARTING_TIMES", startTime[cnt - 1], tStr));
            out << dStr << " " << tStr << std::endl;

            out << "030000" << std::endl;
        }
    }

    FLEXTRA_CHK(in.getValueId("FLEXTRA_OUTPUT_INTERVAL_MODE", str, intervalIds_));
    out << str << " ";
    FLEXTRA_CHK(in.getTimeLenInSec("FLEXTRA_OUTPUT_INTERVAL_VALUE", str));

#if 0
    if(str.find("0") !=std::string::npos &&  str.find_first_not_of("0") == std::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));
#endif
    out << str << std::endl;

    // It might be disabled in the inteface (for CET and FLIGHT modes)
    in.getValue("FLEXTRA_UNCERTAINTY_TRAJECTORIES", str, true);
    if (str == "ON") {
        FLEXTRA_CHK(in.getValue("FLEXTRA_UNCERTAINTY_TRAJECTORY_NUMBER", str));
        out << str << " ";
        FLEXTRA_CHK(in.getValue("FLEXTRA_UNCERTAINTY_TRAJECTORY_DISTANCE", str));
        out << str << " ";
        FLEXTRA_CHK(in.getValue("FLEXTRA_UNCERTAINTY_TRAJECTORY_TIME_CONSTANT", str));
        out << str << " ";
        FLEXTRA_CHK(in.getValue("FLEXTRA_U_RANDOM_ERROR", str));
        out << str << " ";
        FLEXTRA_CHK(in.getValue("FLEXTRA_V_RANDOM_ERROR", str));
        out << str << " ";
        FLEXTRA_CHK(in.getValue("FLEXTRA_W_RANDOM_ERROR", str));
        out << str << std::endl;
    }
    else {
        out << "0 0.5 2.0 0.08 0.08 0.08" << std::endl;
    }


    FLEXTRA_CHK(in.getValue("FLEXTRA_INTERPOLATION_TYPE", str));
    out << str << std::endl;

    FLEXTRA_CHK(in.getValue("FLEXTRA_CFL_SPATIAL", str));
    out << str << std::endl;

    FLEXTRA_CHK(in.getValue("FLEXTRA_CFL_TEMPORAL", str));
    out << str << std::endl;

    // FLEXTRA_CHK(getParamValueId(str,in,"FLEXTRA_RUN_MODE",modeIds_));
    out << trMode << std::endl;

    out.close();

    return true;
}

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

    std::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";

    in.getValue("FLEXTRA_RUN_MODE", str);

    std::vector<std::string> type, name, lat, lon, lev, levUnit;

    FLEXTRA_CHK(in.getValue("FLEXTRA_NORMAL_TYPES", type));
    FLEXTRA_CHK(in.getValue("FLEXTRA_NORMAL_NAMES", name));
    FLEXTRA_CHK(in.getValue("FLEXTRA_NORMAL_LATITUDES", lat));
    FLEXTRA_CHK(in.getValue("FLEXTRA_NORMAL_LONGITUDES", lon));
    FLEXTRA_CHK(in.getValue("FLEXTRA_NORMAL_LEVELS", lev));
    FLEXTRA_CHK(in.getValue("FLEXTRA_NORMAL_LEVEL_UNITS", levUnit));

    unsigned int cnt = type.size();
    if (cnt == 0) {
        marslog(LOG_EROR, "No values specified for parameter FLEXTRA_NORMAL_TYPES!");
        setError(13);
        return false;
    }

    std::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("!") != std::string::npos) {
        marslog(LOG_EROR, "%s", errTxt.c_str());
        setError(13);
        return false;
    }

    for (unsigned int i = 0; i < cnt; i++) {
        out << lon[i] << std::endl;
        out << lat[i] << std::endl;
        out << type[i] << std::endl;
        out << levUnit[i] << std::endl;
        out << lev[i] << std::endl;
        out << name[i] << std::endl;
        out << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << std::endl;
    }

    out.close();

    return true;
}

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

    std::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";

    std::string type, name, levUnit, dx, dy, dz, levTop, levBottom;
    FLEXTRA_CHK(in.getValueId("FLEXTRA_CET_TYPE", type, cetTypeIds_));
    FLEXTRA_CHK(in.getValue("FLEXTRA_CET_NAME", name));
    FLEXTRA_CHK(in.getValueId("FLEXTRA_CET_LEVEL_UNITS", levUnit, cetLevelUnitsIds_));
    FLEXTRA_CHK(in.getValue("FLEXTRA_CET_DX", dx));
    FLEXTRA_CHK(in.getValue("FLEXTRA_CET_DY", dy));
    FLEXTRA_CHK(in.getValue("FLEXTRA_CET_DZ", dz));
    FLEXTRA_CHK(in.getValue("FLEXTRA_CET_TOP_LEVEL", levTop));
    FLEXTRA_CHK(in.getValue("FLEXTRA_CET_BOTTOM_LEVEL", levBottom));

    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 = 0.;
    std::vector<double> areaL;
    for (int i = 0; i < cnt; i++) {
        in.getValue(dval, "FLEXTRA_CET_AREA", i);
        areaL.push_back(dval);
    }
    out << areaL[1] << std::endl;
    out << areaL[0] << std::endl;
    out << areaL[3] << std::endl;
    out << areaL[2] << std::endl;
    out << dx << std::endl;
    out << dy << std::endl;
    out << type << std::endl;
    out << levUnit << std::endl;
    out << levBottom << std::endl;
    out << levTop << std::endl;
    out << dz << std::endl;
    out << name << std::endl;
    out << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << std::endl;

    out.close();

    return true;
}

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

    std::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";

    std::string type, name, levUnit;
    FLEXTRA_CHK(in.getValueId("FLEXTRA_FLIGHT_TYPE", type, flightTypeIds_));
    FLEXTRA_CHK(in.getValue("FLEXTRA_FLIGHT_NAME", name));
    FLEXTRA_CHK(in.getValueId("FLEXTRA_FLIGHT_LEVEL_UNITS", levUnit, flightLevelUnitsIds_));


    std::vector<std::string> lat, lon, lev, startDate, startTime;
    FLEXTRA_CHK(in.getValue("FLEXTRA_FLIGHT_LATITUDES", lat));
    FLEXTRA_CHK(in.getValue("FLEXTRA_FLIGHT_LONGITUDES", lon));
    FLEXTRA_CHK(in.getValue("FLEXTRA_FLIGHT_LEVELS", lev));
    FLEXTRA_CHK(in.getValue("FLEXTRA_FLIGHT_STARTING_DATES", startDate));
    FLEXTRA_CHK(in.getValue("FLEXTRA_FLIGHT_STARTING_TIMES", startTime));

    unsigned int cnt = lat.size();
    if (cnt == 0) {
        marslog(LOG_EROR, "No values specified for parameter FLEXTRA_FLIGHT_LATITUDES!");
        setError(13);
        return false;
    }

    std::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("!") != std::string::npos) {
        marslog(LOG_EROR, "%s", errTxt.c_str());
        setError(13);
        return false;
    }

    out << name << std::endl;
    out << type << std::endl;
    out << levUnit << std::endl;
    out << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << std::endl;

    for (unsigned int i = 0; i < cnt; i++) {
        std::string tStr, dStr;
        FLEXTRA_CHK(MvRequest::getDate("FLEXTRA_FLIGHT_STARTING_DATES", startDate[i], dStr));
        FLEXTRA_CHK(MvRequest::getTime("FLEXTRA_FLIGHT_STARTING_TIMES", startTime[i], tStr));

        out << dStr << " " << tStr << std::endl;
        out << lon[i] << std::endl;
        out << lat[i] << std::endl;
        out << lev[i] << std::endl;
        out << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << std::endl;
    }

    out.close();

    return true;
}


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

bool FlextraRun::getDateRange(MvDate& rangeStart, MvDate& rangeEnd, MvRequest& in)
{
    std::string str, dStr, tStr, trMode;
    int lenSec = 0;
    MvDate startDate1, startDate2;

    // Find outrun mode. It is needed for the starting dates
    FLEXTRA_CHK(in.getValueId("FLEXTRA_RUN_MODE", trMode, modeIds_));

    FLEXTRA_CHK(in.getTimeLenInSec("FLEXTRA_TRAJECTORY_LENGTH", tStr));
    lenSec = metview::fromString<int>(tStr);


    if (trMode != "3") {
        FLEXTRA_CHK(in.getDate("FLEXTRA_FIRST_STARTING_DATE", dStr));
        FLEXTRA_CHK(in.getTime("FLEXTRA_FIRST_STARTING_TIME", tStr));

        if (!getMvDate(dStr, tStr, startDate1))
            return false;

        FLEXTRA_CHK(in.getDate("FLEXTRA_LAST_STARTING_DATE", dStr));
        FLEXTRA_CHK(in.getTime("FLEXTRA_LAST_STARTING_TIME", tStr));

        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 {
        std::vector<std::string> startDate, startTime;
        FLEXTRA_CHK(in.getValue("FLEXTRA_FLIGHT_STARTING_DATES", startDate));
        FLEXTRA_CHK(in.getValue("FLEXTRA_FLIGHT_STARTING_TIMES", startTime));
        int cnt = startDate.size();

        if (cnt > 0 && static_cast<int>(startTime.size()) == cnt) {
            FLEXTRA_CHK(MvRequest::getDate("FLEXTRA_FLIGHT_STARTING_DATES", startDate[0], dStr));
            FLEXTRA_CHK(MvRequest::getTime("FLEXTRA_FLIGHT_STARTING_TIMES", startTime[0], tStr));

            if (!getMvDate(dStr, tStr, startDate1))
                return false;

            FLEXTRA_CHK(MvRequest::getDate("FLEXTRA_FLIGHT_STARTING_DATES", startDate[cnt - 1], dStr));
            FLEXTRA_CHK(MvRequest::getTime("FLEXTRA_FLIGHT_STARTING_TIMES", startTime[cnt - 1], tStr));

            if (!getMvDate(dStr, tStr, startDate2))
                return false;
        }
    }


    rangeStart = startDate1;
    rangeEnd = startDate2 + static_cast<double>(lenSec) / 86400.;

    return true;
}

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

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

    while ((dirp = readdir(dp)) != nullptr) {
        std::string name(dirp->d_name);
        if (name.size() == 13 && name.find("_") != std::string::npos) {
            std::string s;
            std::string sDate = name.substr(0, 8);
            std::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)
{
    std::cout << "--------------FlextraRun::serve()--------------" << std::endl;
    in.print();

    std::string errTxt;

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


    // Create  tmp dir for the flextra run
    std::string tmpPath;
    if (!metview::createWorkDir("flextra", tmpPath, errTxt)) {
        marslog(LOG_EROR, "%s", errTxt.c_str());
        setError(13);
        return;
    }

    // Flextra exe
    std::string exe;
    if (!in.getPathAndReplace("FLEXTRA_EXE_PATH", exe, "USE_MV_FLEXTRA_EXE", "_UNDEF_")) {
        setError(13);
        return;
    }

    // Genarate pathnames  file
    std::string fPathnames = tmpPath + "/pathnames";
    std::string optionsPath = tmpPath;
    std::string outPath = tmpPath;
    if (!generatePathnamesFile(fPathnames, in, optionsPath, outPath)) {
        return;
    }

    // Generate COMMAND file
    std::string fCmd = tmpPath + "/COMMAND";
    if (!generateCommandFile(fCmd, in)) {
        return;
    }

    // Get FLEXTRA RUN MODE
    std::string trMode;
    in.getValueId("FLEXTRA_RUN_MODE", trMode, modeIds_);

    if (trMode == "1") {
        std::string fStart = tmpPath + "/STARTPOINTS";
        if (!generateStartPointsFile(fStart, in)) {
            return;
        }
    }
    else if (trMode == "2") {
        std::string fStart = tmpPath + "/STARTCET";
        if (!generateStartCetFile(fStart, in)) {
            return;
        }
    }
    else if (trMode == "3") {
        std::string fStart = tmpPath + "/STARTFLIGHT";
        if (!generateStartFlightFile(fStart, in)) {
            return;
        }
    }

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

    std::string resFileName = "res.txt";
    std::string logFileName = "log.txt";

    // Run the flextra script
    std::string cmd = flextraScript + " \"" + tmpPath + "\" \"" + exe + "\" \"" + trModeName + "\" \"" + resFileName + "\" \"" + logFileName + "\"";

    // marslog(LOG_INFO,"Execute command: %s",cmd.c_str());
    std::cout << "Execute command: " << cmd << std::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) {
        std::string logFile = tmpPath + "/" + logFileName;
        std::ifstream in(logFile.c_str());
        std::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;
        }
    }

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