/***************************** 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 <inc_iostream.h>
#include <stdio.h>

#include <gdbm.h>

#include <fcntl.h>
#include <math.h>
#include <ctype.h>
#include <Metview.h>
#include "station.h"
#include "utils.h"

#define MAX_LIST 150

class StationService : public MvService {

   int count;
   void AddToResult(MvRequest& out,datum& key);
   void AddToResult(MvRequest& out,station& st);
   void serve_WMO(MvRequest&,MvRequest&);
   void serve_EPS(MvRequest&,MvRequest&);
   void serve_location(MvRequest&,MvRequest&);

public:
   StationService();
   void serve(MvRequest&,MvRequest&);

private:
   bool     bFailError_;
   char     mode;
   position pos;
   station  st;
   int      ident;
   double   north, west, south, east;
   double   point_lat, point_lon, threshold;
};

StationService::StationService() : 
   MvService("STATIONS"),
   bFailError_(true),
   mode(0),
   north(90.0),
   west(-180.0),
   south(-90.0),
   east(180.0)
{ }

void StationService::AddToResult(MvRequest& out,datum& key)
{
/* send the request back */
	datum content;
	station st;

	content = gdbm_fetch(db,key);
	memcpy(&st,content.dptr,MIN(content.dsize,sizeof(st)));

	AddToResult( out, st );
}

void StationService::AddToResult( MvRequest& out, station& st )
{
	printf("%05d %d %d %d %s\n",st.ident,st.pos.lat,st.pos.lon,
				st.pos.station_height,st.name);

	if(count < MAX_LIST)
	{
		MvRequest y("STATION");

		y("NAME")       = nice_name(st.name);
		y("IDENT")      = st.ident;
		y("LATITUDE")   = st.pos.lat/100.0;
		y("LONGITUDE")  = st.pos.lon/100.0;
		y("HEIGHT")     = st.pos.station_height;

		out = out + y;
	}
	count++;
}

void StationService::serve(MvRequest& in,MvRequest& out)
{
   const char cIDENT = 'I';

in.print();

   const char* cfail = (const char*)in("FAIL_ON_ERROR");
   if ( cfail && strcmp(cfail,"NO") == 0 )
      bFailError_ = false;

   const char* p1 = in( "STATION_TYPE" );
   const char* p2 = in( "SEARCH_STATIONS_DATABASE" );

   if( ( p1 && *p1 == 'L' ) || ( p2 && *p2 == 'N' ) )
   {
      serve_location( in, out );
   }
   else
   {
      p1 = in( "SEARCH_KEY" );
      if( !p1 )
         p1 = &cIDENT;

      switch( *p1 )
      {
         case 'N':
            mode = 'n';
            break;

         case 'I':
            mode = 'i';
            ident = in("IDENT");
            break;

         case 'W':
            mode = 'i';
            ident = in("WMO_BLOCK");
            break;

         case 'P':
            mode = 'l';
            threshold = in("THRESHOLD");
            north = in("POSITION",0); north += threshold;
            west  = in("POSITION",1); west  -= threshold;
            south = in("POSITION",0); south -= threshold;
            east  = in("POSITION",1); east  += threshold;
            break;

         case 'A':
            mode = 'l';
            north  = in("AREA",0);
            west   = in("AREA",1);
            south  = in("AREA",2);
            east   = in("AREA",3);
            if(north < south)
            {
               double tmp = north;
               north      = south;
               south      = tmp;
            }
            break;
      }

      if( p2 && *p2 == 'E' )
         serve_EPS( in, out );
      else
         serve_WMO( in, out );
    }

out.print();
}

