/***************************** 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 *************************************/

// Methods for HovmoellerView class

#include "HovmoellerView.h"
#include <MvRequestUtil.hpp>
#include "ObjectList.h"
#include "PlotMod.h"
#include "Root.h"

// Hovmoeller types
static const string HOVLINE = "LINE_HOVM";
static const string HOVAREA = "AREA_HOVM";
static const string HOVVERT = "VERTICAL_HOVM";

//--------------------------------------------------------
static HovmoellerViewFactory hovmoellerViewFactoryInstance;

PlotModView*
HovmoellerViewFactory::Build ( Page& page,
                               const MvRequest& contextRequest,
                               const MvRequest& setupRequest )
{
   // Expand request
   MvRequest expReq = ObjectList::ExpandRequest(contextRequest,EXPAND_DEFAULTS);

   // Copy hidden parameters
   expReq.mars_merge(contextRequest);

   // Instantiate a Hovmoeller view
   return new HovmoellerView ( page, expReq, setupRequest );
}

//--------------------------------------------------------
static HovmoellerViewM3Factory hovmoellerViewM3FactoryInstance;

PlotModView*
HovmoellerViewM3Factory::Build ( Page& page,
                                 const MvRequest& contextRequest,
                                 const MvRequest& setupRequest )
{
   // Translate view request to Metview 4
   MvRequest viewM4Req = this->Translate(contextRequest);

   // Instantiate a Hovmoeller View
   return new HovmoellerView ( page, viewM4Req, setupRequest );
}

MvRequest
HovmoellerViewM3Factory::Translate ( const MvRequest& in )
{
   // Send a warning message
   PlotMod::Instance().MetviewError ("The Metview 3 HOVMOELLER VIEW icon is deprecated. An automatic translation to the Metview 4 HOVMOELLER VIEW 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.","WARNING");

   // Expand request
   MvRequest req = ObjectList::ExpandRequest(in,EXPAND_DEFAULTS);

   // Copy Line parameter
   MvRequest viewReq("MHOVMOELLERVIEW");

   // Expand output request
   MvRequest out = ObjectList::ExpandRequest(viewReq,EXPAND_DEFAULTS);

   return out;
}

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

HovmoellerView::HovmoellerView ( Page& owner,
                                 const MvRequest& viewRequest,
                                 const MvRequest& setupRequest ):
                CommonXSectView ( owner, viewRequest, setupRequest ),
                bDataUnit_(false),
                bUpdateDate_(true)
{
   SetVariables(viewRequest,true);
   ApplicationName(type_.c_str());
}

HovmoellerView::HovmoellerView ( const HovmoellerView &old ) :
                CommonXSectView(old)
{
   type_      = old.type_;
   dateMin_   = old.dateMin_;
   dateMax_   = old.dateMax_;
   bAutoDate_ = old.bAutoDate_;
   swapAxes_  = old.swapAxes_;
   bDataUnit_ = old.bDataUnit_;
   bUpdateDate_ = old.bUpdateDate_;
}

string HovmoellerView::Name()
{
   int id =  Owner().Id();
   string name =  (const char*)ObjectInfo::ObjectName ( viewRequest_, "HovmoellerView", id );

   return name;
}

void HovmoellerView::DescribeYourself ( ObjectInfo& description )
{
   // Convert my request to macro
   set<Cached> skipSet;
   description.ConvertRequestToMacro ( appViewReq_, PUT_END, MacroName().c_str(),"mhovmoellerview", skipSet);
   description.PutNewLine (" " );
}

