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

#include "fstream_mars_fix.h"

#include "Metview.h"

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

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

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

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

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

private:
    void initIds();
    std::string getReleaseDate(const std::string&, const std::string&);
    bool generatePathnamesFile(const std::string&, MvRequest&, const std::string&, const std::string&);
    bool generateCommandFile(const std::string&, MvRequest&);
    bool generateOutGridFile(const std::string&, MvRequest& in);
    bool generateReleasesFile(const std::string&, MvRequest& in);
    bool generateAgeClassesFile(const std::string&, MvRequest& in);
    bool generateReceptorsFile(const std::string&, MvRequest& in);

    std::map<std::string, std::string> directionIds_;
    std::map<std::string, std::string> outputIds_;
    std::map<std::string, std::string> particleDumpIds_;
    std::map<std::string, std::string> onOffIds_;
    std::map<std::string, std::string> sourceUnitsIds_;
    std::map<std::string, std::string> receptorUnitsIds_;
    std::map<std::string, std::string> writeInitIds_;
    std::map<std::string, std::string> relLevelUnitsIds_;
    std::map<std::string, std::string> domainFillIds_;
    std::string runDate_;
    bool forward_{true};
    int receptorNum_{0};
};

void FlexpartRun::initIds()
{
    directionIds_["FORWARD"] = "1";
    directionIds_["BACKWARD"] = "-1";

#if 0
    outputIds_["CONCENTRATION"]="1";
    outputIds_["MIXING RATIO"]="2";
	outputIds_["BOTH"]="3";
	outputIds_["PLUME"]="4";
	outputIds_["CONCENTRATION AND PLUME"]="5";
#endif

    particleDumpIds_["NO"] = "0";
    particleDumpIds_["AT_OUTPUT"] = "1";
    particleDumpIds_["AT_END"] = "2";

    onOffIds_["ON"] = "1";
    onOffIds_["OFF"] = "0";

    sourceUnitsIds_["MASS"] = "1";
    sourceUnitsIds_["MIXR"] = "2";

    receptorUnitsIds_["MASS"] = "1";
    receptorUnitsIds_["MIXR"] = "2";

    writeInitIds_["NO"] = "0";
    writeInitIds_["MASS"] = "1";
    writeInitIds_["MIXR"] = "2";

    relLevelUnitsIds_["AGL"] = "1";
    relLevelUnitsIds_["ASL"] = "2";
    relLevelUnitsIds_["HPA"] = "3";

    domainFillIds_["NO"] = "0";
    domainFillIds_["FULL"] = "1";
    domainFillIds_["O3_TRACER"] = "2";
}

std::string FlexpartRun::getReleaseDate(const std::string& rawStr, const std::string& decodedStr)
{
    std::string res = decodedStr;
    if (rawStr.size() < 3) {
        MvDate rr(metview::fromString<double>(runDate_));
        MvDate dd(metview::fromString<double>(decodedStr));
        MvDate today(0.);
        rr += dd - today;
        char buf[9];
        rr.Format("yyyymmdd", buf);
        res = std::string(buf);
    }
    return res;
}

