/***************************** 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 "mars.h"

#include <algorithm>

#include "Metview.h"
#include "MvObs.h"
#include "MvException.h"
#include "MvKeyProfile.h"
#include "MvMiscellaneous.h"
#include "MvEccBufr.h"

#include "BufrFilterDef.h"
#include "BufrFilterEngine.h"

class BufrFilter : public MvService
{
public:
    BufrFilter();
    ~BufrFilter() = default;

    BufrFilter(const BufrFilter&) = delete;
    void operator=(const BufrFilter&) = delete;

    void serve(MvRequest&, MvRequest&);

private:
    void getParam(MvRequest& in, const std::string& param);
    void getParamVec(MvRequest& in, const std::string& param, const std::string& separator = "/");

    BufrFilterDef def_;
};

BufrFilter::BufrFilter() :
    MvService("BUFRFILTER")
{
}

#if 0
void BufrFilter::getData(MvRequest &in)
{
   //-- D A T A --
   in.getValue( data_, "DATA" );


   //-- O U T P U T --
   const char* outStr = in_( "OUTPUT" );
   if ( !outStr )
      geoVectorType_ = 0;
   else if( std::string(outStr) == geoVectorName_[1] )
      geoVectorType_ = 1;
   else if( std::string(outStr) == geoVectorName_[2] )
      geoVectorType_ = 2;

  const char* fail = in_("FAIL_ON_ERROR");
  if( fail && strcmp( fail, "NO" ) == 0 )
     failOnError_ = false;
}
#endif


void BufrFilter::getParam(MvRequest& in, const std::string& param)
{
    if (const char* val = in(param.c_str())) {
        def_.add(param, std::string(val));
    }
}

void BufrFilter::getParamVec(MvRequest& in, const std::string& param, const std::string& separator)
{
    std::vector<std::string> vals;
    if (in.getValue(param, vals)) {
        def_.add(param, metview::merge(vals, separator));
    }
}

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

    getParam(in, "OUTPUT");
    std::string outFormat = def_.value("OUTPUT");

    std::string bufrFile;
    MvRequest dataReq = in("DATA");
    if (const char* fNameCh = dataReq("PATH")) {
        bufrFile = std::string(fNameCh);
    }

    if (bufrFile.empty()) {
        sendProgress("BufrFilter-> Empty bufr file");
        return;
    }

    if (outFormat == "BUFR") {
        def_.add("EXTRACT", "OFF");
    }
    else {
        def_.add("EXTRACT", "ON");
    }

    getParam(in, "CUSTOM_COUNT");

    getParamVec(in, "COLUMNS", ":");

    getParam(in, "PARAMETER_COUNT");
    std::string cntVal = def_.value("PARAMETER_COUNT");
    if (!cntVal.empty()) {
        int cnt = metview::fromString<int>(cntVal);
        for (int i = 0; i < cnt; i++) {
            std::string numStr = std::to_string(i + 1);
            getParam(in, "PARAMETER_" + numStr);
            getParam(in, "PARAMETER_RANK_" + numStr);
            getParam(in, "PARAMETER_OPERATOR_" + numStr);
            getParamVec(in, "PARAMETER_VALUE_" + numStr);
        }
    }

    getParam(in, "COORDINATE_COUNT");
    cntVal = def_.value("COORDINATE_COUNT");
    if (!cntVal.empty()) {
        int cnt = metview::fromString<int>(cntVal);
        for (int i = 0; i < cnt; i++) {
            std::string numStr = std::to_string(i + 1);
            getParam(in, "COORDINATE_" + numStr);
            getParamVec(in, "COORDINATE_RANK_" + numStr);
            getParam(in, "COORDINATE_OPERATOR_" + numStr);
            getParamVec(in, "COORDINATE_VALUE_" + numStr);
        }
    }

    getParam(in, "EXTRACT_MODE");
    getParam(in, "EXTRACT_COORDINATE");
    getParam(in, "EXTRACT_DATE");
    getParam(in, "MISSING_DATA");
    getParam(in, "MISSING_ELEMENT");

    getParamVec(in, "MESSAGE_INDEX");
    getParamVec(in, "EDITION");
    getParamVec(in, "CENTRE");
    getParamVec(in, "SUBCENTRE");
    getParamVec(in, "MASTERTABLE");
    getParamVec(in, "LOCALTABLE");
    getParamVec(in, "DATA_TYPE");
    getParamVec(in, "DATA_SUBTYPE");
    getParamVec(in, "RDB_TYPE");

    getParam(in, "DATEMODE");
    getParam(in, "DATE");
    getParam(in, "TIME");
    getParam(in, "WINDOW_IN_MINUTES");
    getParam(in, "DATE_1");
    getParam(in, "TIME_1");
    getParam(in, "DATE_2");
    getParam(in, "TIME_2");

    getParamVec(in, "AREA");

    getParam(in, "IDENT_KEY");
    getParamVec(in, "IDENT_VALUE");

    getParam(in, "CUSTOM_COUNT");
    cntVal = def_.value("CUSTOM_COUNT");
    if (!cntVal.empty()) {
        int cnt = metview::fromString<int>(cntVal);
        for (int i = 0; i < cnt; i++) {
            std::string numStr = std::to_string(i + 1);
            getParam(in, "CUSTOM_KEY_" + numStr);
            getParam(in, "CUSTOM_RANK_" + numStr);
            getParam(in, "CUSTOM_OPERATOR_" + numStr);
            getParamVec(in, "CUSTOM_VALUE_" + numStr);
        }
    }

    BufrFilterEngine filter(bufrFile, BufrFilterEngine::IconMode, 0);

    //The object to store the extracted values
    auto* extractedProf = new MvKeyProfile("result");

    //The output file
    std::string resFile(marstmp());
    MvRequest x;

    if (outFormat == "CSV") {
        x = MvRequest("TABLE");  //CSV represented by TABLE in metview
    }
    else if (outFormat == "BUFR") {
        x = MvRequest("BUFR");
    }
    else {
        x = MvRequest("GEOPOINTS");
    }
    x("TEMPORARY") = 1;
    x("PATH")      = resFile.c_str();

#if 0
    outfile_.open( fileName, std::ios::out );if(def_.value("OUTPUT") == "CSV")

    outfile_ << "#GEO" << std::endl;

    if( geoVectorType_ > 0 )
       outfile_ << "#FORMAT " << geoVectorName_[geoVectorType_] << std::endl;

    if ( geoVectorType_ > 0 )
       outfile_ << "PARAMETER = " << param_ << mySeparator << param2_ << std::endl;
    else
       outfile_ << "PARAMETER = " << param_ << std::endl;

    outfile_ << "#lat" << mySeparator << "long" << mySeparator
             << "level" << mySeparator << "date" << mySeparator
             << "time" << mySeparator;
#endif

    //the USE_PREFILTER option is only used for test purposes at the moment
    MvEccBufr* bd = nullptr;
    if (const char* val = in("USE_PREFILTER")) {
        if (strcmp(val, "1") == 0) {
            bd = new MvEccBufr(bufrFile);
            bd->scan();
        }
    }

    try {
        if (bd) {
            filter.runWithBufrData(def_, resFile, extractedProf, -1, bd);
        }
        else {
            std::vector<MvEccBufrMessage*> bdVec;
            filter.run(def_, resFile, extractedProf, -1, bdVec);
        }

        if (outFormat == "CSV") {
            filter.toCsv(resFile);
        }
        else if (outFormat != "BUFR") {
            filter.toGeopoints(resFile, def_.value("OUTPUT"));
        }
    }
    catch (MvException& e) {
        setError(1, "BufrFilter-> %s", e.what());
        return;
    }

    out = x;

    std::cout << "BufrFilter::serve() out" << std::endl;
    out.print();
}

int main(int argc, char** argv)
{
    MvApplication theApp(argc, argv);
    BufrFilter bf;

    theApp.run();
}