void HovmoellerView::SetVariables(const MvRequest &in, bool)
{
   // Set Hovmoeller type
   type_ = (const char*)in("TYPE");

   // Set geographical coordinates
   char param[6];
   if ( in.countValues("LINE") )
       strcpy(param,"LINE");
   else
       strcpy(param,"AREA");

   latMin_ = in(param,2);
   latMax_ = in(param,0);
   lonMin_ = in(param,1);
   lonMax_ = in(param,3);

   // Check if coordinates follow Mars rules (n/w/s/e)
   bool swap = false;
   if ( lonMin_ > lonMax_ )
   {
      double W = lonMin_;
      lonMin_ = lonMax_;
      lonMax_ = W;
      swap = true;
   }
   if( latMin_ > latMax_ )
   {
      double W = latMin_;
      latMin_ = latMax_;
      latMax_ = W;
      swap = true;
   }

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

   // Set date limits
   bool bDateMin, bDateMax;
   if ( (const char*)in("TIME_AXIS_MODE") && strcmp((const char*)in("TIME_AXIS_MODE"),"AUTOMATIC_FORWARDS") == 0 )
   {
      bDateMin = bDateMax = true;
      dateMin_ = "2013-01-01";
      dateMax_ = "2013-01-01 00:01:00";
   }
   else if ( (const char*)in("TIME_AXIS_MODE") && strcmp((const char*)in("TIME_AXIS_MODE"),"AUTOMATIC_BACKWARDS") == 0 )
   {
      bDateMin = bDateMax = true;
      dateMax_ = "2013-01-01";
      dateMin_ = "2013-01-01 00:01:00";
   }
   else
   {
      if ( strcmp((const char*)in("DATE_MIN"),"AUTOMATIC") == 0 )
      {
         bDateMin = true;
         dateMin_ = "2013-01-01";
      }
      else
      {
         bDateMin = false;
         dateMin_ = (const char*)in("DATE_MIN");
      }

      if ( strcmp((const char*)in("DATE_MAX"),"AUTOMATIC") == 0 )
      {
         bDateMax = true;
         dateMax_ = "2013-01-01 00:01:00";
      }
      else
      {
         bDateMax = false;
         dateMax_ = (const char*)in("DATE_MAX");
      }
   }

   // Both date values must be automatic or non-automatic
   if ( bDateMin && bDateMax )
      bAutoDate_ = true;
   else if ( !bDateMin && !bDateMax )
      bAutoDate_ = false;
   else
   {
      PlotMod::Instance().MetviewError ("Hovmoeller Error: both parameters DATE_MIN and DATE_MAX must be automatic or non-automatic.\n","ERROR");
      bAutoDate_ = false;
   }

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

   return;
}

