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

#include <iostream>


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

    // Get data
    if (!GetData(in))
        return;

    // Compute output field
    if (!Apply(out))
        return;

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

int Divrot ::GetField(MvRequest& grib, int param, MvFieldSet& fs)
{
    MvFilter filter("PARAM");         // filter
    MvFieldSet fset(grib);            // field set
    MvFieldSetIterator fsiter(fset);  // field set iterator
    MvField field;                    // auxiliary field
    int nfields;                      // number of fields

    // Get fields
    nfields = 0;
    fsiter.setFilter(filter == param);
    while ((field = fsiter())) {
        nfields++;
        fs += field;
    }

    return nfields;
}

bool Divrot ::GetDataGen(MvRequest& in)
{
    if ((const char*)in("DIVERGENCE_PARAM")) {
        divParamId_ = (int)in("DIVERGENCE_PARAM");
    }

    if ((const char*)in("VORTICITY_PARAM")) {
        vorParamId_ = (int)in("VORTICITY_PARAM");
    }

    // Get spectral truncation, smoothing indicator and constants
    if (strcmp(in("TRUNCATION"), "AV") == 0)
        DtruncOut = 0.;
    else
        DtruncOut = (double)in("TRUNCATION");

    strcpy(Dsmoothing, in("SMOOTHING"));

    if ((const char*)in("FLTC"))
        Dfltc = (double)in("FLTC");
    else
        Dfltc = 19.4;

    if ((const char*)in("MFLTEXP"))
        Dmfltexp = (double)in("MFLTEXP");
    else
        Dmfltexp = 2.;

    return true;
}

bool Divrot::Apply(MvRequest& out)
{
    return convert_DIVROT(out);
}

bool Divrot::appendWindComponent(grib_handle* gh)
{
    int ret;
    if ((ret = grib_set_long(gh, "generatingProcessIdentifier", 254))) {
        setError(1, "Error on GRIB decoding: grib_set_long/generatingProcessIdentifier returned %d in Divrot::appendWindComponent", ret);
        return false;
    }

    if (Dsmoothing[0] == 'Y')  //-- is smoothing requested?
        smooth_BPPSMTH(gh);

    size_t buflen = 0;  //-- get the GRIB msg into 'buffer'
    const void* buffer = nullptr;
    if ((ret = grib_get_message(gh, &buffer, &buflen))) {
        setError(1, "Error on GRIB decoding: grib_get_message returned %d in Divrot::appendWindComponent", ret);
        return false;
    }
    //-- open output file in append mode
    FILE* gribFile = fopen(DfileOut.c_str(), "a");
    if (!gribFile) {
        setError(1, "Cannot open %s", DfileOut.c_str());
        return false;
    }
    //-- append 'buffer' to output file
    if (fwrite(buffer, 1, buflen, gribFile) != buflen) {
        setError(1, "fwrite(%s) failed", DfileOut.c_str());
        fclose(gribFile);
        return false;
    }
    //-- close and double check
    if (fclose(gribFile)) {
        setError(1, "fclose(%s) failed", DfileOut.c_str());
        return false;
    }

    return true;
}


bool Rotwind ::GetData(MvRequest& in)
{
    MvRequest grib;  // grib file

    DresultParam = eRotational;

    // get general data
    if (!GetDataGen(in))
        return false;

    // Get data from request
    in.getValue(grib, "DATA");

    // Get vorticity fields
    Dnfield = GetField(grib, vorParamId_, Dfieldset);
    if (Dnfield == 0) {
        setError(1, "Field is not Vorticity..");
        return false;
    }

    return true;
}

bool Divwind ::GetData(MvRequest& in)
{
    MvRequest grib;  // grib file

    DresultParam = eDivergent;

    // get general data
    if (!GetDataGen(in))
        return false;

    // Get data from request
    in.getValue(grib, "DATA");

    // Get divergence fields
    Dnfield = GetField(grib, divParamId_, Dfieldset);
    if (Dnfield == 0) {
        setError(1, "Field is not Divergence..");
        return false;
    }

    return true;
}

bool UVwind ::GetData(MvRequest& in)
{
    MvRequest grib;  // grib data

    DresultParam = eUV;

    // get general data
    if (!GetDataGen(in))
        return false;

    // Get data from request
    in.getValue(grib, "DATA");

    // Get vorticity fields
    Dnfield = GetField(grib, vorParamId_, Dfieldset);
    if (Dnfield == 0) {
        setError(1, "No vorticity fields in data.");
        return false;
    }

    // Get divergence fields
    Dnfield2 = GetField(grib, divParamId_, Dfieldset2);
    if (Dnfield2 == 0) {
        setError(1, "No divergence fields in data.");
        return false;
    }

    // check that we have the same number of DIV and VOR fields
    if (Dnfield != Dnfield2) {
        setError(1, "Should be the same number of vorticity and divergence fields - there are %d and %d.", Dnfield, Dnfield2);
        return false;
    }


    return true;
}

#if 0
bool
Divrot::check_ecCodes( int ret, const char* gribApiFunc, const char* MvFunc )
{
   if( ret == 0 )
      return true;

   // ecCodes returned an error message
   setError(1,"Error on GRIB decoding: %s returned %d in %s", gribApiFunc, ret, MvFunc);
   return false;
}
#endif

int main(int argc, char** argv)
{
    // From the environment, check whether we're in 'standard' or 'MIR' mode
    char* marsmode = getenv("MV_MARS_MODE");
    const char* servicename = "Divrot";

    if (marsmode && strcmp(marsmode, "MIR") == 0) {
        servicename = "DivrotMir";
    }
    else if (marsmode && strcmp(marsmode, "EMOS") == 0) {
        servicename = "DivrotEmos";
    }

    MvApplication theApp(argc, argv, servicename);
    Rotwind rotw("ROTWIND");
    Divwind divw("DIVWIND");
    UVwind uvw("UVWIND");
    Rotwind rotw_mir("ROTWIND_MIR");
    Divwind divw_mir("DIVWIND_MIR");
    UVwind uvw_mir("UVWIND_MIR");
    Rotwind rotw_emos("ROTWIND_EMOS");
    Divwind divw_emos("DIVWIND_EMOS");
    UVwind uvw_emos("UVWIND_EMOS");

    theApp.run();
}
