/***************************** 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 *************************************/

// BufrPicker.cc, Jul2011/vk, based on ObsFilter by vk & br oct-94, rev vk 970729
//
//  Picks single or vector values from BUFR messages into a geopoints file.
//  'BufrPicker' is targeted for complicated BUFR messages where the requested
//  value(s) occur several times, with different coordinate values.
//
// FAMI20180926, code restructured to call BufrFilter module to do the job.
// This module will read options from the user interface, build a request,
// send it to module BufrFilter and wait for the result.

#include "Metview.h"
#include "MvObs.h"
#include "MvException.h"

//__________________________________________________________ BufrPicker

class BufrPicker : public MvService
{
private:
    bool failOnError_;

public:
    BufrPicker();
    ~BufrPicker() = default;

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

    void serve(MvRequest&, MvRequest&);

protected:
    void getInputParams(MvRequest&, MvRequest&);
};

//_________________________________________________________Constructor
BufrPicker::BufrPicker() :
    MvService("BUFRPICKER"),
    failOnError_(true)
{
}

//_____________________________________________________getInputParams
void BufrPicker::getInputParams(MvRequest& in, MvRequest& out)
{
    // Get data
    out("DATA") = (MvRequest)in.getSubrequest("DATA");

    // Get output format
    const char* outStr = in("OUTPUT");
    out("OUTPUT") = outStr ? outStr : "GEOPOINTS";

    // Get parameter values
    MvObs obs;
    std::string aux;
    if ((const char*)in("PARAMETER"))
        aux = (const char*)in("PARAMETER");

    if (aux.empty())
        throw MvException("No parameter given");

    int nparams = in.countValues("PARAMETER");
    out("PARAMETER_COUNT") = nparams;
    for (int i = 0; i < nparams; i++) {
        // Convert descriptor to key
        std::string spar1 = (const char*)in("PARAMETER", i);
        std::string spar2 = obs.keyC(spar1);

        // Add parameter to the request
        std::ostringstream sparam;
        sparam << "PARAMETER_" << i + 1 << std::ends;
        out.setValue(sparam.str().c_str(), spar2.c_str());
    }

    // Get missing data info
    const char* myMissingData = (const char*)in("MISSING_DATA");
    if (myMissingData && strcmp(myMissingData, "INCLUDE") == 0) {
        out("MISSING_DATA") = "INCLUDE";
        out("MISSING_DATA_VALUE") = (double)in("MISSING_DATA_VALUE");
    }

    // Check coordinate descriptors and coordinate values
    aux.clear();
    if ((const char*)in("COORDINATE_DESCRIPTORS"))
        aux = (const char*)in("COORDINATE_DESCRIPTORS");

    if (aux.empty())
        throw MvException("No coordinate descriptors given");

    int ncoords = in.countValues("COORDINATE_DESCRIPTORS");
    if (in.countValues("COORDINATE_VALUES") != ncoords)
        throw MvException("Different number of coordinate descriptors and values");

    // Get coordinate descriptors and coordinate values
    bool extractAll = false;
    out("COORDINATE_COUNT") = ncoords;
    for (int i = 0; i < ncoords; i++) {
        // Convert descriptor to key
        std::string spar1 = (const char*)in("COORDINATE_DESCRIPTORS", i);
        std::string spar2 = obs.keyC(spar1);

        // Add descriptor to the request
        std::ostringstream sparam;
        sparam << "COORDINATE_" << i + 1 << std::ends;
        out.setValue(sparam.str().c_str(), spar2.c_str());

        // Check for value ALL
        std::string coordValStr = (const char*)in("COORDINATE_VALUES", i);
        if (coordValStr == "ALL")
            extractAll = true;
        else {
            std::ostringstream sparam1;
            sparam1 << "COORDINATE_VALUE_" << i + 1 << std::ends;
            out(sparam1.str().c_str()) = (const char*)in("COORDINATE_VALUES", i);
        }
    }
    out("EXTRACT_MODE") = extractAll ? "ALL" : "FIRST";

    // Get tag informing how to proceed if an error occurs
    const char* fail = in("FAIL_ON_ERROR");
    if (fail && strcmp(fail, "NO") == 0)
        failOnError_ = false;
}

//_________________________________________________________serve
void BufrPicker::serve(MvRequest& in, MvRequest& out)
{
    // in.print();
    try {
        // Get user input parameters
        MvRequest bufrIn("BUFRFILTER");
        MvRequest filterIn = bufrIn.ExpandRequest("BufrFilterDef", "BufrFilterRules", EXPAND_DEFAULTS);
        getInputParams(in, filterIn);

        callService((char*)"BufrFilter", filterIn, out);
    }
    catch (MvException& e) {
        int errcode = failOnError_ ? 1 : 0;
        setError(errcode, "BufrPicker-> %s", e.what());
    }

    // out.print();
    return;
}

//_________________________________________________________main
int main(int argc, char** argv)
{
    MvApplication theApp(argc, argv);
    BufrPicker picker;

    theApp.run();
}
