/***************************** 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 <MvRequestUtil.hpp>
#include "Assertions.hpp"

#include "MagicsTranslator.h"
#include "ObjectList.h"
#include "PlotModConst.h"

// =========================================================
//
// Singleton objects for each class
AxisTranslator         axisTranslatorInstance    ( "PAXIS" );
BufrTranslator         bufrTranslatorInstance    ( "BUFR" );
CoastTranslator        coast1TranslatorInstance  ( "PCOAST" );
//CoastTranslator        coast2TranslatorInstance  ( "GEO_COASTLINE" );
//CoastTranslator        coast3TranslatorInstance  ( "GEO_LAND_SHADE" );
//CoastTranslator        coast4TranslatorInstance  ( "GEO_SEA_SHADE" );
//CoastTranslator        coast5TranslatorInstance  ( "GEO_BOUNDARIES" );
//CoastTranslator        coast6TranslatorInstance  ( "GEO_CITIES" );
//CoastTranslator        coast7TranslatorInstance  ( "GEO_RIVERS" );
//CoastTranslator        coast8TranslatorInstance  ( "GEO_GRID" );
ContTranslator         contTranslatorInstance    ( "PCONT" );
GeopointsTranslator    geopTranslatorInstance    ( "GEOPOINTS" );
GraphTranslator        graphTranslatorInstance   ( "PGRAPH" );
GribTranslator         gribTranslatorInstance    ( "GRIB" );
GribVectorsTranslator  iivectorsTranslatorInstance ( "GRIB_VECTORS" );
NetCDFTranslator       ncGeoPTranslatorInstance  ( "NETCDF_GEO_POINTS"  );
NetCDFTranslator       ncGeoVTranslatorInstance  ( "NETCDF_GEO_VECTORS" );
NetCDFTranslator       ncGeoMTranslatorInstance  ( "NETCDF_GEO_MATRIX"  );
NetCDFTranslator       ncMatTranslatorInstance   ( "NETCDF_XY_MATRIX"   );
NetCDFTranslator       ncXYVTTranslatorInstance  ( "NETCDF_XY_VECTORS"   );
NetCDFTranslator       ncXYPTranslatorInstance   ( "NETCDF_XY_POINTS"   );
ObsTranslator          obsTranslatorInstance     ( "MOBS" );
PageTranslator         pageTranslatorInstance    ( "PAGE" );
SymbolTranslator       symbolTranslatorInstance  ( "PSYMB" );
TextTranslator         textTranslatorInstance	 ( "PTEXT" );
TextTranslator         mtextTranslatorInstance   ( "MTEXT" );
ThermoTranslator       mthermoTranslatorInstance ( "MTHERMO" );
VoidTranslator         mboxplotTranslatorInstance( "MBOXPLOT" );
VoidTranslator         input2TranslatorInstance  ( "INPUT_XY_POINTS" );
VoidTranslator         input3TranslatorInstance  ( "INPUT_GEO_POINTS" );
VoidTranslator         input4TranslatorInstance  ( "INPUT_XY_VECTORS" );
VoidTranslator         input5TranslatorInstance  ( "INPUT_GEO_VECTORS" );
VoidTranslator         input6TranslatorInstance  ( "INPUT_XY_BINNING" );
VoidTranslator         input7TranslatorInstance  ( "INPUT_GEO_BINNING" );
VoidTranslator         input8TranslatorInstance  ( "INPUT_XY_BOXPLOT" );
VoidTranslator         input9TranslatorInstance  ( "INPUT_XY_AREA" );
VoidTranslator         mcoastTranslatorInstance  ( "MCOAST" );
MContTranslator        mcontTranslatorInstance   ( "MCONT" );
VoidTranslator         mgraphTranslatorInstance  ( "MGRAPH" );
VoidTranslator         mlegendTranslatorInstance ( "MLEGEND" );
VoidTranslator         msymbolTranslatorInstance ( "MSYMB" );
VoidTranslator         mwindTranslatorInstance   ( "MWIND" );
VoidTranslator         odbGeoPTranslatorInstance ( "ODB_GEO_POINTS" );
VoidTranslator         odbGeoVTranslatorInstance ( "ODB_GEO_VECTORS" );
VoidTranslator         odbXyPTranslatorInstance  ( "ODB_XY_POINTS" );
VoidTranslator         odbXyVTranslatorInstance  ( "ODB_XY_VECTORS" );
VoidTranslator         odbXyMTranslatorInstance  ( "ODB_XY_BINNING" );
ImportTranslator       mimportTranslatorInstance ( "MIMPORT" );
VoidTranslator         prasterTranslatorInstance ( "PRASTER" );
VoidTranslator         prloopTranslatorInstance  ( "PRASTERLOOP" );
VoidTranslator         pvoidTranslatorInstance   ( "PVOID" );
VoidTranslator         table1TranslatorInstance  ( "TABLE_HISTOGRAM" );
VoidTranslator         table2TranslatorInstance  ( "TABLE_XY_POINTS" );
VoidTranslator         table3TranslatorInstance  ( "TABLE_GEO_POINTS" );
VoidTranslator         table4TranslatorInstance  ( "TABLE_XY_VECTORS" );
VoidTranslator         table5TranslatorInstance  ( "TABLE_GEO_VECTORS" );
VoidTranslator         table6TranslatorInstance  ( "TABLE_XY_BINNING" );
VoidTranslator         table7TranslatorInstance  ( "TABLE_GEO_BINNING" );
VoidTranslator         table8TranslatorInstance  ( "TABLE_XY_BOXES" );
VoidTranslator         table9TranslatorInstance  ( "TABLE_GEO_BOXES" );
VoidTranslator         table10TranslatorInstance ( "TABLE_HOR_BAR" );
VoidTranslator         table11TranslatorInstance ( "TABLE_VER_BAR" );
WindTranslator         windTranslatorInstance    ( "PWIND" );

#if 0
GeoLayersTranslator    geolTranslatorInstance    ( "GEOLAYERS" );
ImageTranslator   imageTranslatorInstance ( "IMAGE"  );
PImageTranslator  pimageTranslatorInstance( "PMIMAGE" );
#endif

//====== Helper function for generating subpage size ==
static void generateSubpageSize(const MvRequest&, MvRequest&, double,double,bool expanded = false);

//========================================================
//
// Default Object ( required by the "prototype" template )
MagicsTranslator&
MagicsTranslatorTraits::DefaultObject()
{
   return GribTranslator::Instance();
}

//==================================================
//
//  Methods for the MagicsTranslator class
//
MagicsTranslator::~MagicsTranslator()
{
   // Empty
}

//=================================================
// GRIB Translator

MagicsTranslator& 
GribTranslator::Instance()
{
   return gribTranslatorInstance;
}

MvRequest
GribTranslator::Execute ( const MvRequest& dataUnitRequest, const MvRequest& dataInfoRequest, const MvRequest& visdefRequest )
{
   MvRequest magicsRequest( "PGRIB" );

   // Set the common parameters of all kind of grib data

   // Set the input file name and Magics internal parameters
   const char* dataPath = dataUnitRequest("PATH");
   magicsRequest ( "GRIB_INPUT_TYPE"        ) = "FILE";
   magicsRequest ( "GRIB_INPUT_FILE_NAME"   ) = dataPath;
   magicsRequest ( "GRIB_FILE_ADDRESS_MODE" ) = "BYTE_OFFSET";
   magicsRequest ( "GRIB_SPECIFICATION"     ) = "OFF";
   CopySomeParameters( dataUnitRequest,magicsRequest,"$METVIEW_" );

   // Set GRIB_* parameters selected by the user.
   // As two fields can be drawn within a 'Newpage' Magics
   // command, the request must be expanded in order to set/reset
   // all the parameters. This will avoid the use of parameters
   // that were set for the previous plot but not set for this one.
   MvRequest expandRequest = ObjectList::ExpandRequest (visdefRequest,EXPAND_DEFAULTS);
   CopySomeParameters( expandRequest,magicsRequest,"GRIB_" );
   CopySomeParameters( expandRequest,magicsRequest,"INPUT_FIELD_SUPPRESS" );

   // Set the specific parameters for each kind of data
   // If it is a single field:
   // GRIB_DIMENSION = 1, GRIB_POSITION = pos
   // (or) GRIB_DIMENSION = 2, GRIB_POSITION_1 = pos1, GRIB_POSITION_2 = pos2
   // If it is all fields:
   // GRIB_DIMENSION = 1
   // (or)GRIB_ DIMENSION = 2
   // If it is subset of fields:
   // GRIB_DIMENSION = 1, GRIB_POSITION = list
   // (or) GRIB_DIMENSION = 2, GRIB_POSITION_1 = list1, GRIB_POSITION_2 = list2

   // Metview's "GRIB_SCALING_OF_RETRIEVED_FIELDS" is equivalent to Magics' "GRIB_AUTOMATIC_SCALING"
   // Metview's "GRIB_SCALING_OF_DERIVED_FIELDS"   is equivalent to Magics' "GRIB_AUTOMATIC_DERIVED_SCALING"
   const char *scalingOfRetrievedFields = (const char*) expandRequest("GRIB_SCALING_OF_RETRIEVED_FIELDS");
   if (scalingOfRetrievedFields)
   {
      magicsRequest.unsetParam("GRIB_SCALING_OF_RETRIEVED_FIELDS");
      magicsRequest("GRIB_AUTOMATIC_SCALING") = scalingOfRetrievedFields;
   }

   const char *scalingOfDerivedFields = (const char*) expandRequest("GRIB_SCALING_OF_DERIVED_FIELDS");
   if (scalingOfDerivedFields)
   {
      magicsRequest.unsetParam("GRIB_SCALING_OF_DERIVED_FIELDS");
      magicsRequest("GRIB_AUTOMATIC_DERIVED_SCALING") = scalingOfDerivedFields;
   }

   // Copy GRIB_* parameters
   CopySomeParameters(dataInfoRequest,magicsRequest,"GRIB_");

   // This is an image field data
   if ( strcmp( (const char*)dataInfoRequest.getVerb(),"IMAGE") == 0)
   {
      magicsRequest ( "GRIB_AUTOMATIC_SCALING" ) = "OFF" ;
      magicsRequest ( "SUBPAGE_MAP_PROJECTION" ) = "INPUT_IMAGE";
   }

   // Handle GRID_SHADDING option: this is a Magics requirement
   if ( (const char*)expandRequest("CONTOUR_SHADE") &&
        strcmp(expandRequest("CONTOUR_SHADE"),"ON") == 0 &&
        (const char*)expandRequest("CONTOUR_SHADE_TECHNIQUE") &&
        strcmp(expandRequest("CONTOUR_SHADE_TECHNIQUE"),"GRID_SHADING") == 0
      )
         magicsRequest("GRIB_INTERPOLATION_METHOD") = "NEAREST";

   // Copy hidden parameters
   magicsRequest ("_NAME" ) = (const char*)dataUnitRequest ("_NAME");
   magicsRequest ("_CLASS") = (const char*)dataUnitRequest ("_CLASS");
   magicsRequest ("_ID"   ) = (const char*)dataUnitRequest ("_ID");

   return magicsRequest;
} 

#if 0
//=============================================================
//
// Image Translator
//
MvRequest
ImageTranslator::Execute ( const MvRequest& dataUnitRequest, 
			   long dataUnitIndex,
			   const MvRequest& dataInfoRequest,
			   MvRequest& )
{

	MvRequest magicsRequest;
	const char *dataIdent = dataInfoRequest ("IDENT");

	require ( dataIdent != NULL );	// this is  an IMAGE

	magicsRequest.setVerb("PGRIB");

	// Set the input file name and field position
	const char* dataPath = dataUnitRequest("PATH");
	
	magicsRequest ( "GRIB_INPUT_TYPE"        ) = "FILE";
	magicsRequest ( "GRIB_INPUT_FILE_NAME"   ) = dataPath;
	magicsRequest ( "GRIB_POSITION"    ) = (int) dataUnitIndex;
	magicsRequest ( "GRIB_FILE_ADDRESS_MODE" ) = "BYTE_OFFSET";
	magicsRequest ( "GRIB_SPECIFICATION"     ) = "OFF";
	magicsRequest ( "GRIB_SCALING"           ) = "OFF" ;

	magicsRequest ( "SUBPAGE_MAP_PROJECTION" ) = "INPUT_IMAGE";

//magicsRequest ( "MAP_COASTLINE_LAND_SHADE_COLOUR" ) = "BEIGE";
//magicsRequest ( "MAP_COASTLINE_SEA_SHADE_COLOUR" ) = "MAGENTA";

return magicsRequest;
} 
#endif

//==============================================================
//
// Bufr Translator
//
MvRequest
BufrTranslator::Execute ( const MvRequest& dataUnitRequest, const MvRequest& dataInfoRequest, const MvRequest& )
{
   MvRequest magicsRequest;
   const char* dataPath;

   // The real path is normally in the dataInfoRequest,
   // where the name of the generated file for this
   // interval is. If no grouping/matching is done,
   // use the original datarequest.
   dataPath = dataInfoRequest("PATH");
   if ( !dataPath )
      dataPath = dataUnitRequest("PATH");

   magicsRequest.setVerb("BUFR");
   magicsRequest("OBS_INPUT_TYPE"      ) = "FILE";
   magicsRequest("OBS_INPUT_FORMAT"    ) = "BUFR";
   magicsRequest("OBS_INPUT_FILE_NAME" ) = dataPath;
   magicsRequest("OBS_INPUT_FILE_UNIT" ) = 23;
   magicsRequest("TEXT_LINE_1")=  "Observations";
   magicsRequest("TEXT_LINE_COUNT")=  1;

   return magicsRequest;
}

//===================================================
// Obs Translator, for setting how the obs. should be plotted.
//
MvRequest
ObsTranslator::Execute ( const MvRequest& req )
{
   return req;
}

//=================================================================
//
// Coast Translator
//
MvRequest
CoastTranslator::Execute ( const MvRequest& mapviewRequest )
{
   // Replace verb
   const char* verb = mapviewRequest.getVerb();
   MvRequest magicsRequest = mapviewRequest;
   magicsRequest.setVerb("MCOAST");

   // Keep backwards compatibility
   if  ( strcmp ( verb, "PCOAST" ) == 0 )
      return magicsRequest;

#if 0
   // Since Coast and Grid are set to ON by default, switch them to OFF according
   // to the relevant Geo action. This will avoid them to be drawn several times.

   //IMPORTANTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT!!!!!!!!!!!!!
   // SWITCH BACK TO OFF BEFORE SUBMITING.
   // IT IS ON BECAUSE MAGICS DOES NOT PLOT IT CORRECTTLY
   if ( strcmp ( verb, "GEO_COASTLINE" ) != 0 )
      magicsRequest("MAP_COASTLINE") = "on"; //"OFF";

   if ( strcmp ( verb, "GEO_GRID" ) != 0 )
   {
      magicsRequest("MAP_GRID")      = "OFF";
      magicsRequest("MAP_LABEL")     = "OFF";
   }
#endif
   return magicsRequest;
}

//=================================================================
//
// --- METHOD : Contour Translator
//
// --- PURPOSE: Translate a metview Contour request into
//              a MAGICS contour request
// 
MvRequest
ContTranslator::Execute ( const MvRequest& visdefRequest )
{
   // Check LEGEND_ENTRY subrequest
   MvRequest magicsRequest ("PCONT");
   MvRequest tmpRequest ("PCONT");
   MvRequest legRequest = visdefRequest.getSubrequest("LEGEND_ENTRY");
   if ( legRequest )
   {
      // Strip out the "GRIB_" and the "INPUT_FIELD_SUPPRESS" parts
      MvRequest tmp1Request ("PCONT");
      CopyAndRemoveParameters ( visdefRequest, tmpRequest, "INPUT_FIELD_SUPPRESS" );
      CopyAndRemoveParameters ( tmpRequest, tmp1Request, "GRIB_" );

      // Translate the LEGEND_ENTRY subrequest
      CopyAndRemoveParameters(tmp1Request,magicsRequest,"LEGEND_ENTRY");
      CopySomeParameters(legRequest,magicsRequest,"LEGEND");
   }
   else
   {
      // Strip out the "GRIB_" and the "INPUT_FIELD_SUPPRESS" parts
      CopyAndRemoveParameters ( visdefRequest, tmpRequest, "INPUT_FIELD_SUPPRESS" );
      CopyAndRemoveParameters ( tmpRequest, magicsRequest, "GRIB_" );
   }

   // Handle GRID_SHADDING option: this is a Magics requiment.
   if ( (const char*)magicsRequest("CONTOUR_SHADE") &&
        strcmp(magicsRequest("CONTOUR_SHADE"),"ON") == 0 &&
        (const char*)magicsRequest("CONTOUR_SHADE_TECHNIQUE") &&
        strcmp(magicsRequest("CONTOUR_SHADE_TECHNIQUE"),"GRID_SHADING") == 0
      )
         magicsRequest("CONTOUR_METHOD") = "LINEAR";

   return magicsRequest;
}

MvRequest
MContTranslator::Execute ( const MvRequest& visdefRequest )
{
   MvRequest magicsRequest = visdefRequest;

   // Handle GRID_SHADDING option: this is a Magics requiment.
   if ( (const char*)magicsRequest("CONTOUR_SHADE") &&
        strcmp(magicsRequest("CONTOUR_SHADE"),"ON") == 0 &&
        (const char*)magicsRequest("CONTOUR_SHADE_TECHNIQUE") &&
        strcmp(magicsRequest("CONTOUR_SHADE_TECHNIQUE"),"GRID_SHADING") == 0
      )
         magicsRequest("CONTOUR_METHOD") = "LINEAR";

   return magicsRequest;
}

MvRequest
PageTranslator::Execute ( const MvRequest& viewRequest )
{
   // Set layout
   MvRequest metviewRequest = viewRequest;  // values needed to be updated internally
   MvRequest magicsRequest("PAGE");
   double width  = metviewRequest ( "_WIDTH"  );
   double height = metviewRequest ( "_HEIGHT" );
   magicsRequest ( "PAGE_X_LENGTH" ) = width;
   magicsRequest ( "PAGE_Y_LENGTH" ) = height;

   magicsRequest ( "PAGE_X_POSITION" ) =  (double)metviewRequest("_X_ORIGIN");
   magicsRequest ( "PAGE_Y_POSITION" ) =  (double)metviewRequest("_Y_ORIGIN");

   // Set/Remove page ID line. By default, Metview does not use this parameter
   magicsRequest ( "PAGE_ID_LINE") = "OFF";
   CopySomeParameters(metviewRequest, magicsRequest, "PAGE_FRAME");
   CopySomeParameters(metviewRequest, magicsRequest, "SUBPAGE_FRAME");
   CopySomeParameters(metviewRequest, magicsRequest, "SUBPAGE_METADATA");
   CopySomeParameters(metviewRequest, magicsRequest, "SUBPAGE_CLIPPING");
   if ( (const char*)metviewRequest( "SUBPAGE_VERTICAL_AXIS_WIDTH" ) )
      magicsRequest( "SUBPAGE_VERTICAL_AXIS_WIDTH" ) = (double)metviewRequest( "SUBPAGE_VERTICAL_AXIS_WIDTH" );

   if ( (const char*)metviewRequest( "SUBPAGE_HORIZONTAL_AXIS_HEIGHT" ) )
      magicsRequest( "SUBPAGE_HORIZONTAL_AXIS_HEIGHT" ) = (double)metviewRequest( "SUBPAGE_HORIZONTAL_AXIS_HEIGHT" );

   // Set zoom definition
   if ( (const char*)metviewRequest( "_ZOOM_DEFINITION" ) )
   {
      magicsRequest( "SUBPAGE_MAP_JSON_DEFINITION" ) = (const char*)metviewRequest( "_ZOOM_DEFINITION" );
      metviewRequest.unsetParam("_ZOOM_DEFINITION");
   }

   // Set subpage sizes
   generateSubpageSize(metviewRequest,magicsRequest,width,height);

   // Include the MAP information
   // Test if the projection exists - if not, expand the request to
   // search for the default projection -- if not, set projection to
   // NONE
   const char* projection = metviewRequest( "MAP_PROJECTION" );
   MvRequest tmpReq;
   if ( !projection )
   {
      tmpReq = ObjectList::ExpandRequest(metviewRequest,EXPAND_DEFAULTS);
      projection = tmpReq( "MAP_PROJECTION" );
   }

   // Temporary solution to draw cartesian view
   if (projection != 0 && (strcmp(projection,"cartesian") == 0 || strcmp(projection,"tephigram") == 0 ||  strcmp(projection,"taylor") == 0) )
   {
      // Add a new request related to the VIEW
      magicsRequest("SUBPAGE_MAP_PROJECTION") = "NEXT";
      RemoveParameters(metviewRequest,"PAGE");
      RemoveParameters(metviewRequest,"SUBPAGE");
      magicsRequest = magicsRequest + metviewRequest;
   }
   else if ( projection )
   {
      // Translate AREA parameter
      const char* area = metviewRequest( "AREA" );
      if ( area != 0 )
      {
         magicsRequest( "SUBPAGE_LOWER_LEFT_LATITUDE" ) = (double)metviewRequest ( "AREA", 0 );
         magicsRequest( "SUBPAGE_LOWER_LEFT_LONGITUDE") = (double)metviewRequest ( "AREA", 1 );
         magicsRequest( "SUBPAGE_UPPER_RIGHT_LATITUDE") = (double)metviewRequest ( "AREA", 2 );
         magicsRequest( "SUBPAGE_UPPER_RIGHT_LONGITUDE")= (double)metviewRequest ( "AREA", 3 );
      }

      // Magics sets SUBPAGE_MINIMAL_AREA to 0.1 degrees to avoid issues with zooming into infinitesimally
      // small areas in ecCharts, but we don't need to be so cautious in Metview and at least one user
      // has requested the ability to zoom into smaller areas, so we set this to 0.001.
      magicsRequest( "SUBPAGE_MINIMAL_AREA" ) = 0.001;


      // Translate MAP to SUBPAGE_MAP parameters
      CopySomeParameters ( metviewRequest,magicsRequest,"MAP_","SUBPAGE_MAP_" );
   }

#if 0 //SatelliteView is not available at the moment
	else if ( metviewRequest.getVerb() == Cached ( "SATELLITEVIEW" ) )
	{
		magicsRequest ( "SUBPAGE_MAP_PROJECTION" ) = "SATELLITE";

//		MvRequest tmpRequest = metviewRequest;

		if ( metviewRequest ( "IMAGE_SUBAREA_SELECTION" ) == ON )
		{
			magicsRequest ( "IMAGE_SUBAREA_SELECTION" ) = ON;
			CopySomeParameters( metviewRequest, magicsRequest, "SUBPAGE_MAP_CENTRE" );
			CopySomeParameters( metviewRequest, magicsRequest, "SUBPAGE_MAP_CORNER" );
		}

		CopySomeParameters( metviewRequest, magicsRequest, "INPUT_IMAGE" );
		CopySomeParameters( metviewRequest, magicsRequest, "SUBPAGE_MAP_INITIAL" );
		CopySomeParameters( metviewRequest, magicsRequest, "SUBPAGE_MAP_SUB_SAT" );
		CopySomeParameters( metviewRequest, magicsRequest, "SUBPAGE_MAP_X_EARTH_DIAMETER" );
		CopySomeParameters( metviewRequest, magicsRequest, "SUBPAGE_MAP_Y_EARTH_DIAMETER" );
		CopySomeParameters( metviewRequest, magicsRequest, "SUBPAGE_MAP_GRID_ORIENTATION" );
		CopySomeParameters( metviewRequest, magicsRequest, "SUBPAGE_MAP_CAMERA_ALTITUDE" );
	}
#endif
   else
   {
      // In case of a non MapView this parameter should be set
      magicsRequest ( "SUBPAGE_MAP_PROJECTION" ) = "NONE";
   }

   return magicsRequest;
}

//=================================================================
//
// Text Translator
//
MvRequest
TextTranslator::Execute ( const MvRequest& textsRequest )
{
   MvRequest magicsRequest = textsRequest;

   // Set text box coordinates explicitely. If these coordinates are not defined,
   // Magics will compute them by using an internal scheme. To guarantee that Magics
   // will use the Metview default values, these coordinates need to be added to the
   // request.
   if ( (const char*)magicsRequest("TEXT_MODE") && strcmp((const char*)magicsRequest("TEXT_MODE"),"POSITIONAL") == 0 )
   {
      MvRequest expRequest = ObjectList::ExpandRequest ( magicsRequest,EXPAND_DEFAULTS );
      magicsRequest("TEXT_BOX_X_POSITION") = expRequest("TEXT_BOX_X_POSITION");
      magicsRequest("TEXT_BOX_Y_POSITION") = expRequest("TEXT_BOX_Y_POSITION");
      magicsRequest("TEXT_BOX_X_LENGTH")   = expRequest("TEXT_BOX_X_LENGTH");
      magicsRequest("TEXT_BOX_Y_LENGTH")   = expRequest("TEXT_BOX_Y_LENGTH");
   }

   return magicsRequest;
}

MvRequest
AxisTranslator::Execute ( const MvRequest& metviewRequest )
{
   // Copy is needed because functions rewind and advance
   MvRequest tmpRequest = metviewRequest;
   tmpRequest.rewind();

   MvRequest magicsRequest;

   tmpRequest.rewind();
   while ( tmpRequest )
   {
      if ( tmpRequest.getVerb() == string("PAXIS") )
         magicsRequest = magicsRequest + tmpRequest.justOneRequest();

      tmpRequest.advance();
   }

   return magicsRequest;
}

//===================================================
// Graph Translator
//
MvRequest 
GraphTranslator::Execute ( const MvRequest& metviewRequest )
{
   ensure ( metviewRequest.getVerb() == string("PGRAPH") );

   MvRequest magicsRequest( "PGRAPH" );
   CopyAndRemoveParameters(metviewRequest,magicsRequest,"LEGEND_ENTRY");

   MvRequest legRequest = metviewRequest.getSubrequest("LEGEND_ENTRY");
   if ( legRequest )
      CopySomeParameters(legRequest,magicsRequest,"LEGEND");

   return magicsRequest;
}

//=================================================================
//
// Grib_Vectors Translator
//
MvRequest
GribVectorsTranslator::Execute ( const MvRequest& dataUnitRequest1, const MvRequest&, const MvRequest& )
{
   // Copy input request (this is because functions iterInit and iterGetNextValue
   // do not accept a const variable)
   MvRequest dataUnitRequest = dataUnitRequest1;

   // Get GRIB info subrequest
   MvRequest gribReq = dataUnitRequest("GRIB");

   // Create Magics request from the input requests
   MvRequest magicsRequest("PGRIB");
   magicsRequest ("GRIB_INPUT_TYPE") = "FILE";
   magicsRequest ("GRIB_INPUT_FILE_NAME") = (const char*)gribReq("PATH");
   magicsRequest ("GRIB_FILE_ADDRESS_MODE") = "BYTE_OFFSET";
   magicsRequest ("GRIB_WIND_MODE") = (const char*)dataUnitRequest("GRIB_WIND_MODE");

   // Get grib dimension list
   int cnt = dataUnitRequest.iterInit( "GRIB_DIMENSION" );
   const char* str;
   int i;
   for( i = 0; i < cnt; ++i )
   {
      dataUnitRequest.iterGetNextValue( str );
      magicsRequest.addValue ("GRIB_DIMENSION",str);
   }

   // Get grib positions list
   cnt = gribReq.iterInit("OFFSET");
   for ( i = 0; i < cnt; ++i )
   {
       gribReq.iterGetNextValue( str );
       magicsRequest.addValue("GRIB_POSITION",str);
   }

   return magicsRequest;
}

//===================================================
// Wind Translator
//
MvRequest 
WindTranslator::Execute ( const MvRequest& metviewRequest )
{
   ensure ( metviewRequest.getVerb() == string("PWIND") );

   return metviewRequest;
}

#if 0
// 
// PImage Translator
//
MvRequest
PImageTranslator::Execute ( const MvRequest& metviewRequest )
{
	MvRequest magicsRequest = metviewRequest;

	ensure ( magicsRequest.getVerb() == Cached ( "PMIMAGE" ) );
	magicsRequest.setVerb( "PIMAGE" );


	/* The parameter IMAGE_COLOUR_TABLE_TYPE was introduced
		solely for the Metview user interface, and is not
		a valid MAGICS parameter. Therefore, we remove it here
		before passing the request on to MAGICS. */

	magicsRequest.unsetParam ( "IMAGE_COLOUR_TABLE_TYPE" );

