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

class MvRequest;

#include <mars.h>
#include <Cached.h>
#include <MvDate.h>
#include <map>
#include <vector>

//! Class to help to access and convert values from/to a request
/*! Metview provides classes MvRequest, MvAccess and MvConstAccess
 *  that deal with the request structure.
 *
 *  MvAccess is transparent to the application programmer.
 *  It is an implicit class that helps the extraction of
 *  parameter values from a request and their assignment to C++
 *  basic types (char, int, double).
 *  It also helps the assignment of values to a parameter.\n
 *  Examples:
 * <PRE>
 *    int getStep( MvRequest& r )
 *    {
 *       int step = r("STEP"); //-- MvAccess is used here implicitly
 *       return step;
 *    }
 *
 *    void putStep( MvRequest& r, int s )
 *    {
 *       r("STEP") = s; //-- MvAccess is used here implicitly
 *    }
 * </PRE>
 *  Use a debugger to step into the commented lines, to see how
 *  MvAccess object is created and used in getting/setting the
 *  parameter value.
 */

class MvAccess
{
    friend class MvRequest;
    friend class MvConstAccess;

    MvRequest* Request;
    const char* LastGet;
    int LastIndex;

public:
    //! Constructor (used by non-const MvRequest objects)
    MvAccess(MvRequest* r, const char* get, int idx) :
        Request(r),
        LastGet(get),
        LastIndex(idx) {}

    MvAccess(const MvAccess&);
    void operator=(const MvAccess&);

    void operator=(const char* val);
    operator const char*() const;

    void operator=(double val);
    operator double() const;

    void operator=(int val);
    operator int() const;

    void operator=(long val);
    operator long() const;

    void operator+=(const char* val);
    void operator+=(double val);
    void operator+=(const MvDate& val);

    operator request*() const;
    void operator=(const request*);
    void operator+=(const request*);

    void operator+=(const MvAccess&);

    operator Cached() const;

    operator MvRequest() const;

    void operator=(const MvDate&);
    operator MvDate() const;
};

//! Class to help to extract values from a request
/*! Metview provides classes MvRequest, MvAccess and MvConstAccess
 *  that deal with the request structure.
 *
 *  MvConstAccess - like MvAccess - is an implicit class that
 *  helps the extraction of parameter values from a request and
 *  their assignment to C++ basic types (char, int, double).
 *  This class is used (implicitly) when the related MvRequest
 *  object is const.\n \n
 *  Example:
 * <PRE>
 *    int getStep( const MvRequest& r )
 *    {
 *       int step = r("STEP"); //-- MvConstAccess is used here implicitly
 *       return step;
 *    }
 * </PRE>
 *  Use a debugger to step into the commented line, to see how
 *  MvConstAccess object is created and used in getting the
 *  parameter value and casting it into int.
 */

class MvConstAccess
{
    friend class MvRequest;

    const MvRequest* Request;
    const char* LastGet;
    int LastIndex;

public:
    //! Constructor (used by const MvRequest objects)
    MvConstAccess(const MvRequest* r, const char* get, int idx) :
        Request(r),
        LastGet(get),
        LastIndex(idx) {}

    MvConstAccess(const MvConstAccess&);
    MvConstAccess(const MvAccess&);

    operator const char*() const;
    operator double() const;
    operator int() const;
    operator long() const;
    operator const request*() const;

    operator Cached() const;
    operator MvRequest() const;

    operator MvDate() const;
};


//! \brief Class to handle the request structure
//!
//! MvRequest is a C++ wrapper around libMars structure 'request'
//! and libMars C functions dealing with this structure.
//! (Structure 'request' is defined in file src/libMars/rpcmars.h
//! and most libMars request functions in files src/libMars/request.c
//! and src/libMars/expand.c)
//!
//! Metview provides classes MvRequest, MvAccess and MvConstAccess
//! that deal with the request structure. MvRequest is the explicit
//! class used by Metview application programmers, whereas MvAccess
//! and MvConstAccess are used implicitly "behind the scene".
//!
//! It is important to remember that a request may have subrequests
//! as a parameter and requests may be concatenated in a single linked list.

class MvRequest
{
    friend class MvAccess;
    friend class MvConstAccess;

private:
    void _validate();
    int _overwrite;
    boolean _free_req_on_destruct;

protected:
    request* FirstRequest;
    request* CurrentRequest;
    value* CurrentValue;
    int CurrentCount;
    parameter* CurrentParam;

