/***************************** 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 "MvPath.hpp"

#include "MagPlusService.h"
#include "MagicsTranslator.h"
#include "MvMiscellaneous.h"
#include "ObjectList.h"
#include "PlotMod.h"
#include "PlotModConst.h"
#include "Path.h"
#include "MvLog.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 ncGeoMVecTranslatorInstance("NETCDF_GEO_MATRIX_VECTORS");
NetCDFTranslator ncMatTranslatorInstance("NETCDF_XY_MATRIX");
NetCDFTranslator ncMatVecTranslatorInstance("NETCDF_XY_MATRIX_VECTORS");
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");
ThermoGridTranslator mthermoGridTranslatorInstance("MTHERMOGRID");
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");
InputBoxplotTranslator inputBoxplotTranslatorInstance("INPUT_XY_BOXPLOT");
VoidTranslator mcoastTranslatorInstance("MCOAST");
MContTranslator mcontTranslatorInstance("MCONT");
VoidTranslator mgraphTranslatorInstance("MGRAPH");
VoidTranslator mlegendTranslatorInstance("MLEGEND");
VoidTranslator msymbolTranslatorInstance("MSYMB");
MWindTranslator 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");
TableTranslator table2TranslatorInstance("TABLE_XY_POINTS");
TableTranslator table3TranslatorInstance("TABLE_GEO_POINTS");
TableTranslator table4TranslatorInstance("TABLE_XY_VECTORS");
TableTranslator table5TranslatorInstance("TABLE_GEO_VECTORS");
TableTranslator table6TranslatorInstance("TABLE_XY_BINNING");
TableTranslator 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() = default;

//=================================================
// 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";
    metview::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);
    metview::CopySomeParameters(expandRequest, magicsRequest, "GRIB_");
    metview::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
    metview::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
    if ((const char*)dataUnitRequest("_PATH"))
        magicsRequest("_PATH") = (const char*)dataUnitRequest("_PATH");
    if ((const char*)dataUnitRequest("_NAME"))
        magicsRequest("_NAME") = (const char*)dataUnitRequest("_NAME");
    if ((const char*)dataUnitRequest("_CLASS"))
        magicsRequest("_CLASS") = (const char*)dataUnitRequest("_CLASS");
    if ((const char*)dataUnitRequest("_ID"))
        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 != nullptr );	// 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& visdefRequest)
{
    MvRequest magicsRequest;
    const char* dataPath = nullptr;

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

    // Set to this BUFR dataunit request some specific parameters defined
    // in the associated visdef. This is a Magics requirement.
    if ((const char*)visdefRequest("OBS_LEVEL"))
        magicsRequest("OBS_LEVEL") = (const char*)visdefRequest("OBS_LEVEL");

    if ((const char*)visdefRequest("OBS_LEVEL_2"))
        magicsRequest("OBS_LEVEL_2") = (const char*)visdefRequest("OBS_LEVEL_2");

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

    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");
        metview::CopyAndRemoveParameters(visdefRequest, tmpRequest, "INPUT_FIELD_SUPPRESS");
        metview::CopyAndRemoveParameters(tmpRequest, tmp1Request, "GRIB_");

        // Translate the LEGEND_ENTRY subrequest
        metview::CopyAndRemoveParameters(tmp1Request, magicsRequest, "LEGEND_ENTRY");
        metview::CopySomeParameters(legRequest, magicsRequest, "LEGEND");
    }
    else {
        // Strip out the "GRIB_" and the "INPUT_FIELD_SUPPRESS" parts
        metview::CopyAndRemoveParameters(visdefRequest, tmpRequest, "INPUT_FIELD_SUPPRESS");
        metview::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";
    metview::CopySomeParameters(metviewRequest, magicsRequest, "PAGE_FRAME");
    metview::CopySomeParameters(metviewRequest, magicsRequest, "SUBPAGE_FRAME");
    metview::CopySomeParameters(metviewRequest, magicsRequest, "SUBPAGE_METADATA");
    metview::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");

    bool hasZoom = false;
    // Set zoom definition
    if ((const char*)metviewRequest("_ZOOM_DEFINITION")) {
        MvRequest req;
        std::string sjson = (const char*)metviewRequest("_ZOOM_DEFINITION");
        MagPlusService::Instance().decode(req, sjson);

        magicsRequest("SUBPAGE_LOWER_LEFT_LATITUDE") = req("subpage_lower_left_latitude");
        magicsRequest("SUBPAGE_LOWER_LEFT_LONGITUDE") = req("subpage_lower_left_longitude");
        magicsRequest("SUBPAGE_UPPER_RIGHT_LATITUDE") = req("subpage_upper_right_latitude");
        magicsRequest("SUBPAGE_UPPER_RIGHT_LONGITUDE") = req("subpage_upper_right_longitude");

        magicsRequest("SUBPAGE_MAP_AREA_DEFINITION") = "CORNERS";
        metviewRequest.unsetParam("_ZOOM_DEFINITION");
        metviewRequest.unsetParam("MAP_AREA_DEFINITION");
        metviewRequest.unsetParam("AREA");

        hasZoom = true;
    }

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

    // Set Id definition
    // FAMI20161124 : it should set only _PRESENTABLE_ID. The _ID value
    // clashes with one of the icon's _ID value, but at this date, this
    // _ID for a PAGE/PRESENTABLE is used somewhere later. Remove this
    // code below if this is no longer the case.
    if ((const char*)metviewRequest("_ID"))
        magicsRequest("_ID") = (const char*)metviewRequest("_ID");
    if ((const char*)metviewRequest("_PRESENTABLE_ID"))
        magicsRequest("_PRESENTABLE_ID") = (const char*)metviewRequest("_PRESENTABLE_ID");

    // Predefined areas
    bool predefArea = false;
    if (const char* predef = metviewRequest("AREA_MODE")) {
        if (strcmp(predef, "NAME") == 0) {
            if (!predefAreas_) {
                loadPredefAreas();
            }

            std::string str_name("europe");
            if (const char* predef_name = metviewRequest("AREA_NAME")) {
                str_name = std::string(predef_name);
            }

            if (!str_name.empty()) {
                metview::toLower(str_name);
                MvRequest req = findPredefArea(str_name);

                if (req) {
                    magicsRequest("SUBPAGE_MAP_PROJECTION") = req("subpage_map_projection");
                    magicsRequest("SUBPAGE_MAP_AREA_DEFINITION") = "CORNERS";
                    if (const char* chv = req("subpage_vertical_longitude")) {
                        magicsRequest("SUBPAGE_MAP_VERTICAL_LONGITUDE") = chv;
                    }
                    if (const char* chv = req("subpage_map_hemisphere")) {
                        magicsRequest("SUBPAGE_MAP_HEMISPHERE") = chv;
                    }

                    // we are not in zoom mode!
                    if (!hasZoom) {
                        magicsRequest("SUBPAGE_LOWER_LEFT_LATITUDE") = req("subpage_lower_left_latitude");
                        magicsRequest("SUBPAGE_LOWER_LEFT_LONGITUDE") = req("subpage_lower_left_longitude");
                        magicsRequest("SUBPAGE_UPPER_RIGHT_LATITUDE") = req("subpage_upper_right_latitude");
                        magicsRequest("SUBPAGE_UPPER_RIGHT_LONGITUDE") = req("subpage_upper_right_longitude");
                    }
                }
            }
            predefArea = true;
        }
    }

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

    // if not predefined map area
    if (!predefArea) {
        const char* projection = metviewRequest("MAP_PROJECTION");
        MvRequest tmpReq;
        if (!projection) {
            tmpReq = ObjectList::ExpandRequest(metviewRequest, EXPAND_DEFAULTS);
            projection = tmpReq("MAP_PROJECTION");
        }
        if (projection) {
            // Temporary solution to draw cartesian view
            if (strcmp(projection, "cartesian") == 0 ||
                strcmp(projection, "tephigram") == 0 ||
                strcmp(projection, "skewt") == 0 || strcmp(projection, "emagram") == 0 ||
                strcmp(projection, "taylor") == 0) {
                // Add a new request related to the VIEW
                magicsRequest("SUBPAGE_MAP_PROJECTION") = "NEXT";
                metview::RemoveParameters(metviewRequest, "PAGE");
                metview::RemoveParameters(metviewRequest, "SUBPAGE");
                magicsRequest = magicsRequest + metviewRequest;
            }
            else {
                // Translate AREA parameter
                const char* area = metviewRequest("AREA");
                if (area) {
                    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);
                }

                // for polar_sterographic for MAP_AREA_DEFINITION the magics paramater
                // we need to use is called SUBPAGE_MAP_AREA_DEFINITION_POLAR!
                const char* area_def = metviewRequest("MAP_AREA_DEFINITION");
                if (strcmp(projection, "POLAR_STEREOGRAPHIC") == 0 &&
                    area_def) {
                    magicsRequest("SUBPAGE_MAP_AREA_DEFINITION_POLAR") = area_def;
                    metviewRequest.unsetParam("MAP_AREA_DEFINITION");
                }

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

                magicsRequest("SUBPAGE_COORDINATES_SYSTEM") = metviewRequest("COORDINATES_SYSTEM");

                // Translate MAP to SUBPAGE_MAP parameters
                metview::CopySomeParameters(metviewRequest, magicsRequest, "MAP_", "SUBPAGE_MAP_");
            }
        }
        else {
            // In case of a non MapView this parameter should be set
            magicsRequest("SUBPAGE_MAP_PROJECTION") = "NONE";
        }
    }

    return magicsRequest;
}

void PageTranslator::loadPredefAreas()
{
    std::string fName = metview::etcDirFile("PredefinedAreaList");
    Path fp(fName);
    if (fp.exists()) {
        predefAreas_.read(fName.c_str());
    }
    // TODO: print warning when file does not exsist
}

MvRequest PageTranslator::findPredefArea(const std::string& name)
{
    if (!predefAreas_) {
        loadPredefAreas();
    }

    MvRequest r = predefAreas_;
    do {
        if (strcmp(r.getVerb(), name.c_str()) == 0) {
            return r;
        }
    } while (r.advance());

    return {};
}


//=================================================================
//
// 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() == std::string("PAXIS"))
            magicsRequest = magicsRequest + tmpRequest.justOneRequest();

        tmpRequest.advance();
    }

    return magicsRequest;
}

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

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

    MvRequest legRequest = metviewRequest.getSubrequest("LEGEND_ENTRY");
    if (legRequest)
        metview::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 = nullptr;
    int i = 0;
    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
MWindTranslator::Execute(const MvRequest& metviewRequest)
{
    MvRequest magRequest = metviewRequest;

    std::string v;
    magRequest.getValue("WIND_THINNING_METHOD", v, true);

    if (v == "DENSITY") {
        std::string dv;
        magRequest("WIND_THINNING_METHOD") = "AUTOMATIC";
        if ((const char*)magRequest("WIND_DENSITY")) {
            magRequest("WIND_THINNING_FACTOR") = magRequest("WIND_DENSITY");
        }
        else {
            magRequest("WIND_THINNING_FACTOR") = 5;
        }
        magRequest.unsetParam("WIND_DENSITY");
    }
    else {
        magRequest("WIND_THINNING_METHOD") = "USER";
    }

    return magRequest;
}


MvRequest
WindTranslator::Execute(const MvRequest& metviewRequest)
{
    ensure(metviewRequest.getVerb() == std::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() == std::string("PSYMB"));

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

    // Check LEGEND_ENTRY subrequest
    MvRequest legRequest = visdefRequest.getSubrequest("LEGEND_ENTRY");
    if (legRequest)
        metview::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");
    std::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&)
{
    std::string plot_type = dataUnitRequest.getVerb();

    if (plot_type == "NETCDF_GEO_MATRIX_VECTORS") {
        MvRequest mag = dataUnitRequest;
        mag.setVerb("NETCDF_GEO_MATRIX");
        const char* ch = mag("NETCDF_VALUE_VARIABLE");
        if (!ch || std::string(ch).empty()) {
            if (const char* chx = mag("NETCDF_X_COMPONENT_VARIABLE")) {
                mag("NETCDF_VALUE_VARIABLE") = chx;
            }
        }
        return mag;
    }
    else if (plot_type == "NETCDF_XY_MATRIX_VECTORS") {
        MvRequest mag = dataUnitRequest;
        mag.setVerb("NETCDF_XY_MATRIX");
        const char* ch = mag("NETCDF_VALUE_VARIABLE");
        if (!ch || std::string(ch).empty()) {
            if (const char* chx = mag("NETCDF_X_COMPONENT_VARIABLE")) {
                mag("NETCDF_VALUE_VARIABLE") = chx;
            }
        }
        return mag;
    }

    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 {"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() == std::string("MTHERMO"));
    ensure((const char*)duRequest("NETCDF_X_VARIABLE"));

    // Get variable and make sure it is lower case
    std::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");
    metview::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 == std::string("t")) {
        metview::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 == std::string("td")) {
        metview::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 {
        MvLog().popup().warn() << "uPlot ThermoTranslator::Execute-> NETCDF_X_VARIABLE not implemented: " << svar;
        return {"EMPTY"};
    }

    return magicsRequest;
}

MvRequest
ThermoGridTranslator::Execute(const MvRequest& req)
{
    assert(strcmp(req.getVerb(), "MTHERMOGRID") == 0);
    MvRequest magicsRequest = req;
    const char* ch = magicsRequest("THERMO_GRID_LAYER_MODE");
    if (ch) {
        magicsRequest.unsetParam("THERMO_GRID_LAYER_MODE");
    }
    return magicsRequest;
}

MvRequest InputBoxplotTranslator::Execute(const MvRequest& inReq)
{
    MvRequest magReq("INPUT_BOXPLOT");

    std::cout << "BOXPLOT TRANSLATOR" << std::endl;
    static std::map<std::string, std::string> params = {
        {"INPUT_X_VALUES", "BOXPLOT_POSITIONS"},
        {"INPUT_DATE_X_VALUES", "BOXPLOT_DATE_POSITIONS"},
        {"INPUT_MINIMUM_VALUES", "BOXPLOT_MINIMUM_VALUES"},
        {"INPUT_MAXIMUM_VALUES", "BOXPLOT_MAXIMUM_VALUES"},
        {"INPUT_MEDIAN_VALUES", "BOXPLOT_MEDIAN_VALUES"},
        {"INPUT_BOX_UPPER_VALUES", "BOXPLOT_BOX_UPPER_VALUES"},
        {"INPUT_BOX_LOWER_VALUES", "BOXPLOT_BOX_LOWER_VALUES"}};

    for (const auto& it : params) {
        metview::copyParam(inReq, magReq, it.first, it.second);
    }
    return magReq;
}

MvRequest
TableTranslator::Execute(const MvRequest& request)
{
    ensure((const char*)request("TABLE_FILENAME"));

    // Make sure that parameter TABLE_FILENAME contains an absolute path
    // First check if the filename contains already an absolute path
    const char* path = (const char*)request("TABLE_FILENAME");
    if (path[0] == '/' && FileCanBeOpened(path, "r"))
        return request;

    // Get absolute path from the input request
    std::string spath = ObjectInfo::ObjectFullPath(request);

    // Update input request
    MvRequest magicsRequest = request;
    spath += "/" + std::string(path);
    magicsRequest("TABLE_FILENAME") = spath.c_str();

    return magicsRequest;
}