bool HovmoellerView::UpdateView ()
{
viewRequest_.print();
   // Check if the View needs to be updated
   if ( string(viewRequest_.getVerb()) == CARTESIANVIEW )
   {
      // The View has already been updated to CARTESIANVIEW,
      // but there is one case where the Date parameter needs to be
      // updated again: when parameters DATE_MIN and DATE_MAX in the
      // HovmoellerView were defined as AUTOMATIC and when the
      // CARTESIANVIEW was first created with no DataUnit attached 
      // (i.e. an empty View was plotted - in this case MAGICS needs to 
      // receive a default X/Y_DATE_MIN/MAX values). This code would not
      // be necessary if MAGICS was able to receive a AUTOMATIC value 
      // without a DataUnit attached.
      if ( bUpdateDate_ )
      {
         if ( (const char*)viewRequest_("X_AXIS_TYPE") && 
           ( strcmp((const char*)viewRequest_("X_AXIS_TYPE"),"DATE") == 0 ) )
         {
            if ( bAutoDate_ )
            {
               viewRequest_("X_AUTOMATIC") = "ON";
			   MvDate dmin = (const char*)viewRequest_("X_DATE_MIN");
			   MvDate dmax = (const char*)viewRequest_("X_DATE_MAX");
			   if ( dmin > dmax )
			      viewRequest_("X_AUTOMATIC_REVERSE") = "ON";

               viewRequest_.unsetParam("X_DATE_MIN");
               viewRequest_.unsetParam("X_DATE_MAX");
               bUpdateDate_ = false;
            }
         }
         else if ( (const char*)viewRequest_("Y_AXIS_TYPE") && 
           ( strcmp((const char*)viewRequest_("Y_AXIS_TYPE"),"DATE") == 0 ) )
         {
            if ( bAutoDate_ )
            {
               viewRequest_("Y_AUTOMATIC") = "ON";
			   MvDate dmin = (const char*)viewRequest_("Y_DATE_MIN");
			   MvDate dmax = (const char*)viewRequest_("Y_DATE_MAX");
			   if ( dmin > dmax )
			      viewRequest_("Y_AUTOMATIC_REVERSE") = "ON";

               viewRequest_.unsetParam("Y_DATE_MIN");
               viewRequest_.unsetParam("Y_DATE_MAX");
               bUpdateDate_ = false;
            }
         }
      }
viewRequest_.print();
      return true;
   }

   // Translate specific parameters
   MvRequest cartView("CARTESIANVIEW");
   if ( type_ == HOVLINE )
      UpdateViewLine( cartView );
   else if ( type_ == HOVAREA )
      UpdateViewArea( cartView );
   else
      UpdateViewVert( cartView );

    // Copy PAGE and SUBPAGE definition
    CopySomeParameters( viewRequest_,cartView,"PAGE" );
    CopySomeParameters( viewRequest_,cartView,"SUBPAGE" );

    // Copy the original request without certain parameteres (to avoid duplication)
    RemoveParameters( viewRequest_, "HORIZONTAL_AXIS" );
    RemoveParameters( viewRequest_, "VERTICAL_AXIS" );
    RemoveParameters( viewRequest_, "PAGE" );
    RemoveParameters( viewRequest_, "SUBPAGE" );
    RemoveParameters( viewRequest_, "_" );
    cartView("_ORIGINAL_REQUEST") = viewRequest_;

    // Update request
    viewRequest_ = cartView;

    // Indicate that the plotting tree needs to be rebuilt
    Root::Instance().Refresh(false);
viewRequest_.print();

    return true;
}

void HovmoellerView::UpdateView ( MvRequest& view )
{
   this->ApplicationInfo(view);
}

void HovmoellerView::UpdateViewLine ( MvRequest& cartView )
{
cartView.print();
   // Translate Geographical and Time coordinates
   // IMPORTANT: 
   // 1) The definition of the min/max/lat/lon coordinates must 
   // match the organisation of the netCDF data matrix, which is:
   // | [LatNorth,LonWest] ... [LatNorth,LonEast] |
   // |          ...                              |
   // | [LatSouth,LonWest] ... [LatSouth,LonEast] |
   //
   // 2) Hovmoeller computes values from North to South (first value
   // in the netCDF matrix is [LatNorth,LonWest] and Magics plots them
   // from bottom to top by default.
   if ( swapAxes_ )
   {
      cartView("Y_AUTOMATIC")     = "OFF";
      cartView("Y_AXIS_TYPE")     = "GEOLINE";
      cartView("Y_MIN_LATITUDE")  = latMin_;
      cartView("Y_MAX_LATITUDE")  = latMax_;
      cartView("Y_MIN_LONGITUDE") = lonMax_;
      cartView("Y_MAX_LONGITUDE") = lonMin_;

      cartView("X_AXIS_TYPE") = "DATE";
      if ( bAutoDate_ && bDataUnit_ )
      {
         cartView("X_AUTOMATIC") = "ON";
         bUpdateDate_ = false;
      }
      else
      {
         cartView("X_AUTOMATIC") = "OFF";
         cartView("X_DATE_MIN")  = dateMin_.c_str();
         cartView("X_DATE_MAX")  = dateMax_.c_str();
         if ( !bAutoDate_ )
            bUpdateDate_ = false;
      }

      // Copy axis definition
      cartView.setValue("HORIZONTAL_AXIS",viewRequest_.getSubrequest("TIME_AXIS"));
      cartView.setValue("VERTICAL_AXIS",viewRequest_.getSubrequest("GEO_AXIS"));
   }
   else
   {
      cartView("X_AUTOMATIC")     = "OFF";
      cartView("X_AXIS_TYPE")     = "GEOLINE";
      cartView("X_MIN_LATITUDE")  = latMax_;
      cartView("X_MAX_LATITUDE")  = latMin_;
      cartView("X_MIN_LONGITUDE") = lonMin_;
      cartView("X_MAX_LONGITUDE") = lonMax_;

      cartView("Y_AXIS_TYPE") = "DATE";
      if ( bAutoDate_ && bDataUnit_ )
      {
         cartView("Y_AUTOMATIC") = "ON";
         bUpdateDate_ = false;
      }
      else
      {
         cartView("Y_AUTOMATIC") = "OFF";
         cartView("Y_DATE_MIN")  = dateMin_.c_str();
         cartView("Y_DATE_MAX")  = dateMax_.c_str();
         if ( !bAutoDate_ )
            bUpdateDate_ = false;
      }

      // Copy axis definition
      cartView.setValue("HORIZONTAL_AXIS",viewRequest_.getSubrequest("GEO_AXIS"));
      cartView.setValue("VERTICAL_AXIS",viewRequest_.getSubrequest("TIME_AXIS"));
   }
cartView.print();
}