return magicsRequest;
}
#endif

MvRequest
SymbolTranslator::Execute ( const MvRequest& visdefRequest )
{
   ensure ( visdefRequest.getVerb() == string("PSYMB") );

   // Copy request
   MvRequest magicsRequest( "PSYMB" );
   CopyAndRemoveParameters(visdefRequest,magicsRequest,"LEGEND_ENTRY");

   // Check LEGEND_ENTRY subrequest
   MvRequest legRequest = visdefRequest.getSubrequest("LEGEND_ENTRY");
   if ( legRequest ) CopySomeParameters(legRequest,magicsRequest,"LEGEND");

   return magicsRequest;
}

MvRequest
GeopointsTranslator::Execute ( const MvRequest& dataUnitRequest, const MvRequest&, const MvRequest& psymb)
{
   MvRequest geo("GEOPOINTS");

   MvRequest record = dataUnitRequest.getSubrequest("RECORD");

   const char *symbType =  psymb("SYMBOL_TYPE");
   const char *tableMode = psymb("SYMBOL_TABLE_MODE");
   string type = "NUMBER";

   // If SYMBOL_TYPE is set, use the value. If SYMBOL_TABLE_MODE is also
   // set, type needs to be number, whatever SYMBOL_TYPE is set to.
   if (  symbType )
      type = symbType;

   if ( tableMode && strcmp(tableMode,"ON") == 0 )
      type = "NUMBER";

   record("SYMBOL_TYPE") = type.c_str();

   geo.setValue("RECORD",record);

   // Copy hidden parameters
   geo("_NAME" ) = (const char*)dataUnitRequest ("_NAME");
   geo("_CLASS") = (const char*)dataUnitRequest ("_CLASS");
   geo("_ID"   ) = (const char*)dataUnitRequest ("_ID");

   return geo;
}

