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

const char* HovLineToolkit::GetSecondCoordName()
{
   // By default, it is longitude, unless values x1_ and x2_
   // are the same. In this case, it is latitude.
   // In the NetCDF Visualiser request, parameter NETCDF_X_VARIABLE
   // can not have all the same values (Magics restriction).
   if ( (x2_ - x1_) > 0.00001 )
   {
      glConvention_ = "lonlat";
      return HOV_VARLON.c_str();
   }
   else
   {
      glConvention_ = "latlon";
      return HOV_VARLAT.c_str();
   }
}

const char* HovLineToolkit::GetSecondAuxiliaryCoordName()
{
   // By default, it is latitude, unless values x1_ and x2_
   // are the same. In this case, it is longitude..
   // In the NetCDF Visualiser request, parameter NETCDF_X_VARIABLE
   // can not have all the same values (Magics restriction).
   if ( (x2_ - x1_) > 0.00001 )
      return HOV_VARLAT.c_str();
   else
      return HOV_VARLON.c_str();
}

bool HovLineToolkit::ComputeSecondCoord()
{
   // Compute number of points
   if ( !this->EvaluateNPoints() )
      return false;

   // Initialize variables
   coord1_.clear();
   coord2_.clear();
   coord1_.reserve(nrPoints_);
   coord2_.reserve(nrPoints_);
   double dx = double( x2_ - x1_ ) / double(nrPoints_ -1);
   double dy = double( y2_ - y1_ ) / double(nrPoints_ -1);

   // Compute values
   for ( int k = 0; k < nrPoints_; k++ )
   {
      coord1_.push_back(x1_ + (k * dx));
      coord2_.push_back(y1_ + (k * dy));
   }

   return true;
}

bool HovLineToolkit::ComputeValues( MvField &field, int )
{
   // Initialize variables
   MvFieldExpander x(field);

   // Interpolatate along a line
   for ( int k = 0; k < nrPoints_; k++)
       xint_[k] = field.interpolateAt(coord1_[k], coord2_[k]);

   return true;
}

string HovLineToolkit::GetTitle(ParamInfo *par)
{
   string title = "Hovmoeller of " + par->ParamLongName() + " " + par->LevelTitle() + " " + par->ExpVerTitle();

   return title;
}

bool HovLineToolkit::EvaluateNPoints()
{
   if ( y1_ > 90. || y1_ < -90. || y2_ > 90. || y2_ < -90.)
   {
      marslog(LOG_EROR, "Failed to generate Hovmoeller Line: wrong geographical coordinates");
      return false;
   }

   double dellat = ABS(y2_-y1_);
   double dellon = ABS(x2_-x1_);
   int np = int( sqrt(dellon*dellon + dellat*dellat) / MAX(gridNS_,gridEW_) );

   nrPoints_ = MAX(np,64); //64->arbitrary minimum number of points

   return true;
}

bool HovLineToolkit::GetInputInfo(MvRequest& in)
{
   // Get line
   y1_ = in("LINE",0);
   x1_ = in("LINE",1);
   y2_ = in("LINE",2);
   x2_ = in("LINE",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 swap axes flag
   swapAxes_ = ( (const char*)in("SWAP_AXES") && strcmp(in("SWAP_AXES"),"YES") == 0 ) ? true : false;

   // Set axes orientation
   verticalAxis_ = swapAxes_ ? HOV_GEO : HOV_TIME;

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

   return true;
}

bool HovLineToolkit::GetInputInfo( MvNetCDF* netcdf )
{
   // Get common information from the netCDF file
   if ( !GetInputCommonInfo(netcdf) )
      return false;

   // Retrieve attributes values
   MvRequest req = netcdf->getRequest();

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

   return true;
}

void HovLineToolkit::NcWriteGlobalAttributesApp()
{
   // Add swap axes flag
   if ( swapAxes_ )
      netcdf_->addAttribute("swap_axes",swapAxes_);
}

bool HovLineToolkit::NcWriteSecondCoord()
{
   // Add longitude coordinate to the netCDF file
   vector<string> v_sdim(1,HOV_VARLON);
   vector<long> v_ndim(1,nrPoints_);
   MvNcVar *ncx = netcdf_->addVariable(v_sdim[0],ncDouble,v_ndim,v_sdim);
   ncx->addAttribute("long_name", "longitude");
   ncx->addAttribute("units", "degrees_east");
   ncx->put(coord1_,(long)nrPoints_);

   // Add latitude coordinate to the netCDF file
   v_sdim[0] = HOV_VARLAT;
   ncx = netcdf_->addVariable(v_sdim[0],ncDouble,v_ndim,v_sdim);
   ncx->addAttribute("long_name", "latitude");
   ncx->addAttribute("units", "degrees_north");   //or degrees_south?
   ncx->put(coord2_,(long)nrPoints_);

   return true;
}

bool HovLineToolkit::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("LINE",0) != (double)req2("LINE",0) ||
        (double)req1("LINE",1) != (double)req2("LINE",1) ||
        (double)req1("LINE",2) != (double)req2("LINE",2) ||
        (double)req1("LINE",3) != (double)req2("LINE",3)
      )
   {
      marslog(LOG_EROR,"LINE coordinates are 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 HovLineToolkit::CreateViewRequest()
{
   MvRequest viewReq("MHOVMOELLERVIEW");
   viewReq("TYPE") = this->ApplicationType();
   viewReq.addValue("LINE",y1_);
   viewReq.addValue("LINE",x1_);
   viewReq.addValue("LINE",y2_);
   viewReq.addValue("LINE",x2_);

   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 LineHovmoeller::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 Line.");

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

   return;
}

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


// Translate Metview 3 LineHovmoeller Data to Metview 4 definition. Call Metview 4
// server to process the job.
void LineHovmoellerM3::serve(MvRequest& in,MvRequest& out)
{
   // Send a general warning message
   setError(0, "The Metview 3 Line-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 Line-Hovmoeller DATA icon is deprecated. Parameters TIME_AXIS_DIRECTION, TIME_AXIS, GEO_AXIS and GEO2_AXIS will not be translated internally.");
   MvRequest req = in;
   req.unsetParam("TIME_AXIS_DIRECTION");
   req.unsetParam("TIME_AXIS");
   req.unsetParam("GEO_AXIS");
   req.unsetParam("GEO2_AXIS");

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

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