/***************************** 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 "ThermoView.h"
#include "MvRequestUtil.hpp"
#include "ObjectList.h"
#include "PlotMod.h"
#include "PmContext.h"
#include "Root.h"

// Thermo types
static const string THERMOTEPHI   = "TEPHIGRAM";
static const string THERMOSKEWT   = "SKEW_T";
static const string THERMOEMAGRAM = "EMAGRAM";

// Thermo data types
map<string,string> create_dataTypeMap()
{
   map<string,string> m;
   m["GRIB"] = "GRIB_THERMO";
   m["BUFR"] = "BUFR_THERMO";
   m["ODB"]  = "ODB_THERMO";
   return m;
}

static map<string,string> DATA_TYPE = create_dataTypeMap();

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

static ThermoViewFactory thermoViewFactoryInstance;

PlotModView*
ThermoViewFactory::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 Thermo view
   return new ThermoView ( page, expReq, setupRequest );
}

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

ThermoView::ThermoView ( Page& owner,
                         const MvRequest& viewRequest,
                         const MvRequest& setupRequest ):
               CommonXSectView ( owner, viewRequest, setupRequest )
{
   SetVariables(viewRequest,true);
}

ThermoView::ThermoView ( const ThermoView &old ) :
               CommonXSectView(old)
{
   type_ = old.type_;
}

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

   return name;
}

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

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

bool ThermoView::UpdateView ()
{
   // It has already been updated
   if ( string(viewRequest_.getVerb()) == CARTESIANVIEW )
      return true;

   // Create a Cartesian View request related to Tephigram
   // Magics requires that the value for parameter map_projection be 
   // in lower case
   MvRequest cartView("CARTESIANVIEW");
   string sdata = type_;
   std::transform(sdata.begin(), sdata.end(), sdata.begin(), ::tolower);
   cartView("MAP_PROJECTION") = sdata.c_str();

   // Translate axes min/max values
   // Check where the values should be taken from
   if ( (const char*)viewRequest_("_DEFAULT") &&
        (int)viewRequest_("_DEFAULT") == 1 &&
        (const char*)viewRequest_("_DATAATTACHED") &&
        strcmp((const char*)viewRequest_("_DATAATTACHED"),"YES") == 0
      )
   {
      cartView("X_AUTOMATIC") = "on";
      cartView("Y_AUTOMATIC") = "on";
   }
   else
   {
      cartView("X_AUTOMATIC") = "off";
      cartView("Y_AUTOMATIC") = "off";

      // Get min/max axes coordinates
      cartView("X_MIN") = viewRequest_("MINIMUM_TEMPERATURE");
      cartView("X_MAX") = viewRequest_("MAXIMUM_TEMPERATURE");
      cartView("Y_MIN") = viewRequest_("BOTTOM_PRESSURE");
      cartView("Y_MAX") = viewRequest_("TOP_PRESSURE");
   }

   // Copy ThermoGrid definition
   MvRequest reqGrid = viewRequest_.getSubrequest("THERMO_GRID");
   if ( reqGrid )
      cartView("THERMO_GRID") = reqGrid;

   // Remove some unnecessary Page and SubPage parameters and
   // copy the remaining ones to the new View
   if ( (const char*)viewRequest_("PAGE_FRAME") &&
        strcmp((const char*)viewRequest_("PAGE_FRAME"),"OFF") == 0 )
      RemoveParameters( viewRequest_, "PAGE_FRAME" );

   if ( (const char*)viewRequest_("PAGE_ID_LINE") &&
        strcmp((const char*)viewRequest_("PAGE_ID_LINE"),"OFF") == 0 )
      RemoveParameters( viewRequest_, "PAGE_ID_LINE" );

   if ( (const char*)viewRequest_("SUBPAGE_FRAME") &&
        strcmp((const char*)viewRequest_("SUBPAGE_FRAME"),"OFF") == 0 )
      RemoveParameters( viewRequest_, "SUBPAGE_FRAME" );

   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);

   return true;
}

bool ThermoView::UpdateView ( MvRequest& viewRequest )
{
   // Get the original ThermoView request in case it has already
   // been translated to a CartesianView request
   MvRequest tview;
   string tverb;
   if ( !ObjectList::IsThermoView( viewRequest_.getVerb() ) )
   {
      // Retrieve the original ThermoView request
      tview = viewRequest_("_ORIGINAL_REQUEST");
   }
   else
      tview = viewRequest_;

   // Changing to another View is not allowed
   if ( !ObjectList::IsThermoView( viewRequest.getVerb() ) )
   {
      // Changing View is disabled at the moment
      char text[128];
      sprintf(text,"Changing View (%s to %s) is currently disabled",tview.getVerb(),viewRequest.getVerb() );
      PlotMod::Instance().errorMessage(text);
      return false;
   }

   // If the request came from an Application then ignore the new
   // View definition and use the current View to draw the new plot
   MvRequest contextReq = viewRequest.getSubrequest("_CONTEXT");
   if ( contextReq )
      return true;;

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

#if 0
   // FAMI20180426 
   // This consistency check was removed to allow users to change View
   // without constraints.

   // Parameters used to compute the thermo data
   // must have the same value
   string param;
   if ( !ConsistencyCheck(tview,req,param) )
   {
      char text[128];
      sprintf(text,"Changing View not allowed.\nParameter %s must have the same value (%s , %s)", param.c_str(), 
      (const char*)tview(param.c_str()),(const char*)req(param.c_str()) );
      PlotMod::Instance().errorMessage(text);
      return false;
   }
#endif

   // Update the current view
   type_ = (const char*)req("TYPE");

   // Update request
   // Owner().UpdateView(req);  // it will be updated later (CommonXSectView)

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

   // Redraw this page
   //Owner().RedrawIfWindow();
   //Owner().NotifyObservers();

   //Owner().InitZoomStacks();

   return true;
}

void ThermoView::ApplicationInfo( const MvRequest& req)
{
   // Get the VIEW request
   MvRequest viewReq;
   if ( ObjectList::IsView(req.getVerb()) )
      viewReq = req;
   else
   {
      // Try to find a hidden VIEW in the request
      viewReq = req.getSubrequest("_VIEW_REQUEST");
      if ( !viewReq )
      {
         // Nothing to be done
         return;
      }
   }

   return;
}

void ThermoView::Drop ( PmContext& context )
{
   // Before processing the drop (parent's function), check for icon drops
   // specific to this class:
   // a) dataUnit: initializes the type of the ThermoData module to be called
   // b) thermogrid: update the View
   // c) thermoview: update the View
   MvRequest dropRequest = context.InRequest();
   bool first = true;
   while ( dropRequest )
   {
      // Data unit
      string verb = dropRequest.getVerb();
      if ( first && ObjectList::IsDataUnit ( verb.c_str() ) == true )
      {
         MvRequest req = dropRequest.justOneRequest();

         if ( !this->SetThermoType(req) )
            return;   // suitable thermo type not found

         first = false;
      }
      // Thermo grid
      else if ( ObjectList::IsThermoGrid ( verb.c_str() ) == true )
      {
         MvRequest req = dropRequest.justOneRequest();
         this->UpdateThermoGrid ( req );
      }
      else if ( ObjectList::IsThermoView( verb.c_str() ) )
      {
         // First: check and update the internal ThermoView structure
         // Second: ask CommonXSectView::Drop to update the Page
         MvRequest req = dropRequest.justOneRequest();
         if ( !this->UpdateView ( req ) )
            return;
      }

      dropRequest.advance();
   }

   // Call the parent to perform the Drop request
   CommonXSectView::Drop(context);

   return;
}

void ThermoView::UpdateThermoGrid ( MvRequest& req )
{
   // Update new thermo grid definition. The View will be updated later.
   viewRequest_("THERMO_GRID") = req;
}

bool ThermoView::SetThermoType( MvRequest& req )
{
   string thermoType;
   string verb = req.getVerb();
   map<string,string>::iterator it;
   it = DATA_TYPE.find(verb.c_str());
   if ( it != DATA_TYPE.end()  )
      thermoType = it->second;
   else if ( verb == "NETCDF" )
   {
      if ( (const char*)req("_VERB") )
         thermoType = (const char*)req("_VERB");
      else if ( (const char*)req("_CLASS") )
         thermoType = (const char*)req("_CLASS");
      else
      {
         // Thermo type not found
         char text[128];
         sprintf( text,"Suitable thermo type not found: %s ",verb.c_str() );
         PlotMod::Instance().errorMessage(text);
         return false;
      }
   }
   else if ( verb.find("NETCDF") != std::string::npos )
   {
      // The application has been already called to process the data.
      // These are a set of requests containing the NETCDF_XY_*;
      // therefore it will be processed withoug calling again the
      // application. There is no need to set the ApplicationName.
      return true;
   }
   else if ( verb.find("INPUT") != std::string::npos )
   {
      // The application has been already called to process the data.
      // These are a set of requests containing the _XY_*;
      // therefore it will be processed withoug calling again the
      // application. There is no need to set the ApplicationName.
      return true;
   }
   else
   {
      // Thermo type not found
      char text[128];
      sprintf( text,"Suitable thermo type not found: %s ",verb.c_str() );
      PlotMod::Instance().errorMessage(text);
      return false;
   }

   ApplicationName(thermoType);
   return true;
}

bool ThermoView::ConsistencyCheck(MvRequest& req1, MvRequest& req2, string& param)
{
   // Build a list of parameters to be checked
   std::vector<string> vparam;
   vparam.push_back("POINT_SELECTION");

   if ( (const char*)req1("COORDINATES") )
      vparam.push_back("COORDINATES");
   if ( (const char*)req1("AREA_AVERAGE") )
      vparam.push_back("AREA_AVERAGE");
   if ( (const char*)req1("STATION") )
      vparam.push_back("STATION");

   vparam.push_back("POINT_EXTRACTION");
   vparam.push_back("DEW_POINT_FORMULATION");
   vparam.push_back("TEMPERATURE_PARAM");
   vparam.push_back("SPECIFIC_HUMIDITY_PARAM");
   vparam.push_back("LNSP_PARAM");
   vparam.push_back("U_WIND_PARAM");
   vparam.push_back("V_WIND_PARAM");

   // Check one by one because we want to know which one is different
   return req1.checkParameters(req2,vparam,param);
}