MvRequest
NetCDFTranslator::Execute ( const MvRequest& dataUnitRequest, const MvRequest&, const MvRequest& )
{
   return dataUnitRequest;
}

MvRequest
VoidTranslator::Execute ( const MvRequest& request )
{
   return request;
}

MvRequest
VoidTranslator::Execute ( const MvRequest& request, const MvRequest& )
{
   return request;
}

MvRequest
VoidTranslator::Execute ( const MvRequest& request, const MvRequest&, const MvRequest& )
{
   return request;
}

MvRequest ImportTranslator::Execute( const MvRequest& req )
{
   // The file is an embedded icon. Extracts the path.
   MvRequest fileReq = req.getSubrequest("IMPORT_FILE");
   if ( fileReq )
   {
      MvRequest out = req;
      out.unsetParam("IMPORT_FILE");
      out("IMPORT_FILE_NAME") = fileReq("PATH");
      return out;
   }

   // Nothing to be translated if file name is given.
   if ( (const char*)req("IMPORT_FILE_NAME") )
      return req;

   // Data file not provided
   return MvRequest("EMPTY");
}

///////////////// Utility function, not normal translator
void generateSubpageSize(const MvRequest& metviewRequest, MvRequest &magicsRequest, double width, double height, bool expanded )
{
   // Copy input request
   MvRequest tmpRequest = metviewRequest.justOneRequest();

   // Check if the request needs to be expanded
   if ( !expanded )
      tmpRequest = ObjectList::ExpandRequest ( tmpRequest,EXPAND_DEFAULTS );

   magicsRequest ( "SUBPAGE_X_LENGTH" ) = width * ( (double)tmpRequest("SUBPAGE_X_LENGTH")/100 );
   magicsRequest ( "SUBPAGE_Y_LENGTH" ) = height * ( (double)tmpRequest("SUBPAGE_Y_LENGTH")/ 100 );
   magicsRequest ( "SUBPAGE_X_POSITION" ) = width * ( (double)tmpRequest("SUBPAGE_X_POSITION") /100 );
   magicsRequest ( "SUBPAGE_Y_POSITION" ) = height * ( (double)tmpRequest("SUBPAGE_Y_POSITION") / 100 );

   return;
}