void HovmoellerView::UpdateViewArea ( MvRequest& cartView )
{
cartView.print();
   // Get geo axis
   MvRequest geoAxisReq = viewRequest_.getSubrequest("GEO_AXIS");
   if ( !geoAxisReq )
      geoAxisReq.setVerb("MAXIS");

   // Translate Geographical and Time coordinates
   string dir = (const char*)viewRequest_("AVERAGE_DIRECTION");
   int hAxis, vAxis;  // 1 - time, 2 - latitude, 3 - longitude
   if ( swapAxes_ )
   {
      hAxis = ( dir == "EAST_WEST" ) ? 2 : 1;
      vAxis = ( dir == "EAST_WEST" ) ? 1 : 3;
   }
   else
   {
      hAxis = ( dir == "EAST_WEST" ) ? 1 : 3;
      vAxis = ( dir == "EAST_WEST" ) ? 2 : 1;
   }

   if ( hAxis == 1 ) // Horizontal time axis
   {
      cartView("X_AXIS_TYPE") = "DATE";
      if ( bAutoDate_ && bDataUnit_ )
      {
         cartView("X_AUTOMATIC") = "ON";
         bUpdateDate_ = false;
      }
      else
      {
         cartView("X_AUTOMATIC") = "OFF";
         cartView("X_DATE_MIN")  = dateMin_.c_str();
         cartView("X_DATE_MAX")  = dateMax_.c_str();
         if ( !bAutoDate_ )
            bUpdateDate_ = false;
      }

      geoAxisReq("AXIS_ORIENTATION") = "VERTICAL";
      if ( vAxis == 2 ) // vertical latitude axis
      {
         geoAxisReq("AXIS_TICK_LABEL_TYPE") = "LATITUDE";
         cartView("Y_MIN") = latMin_;
         cartView("Y_MAX") = latMax_;
      }
      else // vertical longitude axis
      {
         cartView("Y_MIN")  = lonMin_;
         cartView("Y_MAX")  = lonMax_;
         geoAxisReq("AXIS_TICK_LABEL_TYPE") = "LONGITUDE";
      }

      // Set axes definitions
      cartView.setValue("HORIZONTAL_AXIS",viewRequest_.getSubrequest("TIME_AXIS"));
      cartView.setValue("VERTICAL_AXIS",geoAxisReq);
   }
   else if ( hAxis == 2 ) // Horizontal latitude axis
   {
      cartView("X_MIN") = latMin_;
      cartView("X_MAX") = latMax_;
      geoAxisReq("AXIS_ORIENTATION")     = "HORIZONTAL";
      geoAxisReq("AXIS_TICK_LABEL_TYPE") = "LATITUDE";

      cartView("Y_AXIS_TYPE") = "DATE";
      if ( bAutoDate_ && bDataUnit_ )
      {
         cartView("Y_AUTOMATIC") = "ON";
         bUpdateDate_ = false;
      }
      else
      {
         cartView("Y_AUTOMATIC") = "OFF";
         cartView("Y_DATE_MIN")  = dateMin_.c_str();
         cartView("Y_DATE_MAX")  = dateMax_.c_str();
         if ( !bAutoDate_ )
            bUpdateDate_ = false;
      }

      // Set axes definitions
      cartView.setValue("HORIZONTAL_AXIS",geoAxisReq);
      cartView.setValue("VERTICAL_AXIS",viewRequest_.getSubrequest("TIME_AXIS"));
   }
   else   // Horizontal longitude axis
   {
      cartView("X_MIN") = lonMin_;
      cartView("X_MAX") = lonMax_;
      geoAxisReq("AXIS_ORIENTATION")     = "HORIZONTAL";
      geoAxisReq("AXIS_TICK_LABEL_TYPE") = "LONGITUDE";

      cartView("Y_AXIS_TYPE") = "DATE";
      if ( bAutoDate_ && bDataUnit_ )
      {
         cartView("Y_AUTOMATIC") = "ON";
         bUpdateDate_ = false;
      }
      else
      {
         cartView("Y_AUTOMATIC") = "OFF";
         cartView("Y_DATE_MIN")  = dateMin_.c_str();
         cartView("Y_DATE_MAX")  = dateMax_.c_str();
         if ( !bAutoDate_ )
            bUpdateDate_ = false;
      }

      // Set axes definitions
      cartView.setValue("HORIZONTAL_AXIS",geoAxisReq);
      cartView.setValue("VERTICAL_AXIS",viewRequest_.getSubrequest("TIME_AXIS"));
   }
cartView.print();
}

