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

// grib_to_geo.cc,  020722/vk
//
//   Dumps all grid point values into a geopoints file.
//
//   User can select either the minimum XYV format
//   (lon-lat-value, the default) or the traditional
//   full format (lat-lon-height-date-time-value).
//

#include <fstream>
#include <iostream>
#include <string>
#include "fstream_mars_fix.h"

#include "Metview.h"
#include "MvGeoPoints.h"

//_________________________________________________________________

class GribToGeo : public MvService
{
public:
    GribToGeo() :
        MvService("GRIB_TO_GEO") {}
    void serve(MvRequest&, MvRequest&);
};
//_________________________________________________________________

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

    const char cTAB = '\t';

    MvRequest grb;  //-- data request => fieldset
    in.getValue(grb, "DATA");

    fieldset* fs = request_to_fieldset((request*)grb);
    field* f = get_field(fs, 0, expand_mem);
    // gribsec1* s1 = (gribsec1*)&f->ksec1[0];

    MvField field(f);                  // create MvField object
    MvGridBase* grd = field.mvGrid();  // be careful with this pointer because MvField is in charge!

    if (!grd->hasIterator())  //-- check that it is ok
    {
        setError(1, "GribToGeo-> Unimplemented or spectral data - unable to extract location data");
        return;
    }


    if (fs->count > 1)  //-- warning if several fields
    {
        setError(0, "GribToGeo-> Info: only first field will be converted");
    }


    // double height = grd->getDouble("heightPressureEtcOfLevels");//aki always // s1->top_level;
    double height = field.level();
    MvDate vDate(field.yyyymmddFoh());


    // long timePeriod = grd->getLong("periodOfTimeIntervals");//aki grib2//
    long step = field.stepFoh();
    if (step != LONG_MAX && step != 0)  // s1->p1 > 0 )
    {
        vDate += step;
    }
    long dat = vDate.YyyyMmDd();
    long tim = 100 * vDate.Hour() + vDate.Minute();
    // std::cout << height << cTAB << dat << cTAB << tim << cTAB << step << std::endl;


    bool formatXYV = true;  //-- which geopoints file format ?
    const char* fmt = in("GEOPOINTS_FORMAT");
    if (fmt && strcmp(fmt, "TRADITIONAL") == 0) {
        formatXYV = false;
    }


    bool includeMissing = true;  //-- what to do with missing values ?
    const char* mis = in("MISSING_DATA");
    if (mis && strcmp(mis, "IGNORE") == 0) {
        includeMissing = false;
    }


    std::string tmp = marstmp();  //-- open output file
    std::ofstream fout(tmp.c_str());

    // long paramNumber   = grd->getLong("indicatorOfParameter");//aki grib2//
    // long table2Version = grd->getLong("gribTablesVersionNo");//aki grib2//
    std::string paramNumber = grd->getString("mars.param");

    fout << "#GEO\n"  //-- write geopoints header, with a comment
         << "#  extracted from GRIB data, on " << MvDate().YyyyMmDd() << ", by grib_to_geo\n"
         << "#  original parameter " << paramNumber
         //           << ", in GRIB Table 2 Version " << table2Version
         << std::endl;

    if (formatXYV)  //-- add more comment lines
    {
        fout << "#FORMAT XYV\n"  //-- geopoints file format is "XYV"
             << "#  warning: XYV is not suitable for some macro computations!\n"
             << "#  X=lon Y=lat\tV=val" << std::endl;
    }
    else {
        fout << "# lat\tlon\theight\tdate\ttime\tvalue" << std::endl;
    }

    fout << "#DATA" << std::endl;  //-- start-of-data line


    MvGridPoint gp;  //-- extract all grid points
    int cnt = 0;

    do {
        gp = grd->gridPoint();
        bool valueIsMissing = (gp.value_ == grd->missingValue());
        if (includeMissing || !valueIsMissing) {
            ++cnt;
            if (formatXYV)  //-- XYV format
            {
                fout << gp.loc_.longitude() << cTAB
                     << gp.loc_.latitude() << cTAB;
            }
            else  //-- traditional format
            {
                fout << gp.loc_.latitude() << cTAB
                     << gp.loc_.longitude() << cTAB
                     << height << cTAB
                     << dat << cTAB
                     << tim << cTAB;
            }

            if (!valueIsMissing)
                fout << gp.value_ << std::endl;
            else
                fout << GEOPOINTS_MISSING_VALUE << std::endl;
        }
    } while (grd->advance());

    MvRequest gpts = "GEOPOINTS";  //-- return reply request
    gpts("PATH") = tmp.c_str();
    gpts("TEMPORARY") = 1;

    out = gpts;

    std::cout << "GribToGeo::serve() out" << std::endl;
    out.print();
}
//_________________________________________________________________

int main(int argc, char** argv)
{
    MvApplication theApp(argc, argv);
    GribToGeo tool;
    theApp.run();
}