void StationService::serve_WMO(MvRequest& in,MvRequest& out)
{
	datum key,k;
	const char *name = NULL;

	if (open_database(mode)) {
		setError(42,"Station - Cannot open database %s", getenv("METVIEW_STATIONS"));
		exit (1);
	}

	count=0;

	// Look for perfect matches

	switch(mode){
		case 'n':

			if(in.countValues("NAME") == 1)
			{
				name = in("NAME");
				strcpy(st.name,upcase(name));
				k.dptr   = (char*)&st.name;
				k.dsize  = strlen(st.name)+1;

				key = gdbm_fetch(db,k);

				if(key.dptr != NULL)
				{
					AddToResult(out,k);
					gdbm_close(db);
					return;
				}
			}
			break;

		case 'i':
			if(in.countValues("IDENT") == 1)
				if(ident >= 01000)
				{
					st.ident = ident;
					k.dptr   = (char*)&st.ident;
					k.dsize  = sizeof(st.ident);

					key = gdbm_fetch(db,k);

					if(key.dptr != NULL)
					{
						AddToResult(out,k);
						gdbm_close(db);
						return;
					}
				}
			break;

		default : break;
	};

	// If no match, loop

	for (key = gdbm_firstkey(db);  key.dptr != NULL; key = gdbm_nextkey(db, key)) {

		int m, ok = 0;
		int n = 0;

		switch(mode){ /* open database related to request */

		case 'n' :
			while((name = in("NAME",n++)) != NULL)
				if (same(name,(const char *)key.dptr) )
					ok = 1;
			break;

		case 'i' :
			memcpy(&m, key.dptr, sizeof(m));
			if (ident < 100) ok = (m/1000 == ident);
			else ok = (ident == m);
			break;

		case 'l' :
			memcpy(&pos, key.dptr, sizeof(pos));
			ok = 1;
			point_lat=((double) pos.lat)/100.0;
			point_lon=((double) pos.lon)/100.0;
			ok = inbox(point_lat,point_lon,north,west,south,east);
			break;
		}

		if (ok) AddToResult(out,key);
	}

   if(count == 0)
   {
      if( bFailError_ )
         setError(1,"No matching WMO station found");
      else
         setError(0,"No matching WMO station found");
   }

	if(count>MAX_LIST)
		sendProgress("Too many stations, only %d out of %d were returned",MAX_LIST,count);

	gdbm_close(db);
}

void StationService::serve_EPS(MvRequest& in,MvRequest& out)
{
  //serve_WMO( in, out ); //-- not yet implementd, use WMO!!!!
  const char* name;
  ifstream* EPS_file = 0;
  station   st;

  if( open_EPS_stationfile( EPS_file ) )
    {

      while( read_one_station( EPS_file, st ) )
	{
	  int  n = 0;
	  int ok = 0;

	  switch(mode)
	    {

	    case 'n' :
	      while( ( name = in("NAME", n++) ) != NULL )
		if( same( name, st.name ) )
		  ok = 1;
	      break;

	    case 'i' :
	      if( ident < 100 )
		ok = ( st.ident/1000 == ident );
	      else
		ok = ( ident == st.ident );
	      break;

	    case 'l' :
	      ok = 1;
	      point_lat=((double) st.pos.lat)/100.0;
	      point_lon=((double) st.pos.lon)/100.0;
	      ok = inbox(point_lat,point_lon,north,west,south,east);
	      break;
	    }

	  if (ok)
	    AddToResult( out, st );
	}

    }

  delete EPS_file;

  return;
}

void StationService::serve_location(MvRequest& in,MvRequest& out)
{
  // Don't look in database

  MvRequest y("STATION");

  y("NAME")       = in("NAME");
  y("LATITUDE")   = in("POSITION",0);
  y("LONGITUDE")  = in("POSITION",1);

  if( (const char*)in("HEIGHT") )   //-- pass HEIGHT if HEIGHT set
  {
      // specifically check whether it is exactly '0', and don't add it if it is
      if (strcmp((const char*)in("HEIGHT"), "0"))
      {
          y("HEIGHT") = in("HEIGHT");
      }

  /*
    int h = (int)in("HEIGHT");
    if( h != 0 )
       y("HEIGHT") = in("HEIGHT");
   */
  }

  out = y;

  return;
}


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