bool FlexpartRun::generatePathnamesFile(const std::string& fPathnames, MvRequest& in, const std::string& optionsPath, const std::string& outPath)
{
    std::string inPath, fAvailable;

    MvRequest dataR = in("INPUT_DATA");

    // No icon is specified
    if (!dataR) {
        // These can be relative paths
        FLEXPART_CHK(in.getPath("INPUT_PATH", inPath, false));
        FLEXPART_CHK(in.getValue("AVAILABLE_FILE_PATH", fAvailable, true));

        if (fAvailable.empty() || fAvailable == "USE_INPUT_PATH") {
            fAvailable = inPath + "/AVAILABLE";
        }
        else {
            fAvailable += "/AVAILABLE";
        }
    }
    else {
        const char* iconType = dataR.getVerb();
        if (!iconType || strcmp(iconType, "FLEXPART_INPUT") != 0) {
            marslog(LOG_EROR, "No FLEXPART_INPUT icon is specified!");
            setError(13);
            return false;
        }

        // These must be absolute paths
        FLEXPART_CHK(dataR.getValue("INPUT_DATA_PATH", inPath));
        FLEXPART_CHK(dataR.getValue("AVAILABLE_FILE_PATH", fAvailable));
    }

    // 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 FlexpartRun::generateCommandFile(const std::string& fCmd, MvRequest& in)
{
    std::ofstream out(fCmd.c_str());

    std::string str, str2, trMode;
    std::string parName;

    out << "*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n";

    parName = "SIMULATION_DIRECTION";
    FLEXPART_CHK(in.getValueId(parName, str, directionIds_));
    bool bwd = (str == "-1") ? true : false;
    forward_ = !bwd;
    out << str << std::endl;

    parName = "STARTING_DATE";
    FLEXPART_CHK(in.getDate(parName, str));
    runDate_ = str;
    out << str << " ";

    parName = "STARTING_TIME";
    FLEXPART_CHK(in.getTime(parName, str));
    out << str << std::endl;

    parName = "ENDING_DATE";
    FLEXPART_CHK(in.getDate(parName, str));
    out << str << " ";

    parName = "ENDING_TIME";
    FLEXPART_CHK(in.getTime(parName, str));
    out << str << std::endl;

    parName = "OUTPUT_INTERVAL";
    FLEXPART_CHK(in.getTimeLenInSec(parName, str));
    out << str << std::endl;

    parName = "OUTPUT_AVERAGING_INTERVAL";
    FLEXPART_CHK(in.getTimeLenInSec(parName, str));
    out << str << std::endl;

    parName = "OUTPUT_SAMPLING_RATE";
    FLEXPART_CHK(in.getTimeLenInSec(parName, str, true));
    out << str << std::endl;

    parName = "PARTICLE_SPLITTING";
    FLEXPART_CHK(in.getValue(parName, str));
    if (str == "0")
        str = "999999999";
    else
        FLEXPART_CHK(in.getTimeLenInSec(parName, str));

    out << str << std::endl;

    parName = "SYNC_INTERVAL";
    FLEXPART_CHK(in.getTimeLenInSec(parName, str));
    out << str << std::endl;

    parName = "CTL";
    FLEXPART_CHK(in.getValue(parName, str));
    out << str << std::endl;

    parName = "VERTICAL_TIMESTEP_REDUCTION";
    FLEXPART_CHK(in.getValue(parName, str));
    out << str << std::endl;

    std::string outFieldType, outFieldId = "-1";
    parName = "OUTPUT_FIELD_TYPE";
    FLEXPART_CHK(in.getValue(parName, outFieldType));

    parName = "OUTPUT_TRAJECTORY";
    FLEXPART_CHK(in.getValueId(parName, str, onOffIds_, true));

    // Forward run
    if (!bwd) {
        if (outFieldType == "RTIME") {
            marslog(LOG_EROR, "OUTPUT_FIELD_TYPE cannot be set to %s for forward simulations!", outFieldType.c_str());
            setError(13);
            return false;
        }

        // no trajecory
        if (str == "0" || str.empty()) {
            if (outFieldType == "CONC")
                outFieldId = "1";
            else if (outFieldType == "MIXR")
                outFieldId = "2";
            else if (outFieldType == "BOTH")
                outFieldId = "3";
            else {
                marslog(LOG_EROR, "OUTPUT_FIELD_TYPE cannot be set to %s for forward simulations when OUTPUT_TRAJECTORY is set to OFF!", outFieldType.c_str());
                setError(13);
                return false;
            }
        }

        // has trajectory
        else if (str == "1") {
            if (outFieldType == "NONE")
                outFieldId = "4";
            else if (outFieldType == "CONC")
                outFieldId = "5";
            else {
                marslog(LOG_EROR, "OUTPUT_TRAJECTORY cannot be set to ON for forward simulations when OUTPUT_FIELD_TYPE is set to %s!", outFieldType.c_str());
                setError(13);
                return false;
            }
        }
    }

    // backward
    else {
        if (outFieldType != "RTIME" && outFieldType != "NONE") {
            marslog(LOG_EROR, "OUTPUT_FIELD_TYPE cannot be set to %s for backward simulations!", outFieldType.c_str());
            setError(13);
            return false;
        }

        // no trajecory
        if (str == "0" || str.empty()) {
            if (outFieldType == "RTIME")
                outFieldId = "1";
            else {
                marslog(LOG_EROR, "OUTPUT_FIELD_TYPE cannot be set to %s for backward simulations when OUTPUT_TRAJECTORY is set to OFF!", outFieldType.c_str());
                setError(13);
                return false;
            }
        }

        // has trajectory
        else if (str == "1") {
            if (outFieldType == "NONE")
                outFieldId = "4";
            else if (outFieldType == "RTIME")
                outFieldId = "5";
            else {
                marslog(LOG_EROR, "OUTPUT_TRAJECTORY cannot be set to ON for backward simulations when OUTPUT_FIELD_TYPE is set to %s!", outFieldType.c_str());
                setError(13);
                return false;
            }
        }
    }

    out << outFieldId << std::endl;

#if 0
    Dump Particle Position
    Enables dumping the particle positions. The possible values are none, output (at every output time) and end (only at the end of the simulation). The default value is none.
#endif
    parName = "DUMP_PARTICLE_POSITIONS";
    FLEXPART_CHK(in.getValueId(parName, str, particleDumpIds_));
    out << str << std::endl;

    parName = "SUBGRID_TERRAIN";
    FLEXPART_CHK(in.getValueId(parName, str, onOffIds_));
    out << str << std::endl;

    parName = "CONVECTION";
    FLEXPART_CHK(in.getValueId(parName, str, onOffIds_));
    out << str << std::endl;

    std::vector<std::string> ageVec;
    parName = "AGE_CLASSES";
    FLEXPART_CHK(in.getValue(parName, ageVec, true));
    if (ageVec.empty())
        out << "0" << std::endl;
    else
        out << "1" << std::endl;

    // parName="AGE_SPECTRA";
    // FLEXPART_CHK(in.getValueId(parName,str,onOffIds_));
    // out << str << std::endl;

    // Cont simulation with dumped particle data
    out << "0" << std::endl;

    parName = "OUTPUT_FOR_EACH_RELEASE";
    FLEXPART_CHK(in.getValueId(parName, str, onOffIds_));
    if (bwd && str != "1") {
        marslog(LOG_EROR, "For backward simulations OUTPUT_FOR_EACH_RELEASE must be set to ON");
        setError(13);
        return false;
    }
    out << str << std::endl;

    parName = "OUTPUT_FLUX";
    FLEXPART_CHK(in.getValueId(parName, str, onOffIds_));
    out << str << std::endl;

    parName = "DOMAIN_FILL";
    FLEXPART_CHK(in.getValueId(parName, str, domainFillIds_));
    out << str << std::endl;

    parName = "RELEASE_UNITS";
    FLEXPART_CHK(in.getValueId(parName, str, sourceUnitsIds_));
    out << str << std::endl;

    parName = "RECEPTOR_UNITS";
    FLEXPART_CHK(in.getValueId(parName, str, receptorUnitsIds_));
    out << str << std::endl;

    parName = "QUASI_LAGRANGIAN";
    FLEXPART_CHK(in.getValueId(parName, str, onOffIds_));
    out << str << std::endl;

    parName = "NESTED_OUTPUT";
    // FLEXPART_CHK(metview::getParamValueId(str,in,"FLEXPART_NESTED_OUTPUT",onOffIds_));
    // out << str << std::endl;
    out << "0" << std::endl;

    parName = "SENSITIVITY";
    FLEXPART_CHK(in.getValueId(parName, str, writeInitIds_));
    out << str << std::endl;

    out.close();

    return true;
}

bool FlexpartRun::generateReleasesFile(const std::string& fRelease, MvRequest& in)
{
    std::ofstream out(fRelease.c_str());

    std::string str;

    out << "*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n";

    // Species
    std::vector<std::string> sp;
    FLEXPART_CHK(in.getValue("RELEASE_SPECIES", sp));

    out << sp.size() << std::endl;
    for (auto& i : sp) {
        out << i << std::endl;
    }
    out << "===================================================================" << std::endl;

    // Loop through the releases

    MvRequest rel = in("RELEASES");
    do {
        MvRequest rs = rel.justOneRequest();
        const char* iconType = rs.getVerb();
        std::string iconName;
        if (const char* icnam = rs("_NAME"))
            iconName = std::string(icnam);

        // If no icon is specified
        if (!iconType || strcmp(iconType, "FLEXPART_RELEASE") != 0) {
            marslog(LOG_EROR, "No FLEXPART_RELEASE icon is specified!");
            setError(13);
            return false;
        }

        std::string parName;
        std::string tStr, dStr;
        parName = "STARTING_DATE";
        FLEXPART_CHK(rs.getValue(parName, str));
        FLEXPART_CHK(rs.getDate(parName, dStr));
        dStr = getReleaseDate(str, dStr);

        parName = "STARTING_TIME";
        FLEXPART_CHK(rs.getTime(parName, tStr));

        out << dStr << " " << tStr << std::endl;

        parName = "ENDING_DATE";
        FLEXPART_CHK(rs.getValue(parName, str));
        FLEXPART_CHK(rs.getDate(parName, dStr));
        dStr = getReleaseDate(str, dStr);

        parName = "ENDING_TIME";
        FLEXPART_CHK(rs.getTime(parName, tStr));

        out << dStr << " " << tStr << std::endl;

        parName = "AREA";
        std::vector<std::string> area;
        FLEXPART_CHK(rs.getValue(parName, area));

        if (area.size() != 4) {
            marslog(LOG_EROR, "Incorrect AREA is specified! Only %d parameters specified instead of 4!", area.size());
            setError(13);
            return false;
        }

        out << area[1] << std::endl;
        out << area[0] << std::endl;
        out << area[3] << std::endl;
        out << area[2] << std::endl;

        parName = "LEVEL_UNITS";
        FLEXPART_CHK(rs.getValueId(parName, str, relLevelUnitsIds_));
        out << str << std::endl;

        parName = "BOTTOM_LEVEL";
        FLEXPART_CHK(rs.getValue(parName, str));
        out << str << std::endl;

        parName = "TOP_LEVEL";
        FLEXPART_CHK(rs.getValue(parName, str));
        out << str << std::endl;

        parName = "PARTICLE_COUNT";
        FLEXPART_CHK(rs.getValue(parName, str));
        out << str << std::endl;

        parName = "MASSES";
        std::vector<std::string> massVec;
        FLEXPART_CHK(rs.getValue(parName, massVec));

        if (massVec.size() != sp.size()) {
            std::string errTxt = "Incosistent number of items specified for parameter: MASSES! Expected: " +
                                 std::to_string(sp.size()) + " found: " + std::to_string(massVec.size());
            marslog(LOG_EROR, "%s", errTxt.c_str());
            setError(13);
            return false;
        }

        for (auto& j : massVec) {
            out << j << std::endl;
        }

        parName = "NAME";
        FLEXPART_CHK(rs.getValue(parName, str));
        out << str << std::endl;

        out << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << std::endl;

    } while (rel.advance());

    out.close();

    return true;
}


bool FlexpartRun::generateOutGridFile(const std::string& fGrid, MvRequest& in)
{
    std::string parName;

    std::ofstream out(fGrid.c_str());

    out << "*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\
*** HEADER ***\n\n";

    // std::string west,south,name,nx,ny,dx,dy,dz;

    parName = "OUTPUT_AREA";
    std::vector<std::string> area;
    FLEXPART_CHK(in.getValue(parName, area));

    if (area.size() != 4) {
        marslog(LOG_EROR, "Incorrect OUTPUT_AREA is specified! Only %d parameters specified instead of 4!", area.size());
        setError(13);
        return false;
    }

    parName = "OUTPUT_GRID";
    std::vector<std::string> grid;
    FLEXPART_CHK(in.getValue(parName, grid));

    std::vector<std::string> numXy;
    std::string errStr;
    if (!metview::checkGrid(area, grid, numXy, errStr)) {
        marslog(LOG_EROR, "Inconsistency between OUTPUT_AREA and OUTPUT_GRID!");
        marslog(LOG_EROR, "%s", errStr.c_str());
        setError(13);
        return false;
    }

    if (numXy.size() != 2) {
        marslog(LOG_EROR, "Could not determine number of gridpoint on W-E direction!");
        setError(13);
        return false;
    }

    if (metview::fromString<int>(numXy[0]) < 0) {
        marslog(LOG_EROR, "Invalid number of gridpoints in W-E direction: %s", numXy[0].c_str());
        setError(13);
        return false;
    }

    if (metview::fromString<int>(numXy[1]) < 0) {
        marslog(LOG_EROR, "Invalid number of gridpoints in S-N direction: %s", numXy[1].c_str());
        setError(13);
        return false;
    }

    int cnt = in.countValues("OUTPUT_LEVELS");
    if (cnt < 1) {
        marslog(LOG_EROR, "No paramater OUTPUT_LEVELS is specified!");
        setError(13);
        return false;
    }

    double dval = 0.;
    std::vector<double> levL;  // we need to set a limit!
    for (int i = 0; i < cnt && i < 200; i++) {
        in.getValue(dval, "OUTPUT_LEVELS", i);
        levL.push_back(dval);
    }

    float fVal = 0.;
    int iVal = 0;

    out << std::fixed;

    fVal = metview::fromString<float>(area[1]);
    out << "-------" << std::endl;
    out << "    " << std::setw(6) << std::setprecision(4) << fVal << std::endl;
    out << "OUTLONLEFT" << std::endl;
    out << std::endl;

    fVal = metview::fromString<float>(area[0]);
    out << "-------" << std::endl;
    out << "    " << std::setw(6) << std::setprecision(4) << fVal << std::endl;
    out << "OUTLATLOWER" << std::endl;
    out << std::endl;

    iVal = metview::fromString<int>(numXy[0]);
    out << "-------" << std::endl;
    out << "    " << std::setw(5) << iVal << std::endl;
    out << "NUMXGRID" << std::endl;
    out << std::endl;

    iVal = metview::fromString<int>(numXy[1]);
    out << "-------" << std::endl;
    out << "    " << std::setw(5) << iVal << std::endl;
    out << "NUMYGRID" << std::endl;
    out << std::endl;

    fVal = metview::fromString<float>(grid[0]);
    out << "-------" << std::endl;
    out << "    " << std::setw(6) << std::setprecision(3) << fVal << std::endl;
    out << "DXOUTLON" << std::endl;
    out << std::endl;

    fVal = metview::fromString<float>(grid[1]);
    out << "-------" << std::endl;
    out << "    " << std::setw(6) << std::setprecision(3) << fVal << std::endl;
    out << "DYOUTLAT" << std::endl;
    out << std::endl;

    for (double i : levL) {
        out << "-------" << std::endl;
        out << "    " << std::setw(5) << std::setprecision(1) << i << std::endl;
        out << "LEVEL" << std::endl;
        out << std::endl;
    }

    out.close();

    return true;
}


bool FlexpartRun::generateAgeClassesFile(const std::string& fAge, MvRequest& in)
{
    std::ofstream out(fAge.c_str());

    out << "*** HEADER ***\n\
*** HEADER ***\n*** HEADER ***\n\
*** HEADER ***\n*** HEADER ***\n\
*** HEADER ***\n*** HEADER ***\n\
*** HEADER ***\n*** HEADER ***\n\
*** HEADER ***\n*** HEADER ***\n\
*** HEADER ***\n*** HEADER ***\n";

    std::vector<std::string> vals;
    FLEXPART_CHK(in.getValue("AGE_CLASSES", vals, true));

    if (vals.empty()) {
        out << "1" << std::endl;
        out << "999999999" << std::endl;
    }
    else {
        int cnt = static_cast<int>(vals.size());
        out << cnt << std::endl;

        for (int i = 0; i < cnt; i++) {
            std::string str = vals[i];
            std::string val;
            FLEXPART_CHK(in.getTimeLenInSec("AGE_CLASSES", str, val));
            out << val << std::endl;
        }
    }

    out.close();

    return true;
}

bool FlexpartRun::generateReceptorsFile(const std::string& fRec, MvRequest& in)
{
    std::ofstream out(fRec.c_str());
    out << "*** HEADER ***\n\
    *** HEADER ***\n*** HEADER ***\n\
    *** HEADER ***\n*** HEADER ***\n\
    *** HEADER ***\n*** HEADER ***\n";

    std::string parName = "RECEPTORS";
    std::string str;
    FLEXPART_CHK(in.getValueId(parName, str, onOffIds_));
    if (str == "0") {
        out.close();
        return true;
    }

    std::map<std::string, std::vector<std::string> > vals;
    std::vector<std::string> parLst;
    parLst.emplace_back("RECEPTOR_NAMES");
    parLst.emplace_back("RECEPTOR_LATITUDES");
    parLst.emplace_back("RECEPTOR_LONGITUDES");

    for (const auto& s : parLst) {
        FLEXPART_CHK(in.getValue(s, vals[s]));
    }

    unsigned int cnt = vals["RECEPTOR_NAMES"].size();

    receptorNum_ = cnt;

#if 0
    if(cnt ==0)
    {
        marslog(LOG_EROR,"No values specified for parameter RECEPTOR_NAMES!");
        setError(13);
        return false;
    }
#endif

    for (auto& val : vals) {
        if (val.second.size() != cnt) {
            std::string errTxt = "Incosistent number of items specified for parameter: " + val.first + "!";
            marslog(LOG_EROR, "%s", errTxt.c_str());
            setError(13);
            return false;
        }
    }

    out << std::fixed;
    for (unsigned int i = 0; i < cnt; i++) {
        out << "--" << std::endl;
        out << "    " << std::left << std::setw(16) << std::setfill(' ') << vals["RECEPTOR_NAMES"].at(i) << std::endl;

        out << "--" << std::endl;
        out << "--" << std::endl;
        out << "--" << std::endl;

        auto fVal = metview::fromString<float>(vals["RECEPTOR_LONGITUDES"].at(i));
        out << "    " << std::setw(6) << std::setprecision(4) << fVal << std::endl;

        out << "--" << std::endl;
        out << "--" << std::endl;
        out << "--" << std::endl;

        fVal = metview::fromString<float>(vals["RECEPTOR_LATITUDES"].at(i));
        out << "    " << std::setw(6) << std::setprecision(4) << fVal << std::endl;

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

    out.close();

    return true;
}

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

    // Find out flexpart script paths
    std::string flexpartScript;
    std::string ppScript;

    char* mvbin = getenv("METVIEW_BIN");
    if (mvbin == nullptr) {
        marslog(LOG_EROR, "No METVIEW_BIN env variable is defined. Cannot locate script mv_flexpart_run!");
        setError(13);
        return;
    }
    else {
        flexpartScript = std::string(mvbin) + "/mv_flexpart_run";
        ppScript = std::string(mvbin) + "/mv_flexpart_pp";
    }

    // Result path
    std::string resPath;
    std::string errTxt;
    if (!in.getPath("OUTPUT_PATH", resPath, false)) {
        setError(13);
        return;
    }

    // Create  tmp dir for the flexpart run
    std::string tmpPath;
    if (!metview::createWorkDir("flexpart", tmpPath, errTxt)) {
        marslog(LOG_EROR, "%s", errTxt.c_str());
        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;
    }

    // Generate OUTGRID file
    std::string fGrid = tmpPath + "/OUTGRID";
    if (!generateOutGridFile(fGrid, in)) {
        return;
    }

    // Generate RELEASES file
    std::string fRelease = tmpPath + "/RELEASES";
    if (!generateReleasesFile(fRelease, in)) {
        return;
    }

    // Generate AGECLASSES file
    std::string fAge = tmpPath + "/AGECLASSES";
    if (!generateAgeClassesFile(fAge, in)) {
        return;
    }

    // Generate RECEPTORS file
    std::string fReceptors = tmpPath + "/RECEPTORS";
    if (!generateReceptorsFile(fReceptors, in)) {
        return;
    }

    // Flexpart exe
    std::string exe;
    if (!in.getPathAndReplace("USER_EXE_PATH", exe, "USE_MV_FLEXPART_EXE", "_UNDEF_")) {
        setError(13);
        return;
    }

    // The resources directory
    std::string resourcePath;
    if (!in.getPathAndReplace("USER_RESOURCES_PATH", resourcePath, "USE_MV_FLEXPART_RESOURCES", "_UNDEF_")) {
        setError(13);
        return;
    }

    // The species directory
    std::string speciesPath;
    if (!in.getPathAndReplace("USER_SPECIES_PATH", speciesPath, "USE_MV_FLEXPART_SPECIES", "_UNDEF_")) {
        setError(13);
        return;
    }

    //-----------------------
    // Run FLEXPART
    //-----------------------

    std::string flexpartLogFileName = "log.txt";

    // Run the flexpart script
    std::string cmd = flexpartScript + " \"" + tmpPath + "\" \"" + flexpartLogFileName + "\" \"" + exe + "\" \"" +
                      resourcePath + "\" \"" + speciesPath + "\"";

    // marslog(LOG_INFO,"Execute command: %s",cmd.c_str());
    std::cout << "Execute command: " << cmd << std::endl;

    // Flexpart ouput goes into the stdout and into this file
    std::string flexpartLogFile = tmpPath + "/" + flexpartLogFileName;

    // The maximum number of lines we dump from the end of the log
    int maxLogLineNum = 1000;

    // Run the shell script
    int ret = system(cmd.c_str());

    // Log messages

    // If the script failed read log file and
    // write it into LOG_EROR
    if (ret == -1 || WEXITSTATUS(ret) != 0) {
        if (WEXITSTATUS(ret) == 255) {
            marslog(LOG_WARN, "Warnings generated during FLEXPART run!");
            metview::writeFileToLogWarn(flexpartLogFile, maxLogLineNum);
        }
        else if (WEXITSTATUS(ret) == 1) {
            marslog(LOG_EROR, "Failed to perform FLEXPART run!");
            metview::writeFileToLogErr(flexpartLogFile, maxLogLineNum);
            setError(13);
            return;
        }
        else if (WEXITSTATUS(ret) > 1) {
            marslog(LOG_EROR, "FLEXPART run failed with exit code: %d !", WEXITSTATUS(ret));
            metview::writeFileToLogErr(flexpartLogFile, maxLogLineNum);
            setError(13);
            return;
        }
    }
    else {
        std::string line;
        metview::writeFileToLogInfo(flexpartLogFile, maxLogLineNum);
    }

    //-------------------------------
    // Post process the output
    //-------------------------------

    std::string ppLogFileName = "pp_log.txt";

    std::string forwardStr = "1";
    if (!forward_)
        forwardStr = "0";

    // Run the posproc script
    cmd = ppScript + " \"" + tmpPath + "\" \"" + flexpartLogFileName + "\" \"" + ppLogFileName + "\" \"" + resPath +
          "\" \"" + forwardStr + +"\" \"" + std::to_string(receptorNum_) + "\"";

    // marslog(LOG_INFO,"Execute command: %s",cmd.c_str());
    std::cout << "Execute command: " << cmd << std::endl;

    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 ppLogFile = tmpPath + "/" + ppLogFileName;

        if (WEXITSTATUS(ret) == 255) {
            marslog(LOG_WARN, "Warnings generated during conversion of FLEXPART ouput files!");
            metview::writeFileToLogWarn(ppLogFile, maxLogLineNum);
        }
        else if (WEXITSTATUS(ret) == 1) {
            marslog(LOG_EROR, "Failed to perform conversion of FLEXPART output files!");
            metview::writeFileToLogErr(ppLogFile, maxLogLineNum);
            setError(13);
            return;
        }
        else if (WEXITSTATUS(ret) > 1) {
            marslog(LOG_EROR, "FLEXPART output file conversion failed with exit code: %d !", WEXITSTATUS(ret));
            metview::writeFileToLogErr(ppLogFile, maxLogLineNum);
            setError(13);
            return;
        }
    }

    out = MvRequest("FLEXPART_RESULT");
}


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

    FlexpartRun flexpartRun;

    theApp.run();
}