    // This function is similar to put_value (libMars), but it only
    // updates values that are common to both input and output requests
    void update_value(request* r, const char* param, const char* valname, boolean append);
    request* copyFromCurrentTo_Recursive(const request* r, const char* verb);

    // Expand subrequests
    void importSubObjects(std::string&, request*);

public:
    // Contructors

    //! Constructor to create an empty request
    /*! The command (request) name is taken from argument 'verb'.
     */
    MvRequest(const char* verb);

    //! Creates a MvRequest from libMars request structure 'req'
    /*! If argument 'clone' is true, a copy of 'req' is done,
     *  otherwise a pointer to 'req' itself is stored in MvRequest.
     */
    MvRequest(request* req = nullptr, boolean clone = true, boolean free_req = true);

    //! Copy constructor
    MvRequest(const MvRequest&);

    virtual ~MvRequest();

    // Assignement

    //! Assignment operator
    MvRequest& operator=(const MvRequest& r);
    // MvRequest& operator = (const request*);

    void copyFromCurrent(const MvRequest&);
    void copyFromCurrentTo(const MvRequest&, const char* verb);

    //! Concatenation operator
    /*! The operator+ concatenates two MvRequest and returns the resulting
     *  MvRequest.\n \n
     *  Example:
     * <PRE>
     *    MvRequest r1("FIRST"), r2("SECOND"), r3;
     *    r3 = r1 + r2;   //-- r3 contains FIRST concatenated to SECOND
     * </PRE>
     */
    MvRequest operator+(const MvRequest& r) const;

    //! Concatenates an MvRequest to an existing MvRequest, e.g. a += b;
    void operator+=(const MvRequest& r);

    //! Returns a pointer to the internal libMars request structure
    operator request*() const { return CurrentRequest; }

    //! Returns 1 (true) when MvRequest object contains no requests
    int operator!() const { return CurrentRequest == nullptr; }

    //! Access the 'n'th value in list parameter 'p'
    /*! Returns a MvAccess object, which normally is not visible
     *  in user code (like in the example below).\n \n
     *  NOTE 1: Method uses C style indexing (n=0,1,...).\n
     *  NOTE 2: To access long lists of values, it is better to use
     *  methods 'iterInit' and 'iterGetNextValue'.\n \n
     *  Example:
     * <PRE>
     *    MvRequest r1("RETRIEVE"); //-- RETRIEVE
     *    r1("LEVELIST") = 500;     //-- RETRIEVE,LEVELIST=500
     *    r1("LEVELIST",1) = 750;   //-- RETRIEVE,LEVELIST=500/750
     *    //...
     *    const char* lev = r1("LEVELIST",1);//-- lev points to "750"
     * </PRE>
     *  Note that the example above could have been written also like this:
     * <PRE>
     *    MvRequest r1("RETRIEVE");     //-- RETRIEVE
     *    r1.setValue("LEVELIST",500);  //-- RETRIEVE,LEVELIST=500
     *    r1.addValue("LEVELIST",750);  //-- RETRIEVE,LEVELIST=500/750
     *    //...
     *    const char* lev;
     *    r1.getValue(lev,"LEVELIST",1);//-- lev points to "750"
     * </PRE>
     */
    MvAccess operator()(const char* p, int n) { return MvAccess(this, p, n); }

    //! Read the (first) value of parameter 'p'
    MvAccess operator()(const char* p) { return MvAccess(this, p, 0); }

    //! Read the 'n'th value from list parameter 'p'
    /*! Returns a MvConstAccess object, which normally is not visible.\n \n
     *  NOTE 1: Method uses C style indexing (n=0,1,...).\n
     *  NOTE 2: To access long lists of values, it is better to use
     *  methods 'iterInit' and 'iterGetNextValue'.
     */
    MvConstAccess operator()(const char* p, int n) const
    {
        return MvConstAccess(this, p, n);
    }

    //! Read the (first) value of parameter 'p'
    MvConstAccess operator()(const char* p) const
    {
        return MvConstAccess(this, p, 0);
    }

    // -----

    //! Reads the 'n'th value from parameter 'p' into int variable 'v'
    /*! NOTE: Method uses C style indexing (n=0,1,...) */
    void getValue(int& v, const char* p, int n = 0) const;

    //! Reads the 'n'th value from parameter 'p' into double variable 'v'
    /*! NOTE: Method uses C style indexing (n=0,1,...) */
    void getValue(double& v, const char* p, int n = 0) const;

    //! Reads the 'n'th value from parameter 'p' into const char* variable 'v'
    /*! NOTE: Method uses C style indexing (n=0,1,...) */
    void getValue(const char*& v, const char* p, int n = 0) const;

