/***************************** 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,"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,"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,"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,"Parameter HUMIDITY not found...");
		return FALSE;
	}
	Pnhum = GetField(grib,param,Pfshum,Plevtype);
	if(Pnhum == 0)
	{
		setError(1,"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,"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,"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,"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,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,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,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();
}
