/***************************** 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 <Metview.h>
#include <MvFortran.h>

#ifdef FORTRAN_UPPERCASE
#define pott_ POTT
#define eqpott_ EQPOTT
#define seqpott_ SEQPOTT
#endif

#ifdef FORTRAN_NO_UNDERSCORE
#define pott_ pott
#define eqpott_ eqpott
#define seqpott_ seqpott
#endif

extern "C" void pott_(void);
extern "C" void eqpott_(void);
extern "C" void seqpott_(void);

#define TEMP 130
#define SPEHUM 133
#define LNSP 152
#define RELHUM 157

class PottF : public MvService
{
protected:
    char Plevtype[10];   // level type (ML or PL)
    int Pntemp;          // number of temperature fields
    int Pnlnsp;          // number of lnsp fields
    MvFieldSet Pfstemp;  // temperature field set
    MvFieldSet Pfslnsp;  // lnsp field set

public:
    PottF(char* a) :
        MvService(a) {}
    void serve(MvRequest&, MvRequest&);

    virtual int GetData(MvRequest&) = 0;
    virtual int Apply(MvRequest&)   = 0;
    int GetField(MvRequest&, int, MvFieldSet&, char*);
};

class Pott : public PottF
{
public:
    Pott(char* a) :
        PottF(a) {}

    virtual int GetData(MvRequest&);
    virtual int Apply(MvRequest&);
};

class Eqpott : public PottF
{
    int Pnhum;          // number of humidity fields
    MvFieldSet Pfshum;  // humidity field set

public:
    Eqpott(char* a) :
        PottF(a) {}

    virtual int GetData(MvRequest&);
    virtual int Apply(MvRequest&);
};

class Seqpott : public PottF
{
public:
    Seqpott(char* a) :
        PottF(a) {}

    virtual int GetData(MvRequest&);
    virtual int Apply(MvRequest&);
};

void PottF ::serve(MvRequest& in, MvRequest& out)
{
    // Get data

    if (!GetData(in))
        return;

    // Compute output field

    if (!Apply(out))
        return;
}

int PottF ::GetField(MvRequest& grib, int param, MvFieldSet& fs, char* levtype)
{
    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;
        if (nfields == 1)  // check level type
        {
            MvRequest req = field.getRequest();
            strcpy(levtype, req("LEVTYPE"));
        }
    }

    return nfields;
}

int Pott ::GetData(MvRequest& in)
{
    MvRequest grib;  // temperature grib file

    // Get data from request

    in.getValue(grib, "TEMPERATURE");

    // Get Temperature fields

    Pntemp = GetField(grib, TEMP, Pfstemp, Plevtype);
    if (Pntemp == 0) {
        setError(1, "PottF-> Parameter TEMPERATURE not found...");
        return FALSE;
    }

    // Get Lnsp fields

    if (strcmp(Plevtype, "ML") == 0) {
        // first, try from LNSP user interface
        in.getValue(grib, "LNSP");
        Pnlnsp = GetField(grib, LNSP, Pfslnsp, Plevtype);
        if (Pnlnsp == 0) {
            // second, try from TEMPERATURE user interface
            in.getValue(grib, "TEMPERATURE");
            Pnlnsp = GetField(grib, LNSP, Pfslnsp, Plevtype);
            if (Pnlnsp == 0) {
                setError(1, "PottF-> Parameter LNSP not found...");
                return FALSE;
            }
        }
    }

    return TRUE;
}

int Eqpott ::GetData(MvRequest& in)
{
    int param;       // parameter code
    MvRequest grib;  // temperature/humidity grib file

    // Get temperature data from request

    in.getValue(grib, "TEMPERATURE");

    // Get temperature fields

    Pntemp = GetField(grib, TEMP, Pfstemp, Plevtype);
    if (Pntemp == 0) {
        setError(1, "PottF-> Parameter TEMPERATURE not found...");
        return FALSE;
    }

    // Get humidity data from request

    in.getValue(grib, "HUMIDITY");
    MvFieldSet fset1(grib);  // field set
    MvField fs1   = fset1[0];
    MvRequest req = fs1.getRequest();

    // Get humidity fields

    param = (int)req("PARAM");
    if (param != RELHUM && param != SPEHUM) {
        setError(1, "PottF-> Parameter HUMIDITY not found...");
        return FALSE;
    }
    Pnhum = GetField(grib, param, Pfshum, Plevtype);
    if (Pnhum == 0) {
        setError(1, "PottF-> Parameter HUMIDITY not found...");
        return FALSE;
    }

    // Get Lnsp data

    if (strcmp(Plevtype, "ML") == 0) {
        // first, try from LNSP user interface
        in.getValue(grib, "LNSP");
        Pnlnsp = GetField(grib, LNSP, Pfslnsp, Plevtype);
        if (Pnlnsp == 0) {
            // second, try from TEMPERATURE user interface
            in.getValue(grib, "TEMPERATURE");
            Pnlnsp = GetField(grib, LNSP, Pfslnsp, Plevtype);
            if (Pnlnsp == 0) {
                // third, try from HUMIDITY user interface
                in.getValue(grib, "HUMIDITY");
                Pnlnsp = GetField(grib, LNSP, Pfslnsp, Plevtype);
                if (Pnlnsp == 0) {
                    setError(1, "PottF-> Parameter LNSP not found...");
                    return FALSE;
                }
            }
        }
    }

    return TRUE;
}

int Seqpott ::GetData(MvRequest& in)
{
    MvRequest grib;  // temperature grib file

    // Get data from request

    in.getValue(grib, "TEMPERATURE");

    // Get Temperature fields

    Pntemp = GetField(grib, TEMP, Pfstemp, Plevtype);
    if (Pntemp == 0) {
        setError(1, "PottF-> Parameter TEMPERATURE not found...");
        return FALSE;
    }

    // Get Lnsp fields

    if (strcmp(Plevtype, "ML") == 0) {
        // first, try from LNSP user interface
        in.getValue(grib, "LNSP");
        Pnlnsp = GetField(grib, LNSP, Pfslnsp, Plevtype);
        if (Pnlnsp == 0) {
            // second, try from TEMPERATURE user interface
            in.getValue(grib, "TEMPERATURE");
            Pnlnsp = GetField(grib, LNSP, Pfslnsp, Plevtype);
            if (Pnlnsp == 0) {
                setError(1, "PottF-> Parameter LNSP not found...");
                return FALSE;
            }
        }
    }

    return TRUE;
}

int Pott ::Apply(MvRequest& out)
{
    MvFortran pottf("Pott");  // fortran subroutine

    // Call the fortran routines

    pottf.addParameter(Pfstemp);  // setup fortran params
    pottf.addParameter(Plevtype);
    pottf.addParameter(Pfslnsp);

    putenv("POTTF_ENV=OK");
    pott_();
    char* penv = getenv("POTTF_ENV");
    if (strcmp(penv, "OK")) {
        setError(1, "POTTF-> %s", penv);
        return FALSE;
    }

    // Get the result

    out = pottf.getResult();
    return TRUE;
}

int Eqpott ::Apply(MvRequest& out)
{
    MvFortran eqpottf("Eqpott");  // fortran subroutine

    // Call the fortran routines

    eqpottf.addParameter(Pfstemp);  // setup fortran params
    eqpottf.addParameter(Plevtype);
    eqpottf.addParameter(Pfshum);
    eqpottf.addParameter(Pfslnsp);

    putenv("POTTF_ENV=OK");
    eqpott_();
    char* penv = getenv("POTTF_ENV");
    if (strcmp(penv, "OK")) {
        setError(1, "POTTF-> %s", penv);
        return FALSE;
    }

    // Get the result

    out = eqpottf.getResult();
    return TRUE;
}

int Seqpott ::Apply(MvRequest& out)
{
    MvFortran seqpottf("Seqpott");  // fortran subroutine

    // Call the fortran routines

    seqpottf.addParameter(Pfstemp);  // setup fortran params
    seqpottf.addParameter(Plevtype);
    seqpottf.addParameter(Pfslnsp);

    putenv("POTTF_ENV=OK");
    seqpott_();
    char* penv = getenv("POTTF_ENV");
    if (strcmp(penv, "OK")) {
        setError(1, "POTTF-> %s", penv);
        return FALSE;
    }

    // Get the result

    out = seqpottf.getResult();
    return TRUE;
}

int main(int argc, char** argv)
{
    MvApplication theApp(argc, argv);
    Pott pott("POTT_M"), pott1("POTT_P");
    Eqpott eqpott("EQPOTT_M"), eqpott1("EQPOTT_P");
    Seqpott seqpott("SEQPOTT_M"), seqpott1("SEQPOTT_P");

    theApp.run();
}