    //! Reads the 'n'th value from parameter 'p' into Cached variable 'v'
    /*! NOTE: Method uses C style indexing (n=0,1,...) */
    void getValue(Cached& v, const char* p, int n = 0) const;

    //! Reads the 'n'th value from parameter 'p' into void* variable 'v'
    /*! NOTE: Method uses C style indexing (n=0,1,...) */
    void getValue(void*& v, const char* p, int n = 0) const;

    //! Reads the 'n'th value from parameter 'p' into MvRequest variable 'v'
    /*! NOTE: Method uses C style indexing (n=0,1,...) */
    void getValue(MvRequest& v, const char* p, int n = 0) const;


    //! Extracts the 'n'th subrequest from parameter 'p'
    /*! NOTE: Method uses C style indexing (n=0,1,...) */
    MvRequest getSubrequest(const char* p, int n = 0) const;

    //! Return the name of the command/request
    const char* getVerb() const;

    //! Initialise the list iterator for a list valued parameter
    /*! Using method 'operator()(const char* p,int n)' to access
     *  list valued parameters where the list contains thousands
     *  of values, gets very slow. The required time with this
     *  method grows exponentially, relative to the lenght of
     *  the list.
     *
     *  Methods 'iterInit' and 'iterGetNextValue' are provided
     *  to speed up accessing such long list valued parameters.
     *  The required time with this iterator method grows
     *  linearly, relative to the length of the list.
     *
     *  Use method 'iterInit' to tell the list iterator which parameter
     *  contains the list to be iterated, and to get the length of
     *  the list. Then use 'iterGetNextValue' to read each value,
     *  one by one.
     *
     *  Example on how to read curve X and Y values from MvRequest
     *  'req' into double arrays x[] and y[]:
     * <PRE>
     *    int cnt = req.iterInit( "X_VALUES" );
     *    for( int i=0; i<cnt; ++i )
     *       req.iterGetNextValue( x[i] );
     *
     *    req.iterInit( "Y_VALUES" ); //-- assume same number of values
     *    for( int i=0; i<cnt; ++i )
     *       req.iterGetNextValue( y[i] );
     * </PRE>
     *  The following code will do the same, but here the required
     *  computing resources grow exponentially:
     * <PRE>
     *    int cnt = req.countValues( "X_VALUES" ); //-- assume ok also for Y
     *    for( int i=0; i<cnt; ++i )
     *    {
     *       x[i] = req( "X_VALUES", i );
     *       y[i] = req( "Y_VALUES", i );
     *    }
     * </PRE>
     */
    int iterInit(const char* param);  //-- returns 'param' count

    //! Get current value as 'char*' and advance the list iterator
    /*! If no more values are available in the list, returns
     *  'false' and sets argument 'val' to 0 (nullptr);
     *  otherwise returns 'true' and returns the current list
     *  item in argument 'val', and then advances the list iterator.
     */
    bool iterGetNextValue(const char*& val);

    //! Get current value as 'double' and advance the list iterator
    /*! If no more values are available in the list, returns
     *  'false' and sets argument 'val' to 0;
     *  otherwise returns 'true' and returns the current list
     *  item in argument 'val', and then advances the list iterator.
     */
    bool iterGetNextValue(double& val);

    // -----
    //! Advance to the next subrequest
    /*! When a MvRequest object contains more than one request in a single
     *  linked list, this method allows the sequential access to all of them.
     *  Each time this method is called, it advances the current request
     *  to the next one in the list and returns it. nullptr is returned when
     *  the current request points to the end of the list.\n \n
     *  Example:
     * <PRE>
     *    MvRequest r1("FIRST"), r2("SECOND"), r3, r4;
     *    r3 = r1 + r2;      // r3 contains FIRST concatenated to SECOND
     *    r4 = r3.advance(); // r4 contain only SECOND request.
     * </PRE>
     */
    MvRequest& advance();

    // Advance to the next verb request
    MvRequest& advanceTo(std::string&);

    // Advance to after the next verb request
    MvRequest& advanceAfter(std::string&);

    // Advance to the end of all requests
    void advanceToEnd();

    //! Rewind the whole request
    /*! This method rewinds the linked list and returns
     *  the first request in the list.
     */
    MvRequest& rewind();

    //! Extracts the current (sub)request from a (multi) request list structure
    /*! Use this method if you need to modify and/or forward just one
     *  subrequest that is embedded in a MvRequest containing many subrequests.
     */
    MvRequest justOneRequest() const;  // Get just one request