void HovmoellerView::UpdateViewVert ( MvRequest& cartView )
{
cartView.print();
   // Vertical scaling mode
   const char* vs = "REGULAR";
   if ( (const char*)viewRequest_("VERTICAL_SCALING") && strcmp((const char*)viewRequest_("VERTICAL_SCALING"),"LOG") == 0 )
      vs = "LOGARITHMIC";

   // Translate Vertical and Time coordinates
   cartView("X_AXIS_TYPE") = "DATE";
   if ( bAutoDate_ && bDataUnit_ )
   {
      cartView("X_AUTOMATIC") = "ON";
      bUpdateDate_ = false;
   }
   else
   {
      cartView("X_AUTOMATIC") = "OFF";
      cartView("X_DATE_MIN")  = dateMin_.c_str();
      cartView("X_DATE_MAX")  = dateMax_.c_str();
      if ( !bAutoDate_ )
         bUpdateDate_ = false;
   }

   cartView("Y_AUTOMATIC") = "OFF";
   cartView("Y_AXIS_TYPE") = vs;
   cartView("Y_MIN")       = yMin_;
   cartView("Y_MAX")       = yMax_;

   // Copy axis definition
   cartView.setValue("HORIZONTAL_AXIS",viewRequest_.getSubrequest("TIME_AXIS"));
   cartView.setValue("VERTICAL_AXIS",viewRequest_.getSubrequest("VERTICAL_AXIS"));
cartView.print();
}

void HovmoellerView::ApplicationInfo( const MvRequest& in)
{
   // Check if there is a Data Unit attached
   MvRequest req = in;
   while (req)
   {
      if ( ObjectList::IsDataUnit(req.getVerb()) )
      {
         bDataUnit_ = true;
         return;
      }
      req.advance();
   }
   
   bDataUnit_ = false;
   return;
}
