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

#pragma once

/********************************************************************************
  Application for Xsection, Average and Vertical Profile.
  It takes a GRIB file as input (in addition to the values given by the user),
  and produces an NetCDF file as output. There is no metadata on output request,
  only the path to NetCDF file.

  First the GRIB while is read, and info about all parameters and level are stored.
  Then several flags is set, based on the data and the input request, to determine
  how the application should run.

  The file is rewound, and sorted by param, date, time, step and expver. All fields
  with the same values for the sorting parameters are one plot. If any of these
  values change, all data are written out as netcdf variables with names put
  together from the sorting values.
********************************************************************************/

#include "MvXsectFrame.h"

class Xsect : public MvService
{
public:
    // Entry point routine
    void serve(MvRequest&, MvRequest&) override;

    // Process grib data and build all data structures
    bool processData(MvRequest&, ApplicationInfo&, MvRequest&);

    bool getInputParameters(MvRequest&, MvRequest&, ApplicationInfo&);

    void generateKey(std::string&, int, int, int, int, const char*);

    // Get the application (original) view request
    MvRequest getAppView(MvRequest&);

    // Consistency check
    bool consistencyCheck(ParamMap&, ApplicationInfo&);

    // Initialise/create netCDF file
    bool initialiseNetcdf(MvNetCDF&, ParamMap&, ApplicationInfo&, int&, MvField&, double*);

    // Write level info to the Netcdf structure
    virtual bool writeLevelInfoNetcdf(MvNetCDF&, ApplicationInfo&, ParamMap&, MvField* = nullptr, double* = nullptr)
    {
        return true;
    }

    // Write time info to the Netcdf file
    bool writeTimeInfoNetcdf(MvNetCDF&, ParamMap&, int&);

    // Write geographical coordinates info to the Netcdf file
    virtual bool writeGeoCoordsNetcdf(MvNetCDF&, ApplicationInfo&);

    // Write global attributes info to the Netcdf file
    virtual bool writeGlobalAttributesNetcdf(MvNetCDF&, ParamMap&, ApplicationInfo&);

    // Write extra variables to the Netcdf file
    virtual bool generateExtraData(MvNetCDF&, ApplicationInfo&, ParamMap&, MvFieldSetIterator&) { return true; }

    // Check if action is visualisation related
    bool isVisualise(ApplicationInfo&);

    // Write the data to the netcdf file
    virtual bool generateData(ApplicationInfo&, ParamMap&, MvNetCDF&, MvField&, const std::string&) = 0;

    // Get title related to a variable
    virtual std::string titleVariable(ApplicationInfo&, ParamInfo*) = 0;

    // Compute data values
    virtual bool fillValues(ApplicationInfo&, MvField&, double*) = 0;

    // Create output request using information from the application
    virtual MvRequest createOutputRequest(ApplicationInfo&, ParamInfo*) = 0;

    // Create output request using information from the request
    virtual MvRequest createOutputRequest(MvRequest&);

    // Compute number and geographical points
    virtual int computeGeographicalPoints(ApplicationInfo&, MvField* = nullptr) = 0;

protected:
    // Constructor
    Xsect(const char* kw);

    // Destructor
    ~Xsect() override = default;

    virtual void init();
    void getInitialFieldInfo(MvField&, ApplicationInfo&, double&, double&, double&, double&);
    bool updateTopBottomLevels(ApplicationInfo&, ParamMap&, MvFieldSetIterator&, double&, double&, int&);
    bool checkCoordinates(double&, double&, double&, double&);

    // Check if parameters between two requests are consistent
    virtual bool consistencyCheck(MvRequest&, MvRequest&) = 0;

    bool contourValues(ApplicationInfo&, ParamMap&, MvNetCDF&, MvField&, std::string);

    // Get application specific parameters
    virtual bool getAppParameters(MvRequest&, ApplicationInfo&) = 0;

    // Set flags
    virtual void setHorCompFlags(bool, bool, bool, bool) {}

    // Check that this field's vital statistics match those of the others
    // - this is important only in the Average application
    virtual bool fieldConsistencyCheck(MvField&, double, double, double, double) { return true; }

    // Check this field's geographical grid point positions against the user input
    // grid point postion. This is important only in the Vertical Profile application
    virtual bool fieldConsistencyCheck(MvField&, ApplicationInfo&) { return true; }

    // Get default variable to be plotted
    virtual ParamInfo* plotVariable(ApplicationInfo&, ParamMap&);

    // Get netCDF variable name
    virtual std::string getNetcdfVarname(const std::string&);

    // Get netCDF level variable name
    virtual std::string getNetcdfLevelVarname(std::string& varname)
    {
        return varname + "_lev";
    }

    // Get netCDF Value and Components variable names
    virtual std::string getNetcdfValueVarname() { return "value"; }
    virtual std::string getNetcdfXComponentVarname() { return "x_component"; }
    virtual std::string getNetcdfYComponentVarname() { return "y_component"; }

    // Get/create a netCDF variable
    virtual MvNcVar* getNetcdfVariable(MvNetCDF&, const std::string& v, ApplicationInfo&, const std::string&, ParamInfo*);
    virtual MvNcVar* getNetcdfVariableLevel(MvNetCDF&, const std::string& v, ApplicationInfo&);

    // Check if data module was dropped
    bool isDataModuleDropped(const char*);

    // Free memory
    void deleteAuxiliaryMemory(double*, ParamMap&);

    // Add information to help the Macro Converter to translate
    // the output request to a Macro code
    MvRequest buildMacroConverterRequest();


    // get and set geoIndexVar_
    void setGeoIndexVar(const std::string& var) { geoIndexVar_ = var; }
    std::string getGeoIndexVar() { return geoIndexVar_; }


    // Variables
    MvRequest origReq_;        // input request
    std::string type_;         // application type: MVPROFILE, MXSECTION, MXAVERAGE
    std::string view_;         // application view: MVPROFILEVIEW, MXSECTIONVIEW, MXAVERAGEVIEW
    std::string ncName_;       // netCDF file name
    int ntime_;                // time counter. It assumes that the input data is sorted
                               // by variable and time, e.g. v1t1, v1t2, .. vntm, v2t1, ...
    std::string geoIndexVar_;  // the last dimension in the 'value' variable (lat/lon)

    std::string moduleLabel_{"Xsect-> "};
};