    // -----
    //! Sets double value 'v' to parameter 'p'
    void setValue(const char* p, double v);

    //! Sets int value 'v' to parameter 'p'
    void setValue(const char* p, int v);

    //! Sets const char* value 'v' to parameter 'p'
    void setValue(const char* p, const char* v);

    //! Sets request* value 'v' to parameter 'p'
    void setValue(const char* p, const request* v);

    //! Sets MvRequest* value 'v' to parameter 'p', as a subrequest
    void setValue(const char* p, const MvRequest& v);

    //! Sets long value 'v' to parameter 'p'
    void setValue(const char* p, long v);

    //! Sets void* value 'v' to parameter 'p'
    void setValue(const char* p, const void* v);


    //! Sets/changes the command verb to 'cmd'
    void setVerb(const char* cmd);


    //! Unsets (removes) parameter 'pname'
    void unsetParam(const char* pname);

    // -----

    //! Returns the number of parameters
    int countParameters(bool includeHidden = true) const;

    //! Returns the name of the 'n'th parameter
    /*! NOTE: Method uses C style indexing (n=0,1,...) */
    const char* getParameter(int n) const;

    //! Initialize Parameter structure
    /*! If structure Parameter is empty, returns 'false';
     *  otherwise, returns 'true'.
     */
    bool iterInitParam();

    //! Get 'Interface' request from the current 'parameter'
    //! and advance the list iterator
    /*! If no more parameter is available in the current request,
     *  returns 'false' and sets output request to empty;
     *  otherwise, returns 'true' and returns the Interface request
     *  related to the current parameter.
     */
    bool iterGetNextParamInterface(MvRequest&);

    //! Merges input request with current MvRequest using 'update' function
    /*! This function does a "partial merge". Only common parameters are updated,
     *  i.e. those parameters in 'req' that do not exist in the current object are skipped.
     */
    void merge(const MvRequest& req);

    //! Merges input request with current MvRequest using a function from Mars
    /*! This function does a "full merge" using Mars function 'reqcpy()'.
     *  If requests contain parameters with same names, it looks like the
     *  the values from 'req' overwrite original values (needs to be checked).
     */
    void mars_merge(const MvRequest& req);

    //! Update current MvRequest.
    /*! This routine is similar to 'mars_merge' function
     *  but only common parameters are updated
     */
    void update(const MvRequest&);

    // Convert request to upper/lower case
    MvRequest convertLetterCase(bool upper = true);

    // -----

    //! Adds double value 'v' to list parameter 'p'
    void addValue(const char* p, double v);

    //! Adds int value 'v' to list parameter 'p'
    void addValue(const char* p, int v);

    //! Adds const char* value 'v' to list parameter 'p'
    void addValue(const char* p, const char* v);

    //! Adds long value 'v' to list parameter 'p'
    void addValue(const char* p, long v);

    //! Adds request* value 'v' to list parameter 'p'
    void addValue(const char* p, const request* v);

    //! Adds MvRequest* value 'v' to list parameter 'p'
    void addValue(const char* p, const MvRequest& v);

    //! Adds void* value 'v' to list parameter 'p'
    void addValue(const char* p, const void* v);

    //! Returns the number of values in parameter 'pname'
    /*! Mostly used for list valued parameters
     */
    int countValues(const char* pname) const;

    //! Prints the request to standard output
    /*! Hidden parameters are not printed (unless -DEBUG flag
     *  is used, or mars.debug is set to non-zero)
     */
    void print(bool force = false) const;

    //! Reads a request from file named 'file'.
    /*! if the second parameter is true then any request parameter,
     *  which is an icon (subrequest), will be expanded
     */
    void read(const char* file, bool expand = false, bool shidden = false);

    //! Saves a request into file named 'file'
    void save(const char* file, bool hidden = false) const;

    //! Saves a request into an open writable C FILE*
    void save(FILE* file) const;

    // -----
    // add of those method should not be there B.R.

    //! Handles parameters marked with "_APPLICATION_OVERRIDES" in 'req'
    /*! All parameters marked with "_APPLICATION_OVERRIDES" parameter
     *  in 'req' are either replaced (if a new value is given in 'req')
     *  or unset (if no new value given in 'req').
     */
    void appOverrides(MvRequest& req);

#if 0
//-- following commented-out functions are now (May 2008) obsolete
	// Functions that apply to every request

