/***************************** 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 "HovArea.h"

HovAreaToolkit::HovAreaToolkit() :
                geoDir_(HOV_EW)
{}

const char* HovAreaToolkit::GetSecondCoordName()
{
   if ( geoDir_ == HOV_NS )
      return HOV_VARLON.c_str();
   else
      return HOV_VARLAT.c_str();
}

bool HovAreaToolkit::ComputeSecondCoord()
{
   double x, y;
   int    i;

   if( geoDir_ == HOV_EW )    //average E->W
   {
      for (i = 0, y = y1_; y >= y2_;)
      {
         // do not add within the loop becuase of cumulative errors
         coord1_.push_back(y);
         i++;
         y = y1_ - (i * gridNS_);
      }
   }
   else                       //average N->S
   {
      for (i = 0, x = x1_; x <= x2_;)
      {
         // do not add within the loop becuase of cumulative errors
         coord1_.push_back(x);
         i++;
         x = x1_ + (i * gridEW_);
      }
   }

   // Initialise number of points
   this->NPoints(coord1_.size());

   return true;
}

bool HovAreaToolkit::ComputeValues(MvField &field, int)
{
   MvFieldExpander x(field);

   // Average over area
   // 1) AverageAlong() does not know array size. If it fails then returned array
   //    remains empty. Thus, fill with missing values here.
   // 2) AverageAlong() assumes that the AREA definition follows the MARS convention
   //    N-S/W-E. Therefore, if average direction is EW then the computed values follow
   //    the order N-S, e.g. xint_[0] = average(N), ..., xint_[nrPoints_] = average(S)
   //    and gcoord1_[0] = N], ..., gcoord1_[nrPoints_] = S.
   // 3) gridNS_ or gridEW_ have the same value

   bool interp = false; //set bilinear interpolation
   if ( !field.averageAlong(xint_,x1_,y1_,x2_,y2_,geoDir_,nrPoints_,gridNS_,interp) )
   {
      for (int i = 0; i < nrPoints_; i++ )
         xint_[i] = DBL_MAX;

      return false;
   }

   return true;
}

string HovAreaToolkit::GetTitle(ParamInfo *par)
{
   char title[256];
   if ( geoDir_ == HOV_EW )
   {
      sprintf(title,"Hovmoeller of %s %s %s (%.1f%s-%.1f%s)",
              par->ParamLongName().c_str(), par->LevelTitle().c_str(), par->ExpVerTitle().c_str(),
              (x1_< 0) ? -x1_ : x1_, (x1_<0) ? "W" : "E",
              (x2_< 0) ? -x2_ : x2_, (x2_<0) ? "W" : "E" );
   }
   else
   {
      sprintf(title,"Hovmoeller of %s %s %s (%.1f%s-%.1f%s)",
              par->ParamLongName().c_str(), par->LevelTitle().c_str(), par->ExpVerTitle().c_str(),
              (y1_< 0) ? -y1_ : y1_, (y1_<0) ? "S" : "N",
              (y2_< 0) ? -y2_ : y2_, (y2_<0) ? "S" : "N" );
   }

   return string(title);
}

bool HovAreaToolkit::GetInputInfo(MvRequest& in)
{
    // Get area
   y1_ = in("AREA",0);
   x1_ = in("AREA",1);
   y2_ = in("AREA",2);
   x2_ = in("AREA",3);

   // Check if coordinates follow Mars rules (n/w/s/e)
   bool swap = false;
   if ( x1_ > x2_ )
   {
      double W = x1_;
      x1_ = x2_;
      x2_ = W;
      swap = true;
   }
   if( y2_ > y1_ )
   {
      double W = y1_;
      y1_ = y2_;
      y2_ = W;
      swap = true;
   }

   // Send a warning message
   if ( swap )
//      marslog(LOG_WARN,"WARNING: Input geographical coordinates do not follow MARS rules (n/w/s/e). Values have been swapped.");
      cout << "WARNING: Input geographical coordinates do not follow MARS rules (n/w/s/e). Values have been swapped." << endl;

   // Get direction
   geoDir_ = ((const char*)in("AVERAGE_DIRECTION") && strcmp(in("AVERAGE_DIRECTION"),"NORTH_SOUTH" ) == 0 ) ? HOV_NS : HOV_EW;

   // Get swap axes flag
   swapAxes_ = ( (const char*)in("SWAP_AXES") && strcmp(in("SWAP_AXES"),"YES") == 0 ) ? true : false;

   // Set axes orientation
   if ( (swapAxes_ && geoDir_ == HOV_EW) || (!swapAxes_ && geoDir_ == HOV_NS) )
      verticalAxis_ = HOV_TIME;
   else
      verticalAxis_ = HOV_GEO;

   // Initialize x/y resolution
   gridNS_= in("RESOLUTION");
   gridEW_= in("RESOLUTION");

   return true;
}

bool HovAreaToolkit::GetInputInfo( MvNetCDF* netcdf )
{
   // Retrieve attributes values
   MvRequest req = netcdf->getRequest();

   // Retrieve Direction value
   const char* cp = (const char*)req("average_direction");
   if ( cp == NULL )
   {
      marslog(LOG_EROR, "Netcdf file: attribute average_direction not found");
      return false;
   }
   geoDir_ = ( strcmp(cp,"NORTH_SOUTH") == 0 ) ? HOV_NS : HOV_EW;

   // Retrieve swap axes flag, if exists
   cp = (const char*)req("swap_axes");
   swapAxes_ = ( cp == NULL ) ? false : atoi(cp);

   // Get common information from the netCDF file
   // This command needs the geoDir_ info
   if ( !GetInputCommonInfo(netcdf) )
      return false;

   return true;
}

void HovAreaToolkit::NcWriteGlobalAttributesApp()
{
   // Add average direction
   const char* adir = ( geoDir_ == HOV_NS ) ? "NORTH_SOUTH" : "EAST_WEST";
   netcdf_->addAttribute("average_direction",adir);

   // Add swap axes flag
   if ( swapAxes_ )
      netcdf_->addAttribute("swap_axes",swapAxes_);
}

bool HovAreaToolkit::NcWriteSecondCoord()
{
   // Compute second coords (lat/lng)
   // Add second coordinate to the netCDF file
   vector<string> v_sdim(1,GetSecondCoordName());
   vector<long> v_ndim(1,nrPoints_);
   MvNcVar *ncx = netcdf_->addVariable(v_sdim[0],ncDouble,v_ndim,v_sdim);
   ncx->put(coord1_,(long)nrPoints_);
   if ( verticalAxis_ == HOV_TIME )
   {
      ncx->addAttribute("long_name", "longitude");
      ncx->addAttribute("units", "degrees_east");
   }
   else
   {
      ncx->addAttribute("long_name", "latitude");
      ncx->addAttribute("units", "degrees_north"); //or degrees_south?
   }

   return true;
}

bool HovAreaToolkit::consistencyCheck( MvRequest& req1, MvRequest& req2)
{
   // Check input TYPE
   if ( (const char*)req1("TYPE") != (const char*)req2.getVerb() )
   {
      marslog(LOG_EROR,"TYPE parameter is not consistent");
      return false;
   }

   // Check coordinates
   if ( (double)req1("AREA",0) != (double)req2("AREA",0) ||
        (double)req1("AREA",1) != (double)req2("AREA",1) ||
        (double)req1("AREA",2) != (double)req2("AREA",2) ||
        (double)req1("AREA",3) != (double)req2("AREA",3)
      )
   {
      marslog(LOG_EROR,"AREA coordinates are not consistent");
      return false;
   }

   // Check DIRECTION
   if ( (const char*)req1("AVERAGE_DIRECTION") != (const char*)req2("AVERAGE_DIRECTION") )
   {
      marslog(LOG_EROR,"AVERAGE_DIRECTION parameter is not consistent");
      return false;
   }

     // Check SWAP AXES
   if ( (const char*)req1("SWAP_AXES") != (const char*)req2("SWAP_AXES") )
   {
      marslog(LOG_EROR,"SWAP_AXES parameter is not consistent");
      return false;
   }

   // Check RESOLUTION
   if ( (const char*)req1("RESOLUTION") != (const char*)req2("RESOLUTION") )
   {
      marslog(LOG_EROR,"RESOLUTION parameter is not consistent");
      return false;
   }

   return true;
}

MvRequest HovAreaToolkit::CreateViewRequest()
{
   MvRequest viewReq("MHOVMOELLERVIEW");

   viewReq("TYPE") = this->ApplicationType();
   viewReq.addValue("AREA",y1_);
   viewReq.addValue("AREA",x1_);
   viewReq.addValue("AREA",y2_);
   viewReq.addValue("AREA",x2_);

   viewReq("AVERAGE_DIRECTION") = ( geoDir_ == HOV_NS ) ? "NORTH_SOUTH" : "EAST_WEST";

   viewReq("SWAP_AXES") = swapAxes_ ? "YES" : "NO";
   if ( (const char*)origReq_("RESOLUTION") )
      viewReq("RESOLUTION") = (const char*)origReq_("RESOLUTION");

   viewReq("_CLASS") = "MHOVMOELLERVIEW";
   viewReq("_DEFAULT") = 1;
   viewReq("_DATAATTACHED") = "YES";

   return viewReq;
}

//--------------------------------------------------------------

void AreaHovmoeller::serve (MvRequest& in, MvRequest& out)
{
cout << "Request IN:" << endl;
in.print();

   // Compute Hovmoeller diagram
   origReq_ = in;
   if (!Compute(in,out))
      setError(1, "Failed to generate Hovmoeller.");

cout << "Request OUT:" << endl;
out.print();
}

//-------------------------------------------------------------------------

// Translate Metview 3 AreaHovmoeller Data to Metview 4 definition. Call Metview 4
// server to process the job.
void AreaHovmoellerM3::serve(MvRequest& in,MvRequest& out)
{
   // Send a general warning message
   setError(0, "The Metview 3 Area-Hovmoeller DATA icon is deprecated. An automatic translation to the correspondent Metview 4 icon will be performed internally, but may not work for all cases. It is recommended to manually replace the old icons with their new equivalents.");

   // There are input parameters that are no longer available in Metview 4.
   // Remove them and send a warning message.
   setError(0,"The Metview 3 Area-Hovmoeller DATA icon is deprecated. Parameters TIME_AXIS_DIRECTION, TIME_AXIS and GEO_AXIS will not be translated internally.");
   MvRequest req = in;
   req.unsetParam("TIME_AXIS_DIRECTION");
   req.unsetParam("TIME_AXIS");
   req.unsetParam("GEO_AXIS");

   // Keep the remaining parameters and update the VERB
   req.setVerb("AREA_HOVM");

   // Call the server to process the job
   AreaHovmoeller::serve(req,out);
}