MvRequest
ThermoTranslator::Execute ( const MvRequest& visdefRequest, const MvRequest& duRequest )
{
   // This is a very specific translator. The acceptable thermo data variables are:
   // t (temperature) and td (dewpoint)
   ensure ( visdefRequest.getVerb() == string("MTHERMO") );
   ensure ( (const char*)duRequest("NETCDF_X_VARIABLE")  );

   // Get variable and make sure it is lower case
   string svar = (const char*)duRequest("NETCDF_X_VARIABLE");
   std::transform(svar.begin(), svar.end(), svar.begin(), ::tolower);

   // Convert to Magics request
   // Copy legend parameters
   MvRequest magicsRequest( "MGRAPH" );
   CopySomeParameters(visdefRequest,magicsRequest,"LEGEND");

   // Convert remaining parameters accordingly.
   // There are a few MTHERMO parameters whose default values are different from 
   // the equivalent MGRAPH parameters.
   if ( svar == string("t") )
   {
      CopySomeParameters (visdefRequest, magicsRequest, "THERMO_TEMPERATURE", "GRAPH");

      if ( !(const char*)magicsRequest("GRAPH_LINE_COLOUR") )
          magicsRequest("GRAPH_LINE_COLOUR") = "RED";
      if ( !(const char*)magicsRequest("GRAPH_LINE_THICKNESS") )
          magicsRequest("GRAPH_LINE_THICKNESS") = 8;
      if ( !(const char*)magicsRequest("GRAPH_MISSING_DATA_MODE") )
          magicsRequest("GRAPH_MISSING_DATA_MODE") = "JOIN";
      if ( !(const char*)magicsRequest("GRAPH_MISSING_DATA_STYLE") )
          magicsRequest("GRAPH_MISSING_DATA_STYLE") = "SOLID";
      if ( !(const char*)magicsRequest("GRAPH_MISSING_DATA_THICKNESS") )
          magicsRequest("GRAPH_MISSING_DATA_THICKNESS") = 8;
   }
   else if ( svar == string("td") )
   {
      CopySomeParameters (visdefRequest, magicsRequest, "THERMO_DEWPOINT", "GRAPH");

      if ( !(const char*)magicsRequest("GRAPH_LINE_STYLE") )
          magicsRequest("GRAPH_LINE_STYLE") = "DASH";
      if ( !(const char*)magicsRequest("GRAPH_LINE_COLOUR") )
          magicsRequest("GRAPH_LINE_COLOUR") = "RED";
      if ( !(const char*)magicsRequest("GRAPH_LINE_THICKNESS") )
          magicsRequest("GRAPH_LINE_THICKNESS") = 8;
      if ( !(const char*)magicsRequest("GRAPH_MISSING_DATA_MODE") )
          magicsRequest("GRAPH_MISSING_DATA_MODE") = "JOIN";
      if ( !(const char*)magicsRequest("GRAPH_MISSING_DATA_THICKNESS") )
          magicsRequest("GRAPH_MISSING_DATA_THICKNESS") = 8;
   }
   else
   {
      cout << "NETCDF_X_VARIABLE not implemented: " << svar.c_str() << endl;
      cout << "Variables implemented are T and TD" << endl;
      return MvRequest("EMPTY");
   }

   return magicsRequest;
}