	//! Obsolete method?
	/*! Was used only by VisMod in VisMod/MagJob.cc (before Magics 6 and PlotMod)
	 */
	void noOverwrite() { _overwrite = 0; }

	//! Obsolete methods?
	/*! All three 'setValueToAll' methods were used only by VisMod
	 *  in VisMod/MagJob.cc (before Magics 6 and PlotMod)
	 */
	void setValueToAll(const char*, const char*, double);
	void setValueToAll(const char*, const char*, int);
	void setValueToAll(const char*, const char*, const char*);

	//! Obsolete methods?
	/*! All three 'addValueToAll' methods were used only by VisMod
	 *  in VisMod/MagJob.cc (before Magics 6 and PlotMod)
	 */
	void addValueToAll(const char*, const char*, double);
	void addValueToAll(const char*, const char*, int);
	void addValueToAll(const char*, const char*, const char*);

	//! Not used, or is it?
	const char* enquire(const char* request_name,
		const char* classtype,
		const char* keyword) ;
	//	enquire value
	//	Note: strip command and allocate string
#endif

    //! Used by PlotMod/uPlot
    const char* enquire(const char* request_name,
                        const char* keyword, int posit = 0);
    //	enquire value
    //	Note: does not allocate string

    // Clean request
    //! Removes everything, making the request empty
    void clean();

    bool isEmpty()
    {
        return (FirstRequest == 0);
    }

    // Get the Object request given its name
    request* findRequestObject();

    // Expand request
    MvRequest ExpandRequest(const char*, const char*, long);


    //  Get the base date from the request (specific to request from GRIB data)
    int getBaseDate();

    // Find out path (it can be relative path as well) stored in parameter par.
    bool getPath(const std::string& par, std::string& resPath, bool canBeEmpty) const;
    bool getPath(const char* iconPar, const char* textPar, std::string& resPath, bool canBeEmpty, std::string& errTxt);
    bool getPathAndReplace(const std::string& par, std::string& resPath, const std::string& replaceFrom,
                           const std::string& replaceTo, bool canBeEmpty = false);
    bool getDate(const std::string& par, std::string& value, bool canbeEmpty = false);
    bool getTime(const std::string& par, std::string& value, bool canBeEmpty = false);
    bool getTimeLenInSec(const std::string& par, std::string& value, bool canBeEmpty = false);
    bool getValueId(const std::string& par, std::string& value, const std::map<std::string, std::string>& idMap, bool canBeEmpty = false);
    bool getValue(const std::string& par, std::string& value, bool canBeEmpty = false) const;
    bool getValue(const std::string& par, std::vector<std::string>& value, bool canBeEmpty = false) const;

    static bool getDate(const std::string& par, const std::string& str, std::string& value);
    static bool getTime(const std::string& par, const std::string& str, std::string& value);
    static bool getTimeLenInSec(const std::string& par, const std::string& str, std::string& value);

    // Get error message from the request
    bool getError(std::string&);

    // Check if the input request has the same contents given a set of parameters
    bool checkParameters(MvRequest&, std::vector<std::string>&);
    bool checkParameters(MvRequest&, std::vector<std::string>&, std::string&);
    bool checkOneParameter(MvRequest&, const std::string&);

    static void replaceDotInPath(MvRequest& in);

    std::string toJson() const;


    static bool paramsEqual(MvRequest& req1, MvRequest& req2,
                            const std::string& name,
                            const std::string& label1, const std::string& label2,
                            std::string& msg);

    static bool paramEqualToVerb(MvRequest& req1, MvRequest& req2,
                                 const std::string& name,
                                 const std::string& label1, const std::string& label2,
                                 std::string& msg);
};

//! Convenience class (not used currently)
/*! MvValue is a convenience class to easily create NUMBER
 *  and STRING requests. Although this class is not used
 *  currently (May 2008), it could be used in the future.
 */
class MvValue : public MvRequest
{
public:
    //! Constructor for NUMBER request
    /*! Creates a NUMBER request with a VALUE parameter.
     *  Argument 'v' is assigned as the value for VALUE.
     */
    MvValue(double v);

    //! Constructor for STRING request
    /*! Creates a STRING request with a VALUE parameter.
     *  Argument 's' is assigned as the value for VALUE.
     */
    MvValue(const char* s);
};

//! Convenience class (not used currently)
/*! MvGrid is a convenience class to easily create
 *  GRIB requests. Although this class is not used
 *  currently (May 2008), it could be used in the future.
 */
class MvGrib : public MvRequest
{
public:
    //! Constructor, with GRIB file path name as an argument
    MvGrib(const char* path);
};
