/*
 * (C) Copyright 1996-2016 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#include "MagPlus.h"
#include <iostream>

#include <AxisWrapper.h>
#include <CoastlinesWrapper.h>
#include <FortranSceneNodeWrapper.h>
#include <FortranViewNodeWrapper.h>
#include <MvRootSceneNodeWrapper.h>
#include <TaylorGridWrapper.h>
#include <TephiGridWrapper.h>
#include <VisualAction.h>
#include "ImportObjectHandlerWrapper.h"
#include "MetaDataWrapper.h"

#ifdef HAVE_GRIB
#include <GribDecoderWrapper.h>
#include <GribLoopWrapper.h>
#endif

#ifdef HAVE_NETCDF
#include <NetcdfDecoderWrapper.h>
#endif

#include <BinningObjectWrapper.h>
#include <GeoPointsDecoderWrapper.h>

#ifdef HAVE_ODB
#include <OdaGeoDecoderWrapper.h>
#include <OdaXYDecoderWrapper.h>
#endif

#ifdef HAVE_BUFR
#include <ObsDecoderWrapper.h>
#include <ObsPlottingWrapper.h>
#endif

#include <ContourWrapper.h>
#include <GeoJSonWrapper.h>
#include <ImportActionWrapper.h>
#include <ImportPlotWrapper.h>
#include <InputDataWrapper.h>
#include <TableDecoderWrapper.h>

#include <LegendVisitorWrapper.h>
#include <TextVisitor.h>
#include <TextVisitorWrapper.h>

#include <GraphPlottingWrapper.h>
#include <MultiVisdef.h>
#include <SymbolPlottingWrapper.h>
#include <WindWrapper.h>

#include <EmagramGrid.h>
#include <EmagramGridWrapper.h>
#include <SkewtGrid.h>
#include <SkewtGridWrapper.h>

#include <UserPoint.h>
#include "MagJSon.h"
#include "MagicsEvent.h"

#include <PostScriptDriverWrapper.h>

#ifdef HAVE_CAIRO
#include <CairoDriverWrapper.h>
#endif

#include <GeoJsonDriverWrapper.h>
#include <KMLDriverWrapper.h>
#include <SVGDriverWrapper.h>

#ifdef MAGICS_QT
#include <QtDriver.h>
#endif


template <class T>
void replace(magics::MagRequest& request, const string& name, T from, T to) {
    if (request.countValues(name.c_str()) == 0) {
        request(name) = to;
        return;
    }
    T val = request(name);
    if (val == from)
        request(name) = to;
}

void replace_string(magics::MagRequest& request, const string& name, const string& from, const string& to) {
    if (request.countValues(name) == 0) {
        request(name) = to;
        return;
    }
    string val = request(name);
    if (val == from)
        request(name) = to;
}

void replace_default(magics::MagRequest& request, const string& name, const string& to) {
    if (request.countValues(name) == 0) {
        request(name) = to;
        return;
    }
}

void copy_string(magics::MagRequest& request, const string& from, const string& to) {
    if (request.countValues(from)) {
        string val  = request(from);
        request(to) = val;
    }
}

template <class T>
void replace(magics::MagRequest& request, const string& name, T from, const string& newname, T to) {
    if (request.countValues(name) == 0) {
        request(newname.c_str()) = to;
        return;
    }
    T val = request(name);
    if (val == from)
        request(newname) = to;
    else
        request(newname) = val;
}

string get(magics::MagRequest& request, const string& param, const string& val) {
    string v = request(param);

    return (!v.empty()) ? v : val;
}

using namespace std;
using namespace magics;


map<string, MagPlus::ObjectCreator> MagPlus::sceneCreators_;
map<string, MagPlus::ObjectCreator> MagPlus::driverCreators_;
map<string, MagPlus::ObjectCreator> MagPlus::sceneUpdators_;
map<string, MagPlus::DataCreator> MagPlus::dataCreators_;


MagPlus::MagPlus() : root_(0), superpage_(-1), geographical_(true), mode_(interactif), currentMulti_(0) {
#ifdef MAGICS_QT
    qtDriver_ = 0;
    qtScene_  = 0;
#endif
    if (sceneUpdators_.empty()) {
        sceneUpdators_["PAGE"] = &MagPlus::page_update;
    }
    if (dataCreators_.empty()) {
        dataCreators_["GEOPOINTS"] = &MagPlus::createGeopoints;
#ifdef HAVE_NETCDF
        dataCreators_["NETCDF_GEOPOINTS"]   = &MagPlus::createnetcdf;
        dataCreators_["NETCDF_GEOVECTORS"]  = &MagPlus::createnetcdf;
        dataCreators_["NETCDF_GEOMATRIX"]   = &MagPlus::createnetcdf;
        dataCreators_["NETCDF_GEO_POINTS"]  = &MagPlus::createnetcdf;
        dataCreators_["NETCDF_GEO_VECTORS"] = &MagPlus::createnetcdf;
        dataCreators_["NETCDF_GEO_MATRIX"]  = &MagPlus::createnetcdf;
        dataCreators_["NETCDF_POINTS"]      = &MagPlus::createnetcdf;
        dataCreators_["NETCDF_VECTORS"]     = &MagPlus::createnetcdf;
        dataCreators_["NETCDF_MATRIX"]      = &MagPlus::createnetcdf;
        dataCreators_["NETCDF_XY_POINTS"]   = &MagPlus::createnetcdf;
        dataCreators_["NETCDF_XY_VECTORS"]  = &MagPlus::createnetcdf;
        dataCreators_["NETCDF_XY_MATRIX"]   = &MagPlus::createnetcdf;
#endif
    }
    if (sceneCreators_.empty()) {
        sceneCreators_["PAGE"]        = &MagPlus::page;
        sceneCreators_["NEWPAGE"]     = &MagPlus::newpage;
        sceneCreators_["MCOAST"]      = &MagPlus::coastlines;
        sceneCreators_["MTHERMOGRID"] = &MagPlus::tephigrid;
        sceneCreators_["PCOAST"]      = &MagPlus::oldcoastlines;


        sceneCreators_["PAXIS"]         = &MagPlus::axis;
        sceneCreators_["CARTESIANVIEW"] = &MagPlus::cartesian;
        sceneCreators_["PGRIB"]         = &MagPlus::gribloop;
        sceneCreators_["GEOJSON"]       = &MagPlus::geojson;
        sceneCreators_["GRIBLOOP"]      = &MagPlus::gribloop;
        sceneCreators_["DATALOOP"]      = &MagPlus::dataloop;
        sceneCreators_["GEOPOINTS"]     = &MagPlus::geopoints;
#ifdef HAVE_NETCDF
        sceneCreators_["NETCDF_GEOPOINTS"]   = &MagPlus::netcdf;
        sceneCreators_["NETCDF_GEOVECTORS"]  = &MagPlus::netcdf;
        sceneCreators_["NETCDF_GEOMATRIX"]   = &MagPlus::netcdf;
        sceneCreators_["NETCDF_GEO_POINTS"]  = &MagPlus::netcdf;
        sceneCreators_["NETCDF_GEO_VECTORS"] = &MagPlus::netcdf;
        sceneCreators_["NETCDF_GEO_MATRIX"]  = &MagPlus::netcdf;
        sceneCreators_["NETCDF_POINTS"]      = &MagPlus::netcdf;
        sceneCreators_["NETCDF_VECTORS"]     = &MagPlus::netcdf;
        sceneCreators_["NETCDF_MATRIX"]      = &MagPlus::netcdf;
        sceneCreators_["NETCDF_XY_POINTS"]   = &MagPlus::netcdf;
        sceneCreators_["NETCDF_XY_VECTORS"]  = &MagPlus::netcdf;
        sceneCreators_["NETCDF_XY_MATRIX"]   = &MagPlus::netcdf;
#endif
        sceneCreators_["BUFR"]              = &MagPlus::bufr;
        sceneCreators_["INPUT_XY_POINTS"]   = &MagPlus::input;
        sceneCreators_["INPUT_GEO_POINTS"]  = &MagPlus::input;
        sceneCreators_["INPUT_XY_VECTORS"]  = &MagPlus::input;
        sceneCreators_["INPUT_GEO_VECTORS"] = &MagPlus::input;
        sceneCreators_["INPUT_XY_BINNING"]  = &MagPlus::input;
        sceneCreators_["INPUT_GEO_BINNING"] = &MagPlus::input;

        sceneCreators_["TABLE_XY_POINTS"]   = &MagPlus::table;
        sceneCreators_["TABLE_GEO_POINTS"]  = &MagPlus::table;
        sceneCreators_["TABLE_XY_VECTORS"]  = &MagPlus::table;
        sceneCreators_["TABLE_GEO_VECTORS"] = &MagPlus::table;
        sceneCreators_["TABLE_XY_BINNING"]  = &MagPlus::table;
        sceneCreators_["TABLE_GEO_BINNING"] = &MagPlus::table;
        sceneCreators_["cartesian"]         = &MagPlus::cartesianGrid;
        sceneCreators_["tephigram"]         = &MagPlus::tephiGrid;
        sceneCreators_["skewt"]             = &MagPlus::skewtGrid;
        sceneCreators_["emagram"]           = &MagPlus::emagramGrid;
        sceneCreators_["taylor"]            = &MagPlus::taylorGrid;
#ifdef HAVE_ODB
        sceneCreators_["ODB_GEO_POINTS"]  = &MagPlus::geoodb;
        sceneCreators_["ODB_GEO_VECTORS"] = &MagPlus::geoodb;
        sceneCreators_["ODB_XY_POINTS"]   = &MagPlus::xyodb;
        sceneCreators_["ODB_XY_VECTORS"]  = &MagPlus::xyodb;
        sceneCreators_["ODB_XY_BINNING"]  = &MagPlus::xyodb;
#endif
        sceneCreators_["VISDEFS"]        = &MagPlus::visdef;
        sceneCreators_["MULTI"]          = &MagPlus::multi;
        sceneCreators_["PCONT"]          = &MagPlus::contour;
        sceneCreators_["MCONT"]          = &MagPlus::contour;
        sceneCreators_["POBS"]           = &MagPlus::obs;
        sceneCreators_["MOBS"]           = &MagPlus::obs;
        sceneCreators_["PSYMB"]          = &MagPlus::symbol;
        sceneCreators_["MSYMB"]          = &MagPlus::symbol;
        sceneCreators_["PSYMBPLUS"]      = &MagPlus::symbol;
        sceneCreators_["PWIND"]          = &MagPlus::wind;
        sceneCreators_["MWIND"]          = &MagPlus::wind;
        sceneCreators_["MGRAPH"]         = &MagPlus::graph;
        sceneCreators_["SUPERPAGE"]      = &MagPlus::superpage;
        sceneCreators_["LAYER"]          = &MagPlus::layer;
        sceneCreators_["PTEXT"]          = &MagPlus::ptext;
        sceneCreators_["MTEXT"]          = &MagPlus::text;
        sceneCreators_["MLEGEND"]        = &MagPlus::legend;
        sceneCreators_["DEVICE"]         = &MagPlus::device;
        sceneCreators_["MIMPORT"]        = &MagPlus::import;
        sceneCreators_["PRASTER"]        = &MagPlus::raster;
        sceneCreators_["PRASTERLOOP"]    = &MagPlus::rasterloop;
        sceneCreators_["BINNING_OBJECT"] = &MagPlus::binning;
    }

    if (driverCreators_.empty()) {
#ifdef MAGICS_QT
        driverCreators_["QTOUTPUT"] = &MagPlus::qtdriver;
#endif
        driverCreators_["PSOUTPUT"]        = &MagPlus::psdriver;
        driverCreators_["PSCAIRO_OUTPUT"]  = &MagPlus::pscairodriver;
        driverCreators_["EPSCAIRO_OUTPUT"] = &MagPlus::epscairodriver;


        driverCreators_["PNGOUTPUT"]     = &MagPlus::pngdriver;
        driverCreators_["KMLOUTPUT"]     = &MagPlus::kmldriver;
        driverCreators_["GEOJSONOUTPUT"] = &MagPlus::geojsondriver;
        driverCreators_["PDFOUTPUT"]     = &MagPlus::pdfdriver;
        driverCreators_["SVGOUTPUT"]     = &MagPlus::svgdriver;
        driverCreators_["EPSOUTPUT"]     = &MagPlus::epsdriver;
    }
}

bool MagPlus::superpage(magics::MagRequest& in) {
    MagLog::dev() << "superpage--->" << endl;
    if (root_) {
        root_->newpage();

        return false;
    }
    int superpage = in("SUPERPAGE_INDEX");
    in("LAYOUT")  = "positional";

    if (superpage == superpage_)
        return false;
    superpage_   = superpage;
    in("LAYOUT") = "positional";

    replace_string(in, "SUPER_PAGE_FRAME_COLOUR", "BLUE", "grey");

    in.print();
    MvRootSceneNodeWrapper helper;
    helper.set(in);

    root_ = helper.object();

    MagLog::dev() << "<----superpage" << endl;
    return false;
}

bool MagPlus::newpage(magics::MagRequest& in) {
    root_->newpage();

    return false;
}

bool MagPlus::layer(magics::MagRequest& in) {
    in.print();
    int visibility = in("VISIBILITY");
    visibility_    = visibility;
    zindex_        = in("STACKING_ORDER");
    transparency_  = in("TRANSPARENCY");
    id_            = (string)in("_ID");
    layer_         = (string)in("_NAME");


    return false;
}


bool MagPlus::psdriver(magics::MagRequest& in) {
    PostScriptDriverWrapper helper;
    helper.set(in);

    drivers_.push_back(helper.object());
    mode_ = paper;
    return false;
}

bool MagPlus::epsdriver(magics::MagRequest& in) {
    PostScriptDriverWrapper helper;
    helper.set(in);
    helper.me()->setEPS(true);
    drivers_.push_back(helper.object());
    mode_ = paper;
    return false;
}

bool MagPlus::pngdriver(magics::MagRequest& in) {
#ifdef HAVE_CAIRO
    CairoDriverWrapper helper;
    helper.set(in);
    helper.me()->setPNG();
    drivers_.push_back(helper.object());
    mode_ = paper;
#endif
    return false;
}

bool MagPlus::pscairodriver(magics::MagRequest& in) {
#ifdef HAVE_CAIRO
    CairoDriverWrapper helper;
    helper.set(in);
    helper.me()->setPS();
    drivers_.push_back(helper.object());
    mode_ = paper;
#endif
    return false;
}

bool MagPlus::epscairodriver(magics::MagRequest& in) {
#ifdef HAVE_CAIRO
    CairoDriverWrapper helper;
    helper.set(in);
    helper.me()->setEPS();
    drivers_.push_back(helper.object());
    mode_ = paper;
#endif
    return false;
}

bool MagPlus::pdfdriver(magics::MagRequest& in) {
#ifdef HAVE_CAIRO
    CairoDriverWrapper helper;
    helper.set(in);
    helper.me()->setPDF();
    drivers_.push_back(helper.object());
    mode_ = paper;

#endif
    return false;
}

#ifdef MAGICS_QT
void MagPlus::setQtScene(QGraphicsScene* scene) {
    qtScene_ = scene;
}

bool MagPlus::qtdriver(magics::MagRequest& /*in*/) {
    if (!qtDriver_) {
        ASSERT(qtScene_);
        qtDriver_ = new magics::QtDriver();
        qtDriver_->setScene(qtScene_);
    }
#ifndef MAG_NEXT
    drivers_.push_back(qtDriver_);
#else
    if (mvMode_ == creation)
        drivers_.push_back(qtDriver_);
    qtDriver_->setUpdateMode(mvMode_ != creation);
#endif
    return false;
}

#endif

bool MagPlus::svgdriver(magics::MagRequest& in) {
    SVGDriverWrapper helper;
    helper.set(in);

    drivers_.push_back(helper.object());
    mode_ = paper;
    return false;
}

bool MagPlus::kmldriver(magics::MagRequest& in) {
    KMLDriverWrapper helper;
    in("KML_DESCRIPTION") = "Metview/Magics++";
    helper.set(in);
    mode_ = paper;
    drivers_.push_back(helper.object());

    return false;
}

bool MagPlus::geojsondriver(magics::MagRequest& in) {
    GeoJsonDriverWrapper helper;
    in("GEOJSON_DESCRIPTION") = "Metview/Magics++";
    helper.set(in);
    mode_ = paper;
    drivers_.push_back(helper.object());

    return false;
}

bool MagPlus::page_update(magics::MagRequest& in) {
    // get the Metview ID;
    // reset the page!!!
    int id                       = in("_ID");
    FortranViewNodeWrapper* page = pages_[id];
    page->set(in);
    FortranViewNodeAttributes* node = page->object();
    page->object()->getReady();
    MagLog::progress() << "new geometry" << *node << endl;
    MagLog::broadcast();


    return false;
}

bool MagPlus::page(magics::MagRequest& in) {
    MagLog::dev() << "page and subpage--->" << endl;

    sceneCreators_["MLEGEND"] = &MagPlus::legend;


    while (!empty())
        pop();

    geographical_ = true;

    FortranSceneNodeWrapper scenehelper;
    scenehelper.set(in);


    setIconInfo(in, *scenehelper.object());

    root_->insert(scenehelper.object());

    push(scenehelper.object());
    replace(in, "SUBPAGE_Y_LENGTH", 17.85, -1.);  // reset to the default!
    in("SUBPAGE_MAP_PREVIEW") = "on";

    page_ = 0;


    if ((string)in("SUBPAGE_MAP_PROJECTION") != "NEXT") {
        FortranViewNodeWrapper* viewhelper = new FortranViewNodeWrapper();
        viewhelper->set(in);
        FortranViewNode* view = viewhelper->object();

        string id = in("_ID");
        int i     = in("_ID");
        pages_[i] = viewhelper;
        if (!id.empty()) {
            view->setInteractiveInfo(id.c_str(), in("ZOOM_NUMBER_OF_LEVELS"), in("ZOOM_CURRENT_LEVEL"));
        }
        top()->insert(view);

        push(view);

        string info = (string)in("SUBPAGE_METADATA_INFO");
        if (info == "ON") {
            MetaDataVisitor* meta = new MetaDataVisitor();
            string path           = (string)in("SUBPAGE_METADATA_JAVASCRIPT_PATH");
            meta->javascript_     = (path);
            view->push_back(meta);
        }
    }
    else {
        page_                        = new FortranViewNodeWrapper();
        in("SUBPAGE_MAP_PROJECTION") = "cartesian";
        page_->set(in);
        int id     = in("_ID");
        pages_[id] = page_;
    }

    MagLog::dev() << "<----page and subpage" << endl;
    return false;  // do not exit
}


bool MagPlus::cartesian(magics::MagRequest& in) {
    string projection            = get(in, "MAP_PROJECTION", "cartesian");
    in("SUBPAGE_MAP_PROJECTION") = projection;

    if (!page_)
        page_ = new FortranViewNodeWrapper();
    page_->set(in);
    id_       = (string)in("_ID");
    in("_ID") = id_;

    string zindex = in("STACKING_ORDER");
    zindex_       = zindex.empty() ? -1 : tonumber(zindex);

    string visibility = in("VISIBILITY");
    visibility_       = visibility.empty() ? true : tonumber(visibility);

    string transparency = in("TRANSPARENCY");
    transparency_       = transparency.empty() ? 0 : tonumber(transparency);


    layer_                = (string)in("_NAME");
    FortranViewNode* view = page_->object();
    setIconInfo(in, *view);

    if (!id_.empty()) {
        view->setInteractiveInfo(id_.c_str(), in("ZOOM_NUMBER_OF_LEVELS"), in("ZOOM_CURRENT_LEVEL"));
    }
    top()->insert(view);
    push(view);

    in.print();


    map<string, ObjectCreator>::iterator creator = sceneCreators_.find(projection);
    if (creator != sceneCreators_.end()) {
        (this->*creator->second)(in);
    }

    geographical_ = false;
    return false;  // do not exit
}

bool MagPlus::cartesianGrid(magics::MagRequest& in) {
    string xtype = lowerCase(in("X_AXIS_TYPE"));
    string ytype = lowerCase(in("Y_AXIS_TYPE"));

    replace_string(in, "X_AXIS_TYPE", "", "regular");
    replace_string(in, "Y_AXIS_TYPE", "", "regular");
    replace_string(in, "X_AXIS_TYPE", "longitude", "regular");
    replace_string(in, "X_AXIS_TYPE", "LONGITUDE", "regular");
    replace_string(in, "X_AXIS_TYPE", "latitude", "regular");
    replace_string(in, "X_AXIS_TYPE", "LATITUDE", "regular");
    replace_string(in, "Y_AXIS_TYPE", "", "regular");
    replace_string(in, "Y_AXIS_TYPE", "longitude", "regular");
    replace_string(in, "Y_AXIS_TYPE", "latitude", "regular");
    replace_string(in, "Y_AXIS_TYPE", "LONGITUDE", "regular");
    replace_string(in, "Y_AXIS_TYPE", "LATITUDE", "regular");
    magics::MagRequest& haxis = in.getSubRequest("HORIZONTAL_AXIS");

    if (haxis) {
        // use the user defined one
        HorizontalAxis* axis = new HorizontalAxis();
        haxis.print();
        replace_string(haxis, "_NAME", "", "Horizontal Axis");
        replace_string(haxis, "_CLASS", "", "MAXIS");

        setIconInfo(haxis, *axis);
        AxisWrapper helper(axis);
        string type = haxis("AXIS_TYPE");
        if (type != "POSITION_LIST") {
            haxis("AXIS_TYPE") = xtype;
            if (xtype == "latitude" || xtype == "longitude") {
                haxis("AXIS_TYPE")            = "regular";
                haxis("AXIS_TICK_LABEL_TYPE") = xtype;
            }
        }

        haxis.print();
        helper.set(haxis);

        axis->orientation_ = "HORIZONTAL";
        axis->position_    = ("bottom");
        top()->push_back(axis);
    }
    else {
        HorizontalAxis* haxis = new HorizontalAxis();
        haxis->icon("Horizontal Axis", "MAXIS");
        haxis->label_type_ = xtype;

        haxis->method_ = unique_ptr<AxisMethod>(MagTranslator<string, AxisMethod>()(in("X_AXIS_TYPE")));
        top()->push_back(haxis);
    }


    magics::MagRequest& vaxis = in.getSubRequest("VERTICAL_AXIS");


    if (vaxis) {
        // use the user defined one
        vaxis.print();
        VerticalAxis* axis = new VerticalAxis();
        replace_string(vaxis, "_NAME", "", "Vertical Axis");
        replace_string(vaxis, "_CLASS", "", "MAXIS");


        setIconInfo(vaxis, *axis);
        AxisWrapper helper(axis);
        string type = vaxis("AXIS_TYPE");
        if (type != "POSITION_LIST") {
            vaxis("AXIS_TYPE") = ytype;
            if (ytype == "latitude" || ytype == "longitude") {
                vaxis("AXIS_TYPE")            = "regular";
                vaxis("AXIS_TICK_LABEL_TYPE") = ytype;
            }
        }

        helper.set(vaxis);
        axis->orientation_ = "VERTICAL";
        top()->push_back(axis);
    }
    else {
        VerticalAxis* vaxis = new VerticalAxis();
        vaxis->icon("Vertical Axis", "MAXIS");
        vaxis->label_type_ = ytype;
        vaxis->method_     = unique_ptr<AxisMethod>(MagTranslator<string, AxisMethod>()(in("Y_AXIS_TYPE")));
        top()->push_back(vaxis);
    }
    return true;  //< @note return value was missing, what should it return?
}

bool MagPlus::tephiGrid(magics::MagRequest& in) {
    magics::MagRequest& tephi = in.getSubRequest("THERMO_GRID");
    if (tephi) {
        // use the user defined one
        tephi.print();
        TephiGridWrapper helper;
        setIconInfo(tephi, *helper.object());
        helper.set(tephi);

        top()->push_back(helper.object());
        setIconInfo(in, *helper.object());
    }
    else {
        TephiGrid* grid = new TephiGrid();
        grid->icon("Tephigram Grid", "MTHERMO_GRID");
        top()->push_back(grid);
    }
    return true;  //< @note return value was missing, what should it return?
}

bool MagPlus::skewtGrid(magics::MagRequest& in) {
    magics::MagRequest& tephi = in.getSubRequest("THERMO_GRID");
    if (tephi) {
        // use the user defined one
        tephi.print();
        SkewtGridWrapper helper;
        setIconInfo(tephi, *helper.object());
        helper.set(tephi);

        top()->push_back(helper.object());
        setIconInfo(in, *helper.object());
    }
    else {
        SkewtGrid* grid = new SkewtGrid();
        grid->icon("SkewT Grid", "MTHERMO_GRID");
        top()->push_back(grid);
    }
    return true;  //< @note return value was missing, what should it return?
}

bool MagPlus::emagramGrid(magics::MagRequest& in) {
    magics::MagRequest& tephi = in.getSubRequest("THERMO_GRID");
    if (tephi) {
        // use the user defined one
        tephi.print();
        EmagramGridWrapper helper;
        setIconInfo(tephi, *helper.object());
        helper.set(tephi);

        top()->push_back(helper.object());
        setIconInfo(in, *helper.object());
    }
    else {
        EmagramGrid* grid = new EmagramGrid();
        grid->icon("Emagram Grid", "MTHERMO_GRID");
        top()->push_back(grid);
    }
    return true;  //< @note return value was missing, what should it return?
}


bool MagPlus::taylorGrid(magics::MagRequest& in) {
    magics::MagRequest& taylor = in.getSubRequest("TAYLOR_GRID");
    if (taylor) {
        // use the user defined one
        taylor.print();
        TaylorGridWrapper helper;
        setIconInfo(taylor, *helper.object());
        helper.set(taylor);

        top()->push_back(helper.object());
        setIconInfo(in, *helper.object());
    }
    else {
        TaylorGrid* grid = new TaylorGrid();
        grid->icon("Taylor Grid", "MTAYLOR");
        top()->push_back(grid);
    }
    return true;  //< @note return value was missing, what should it return?
}
bool MagPlus::oldcoastlines(magics::MagRequest& in) {
    replace_string(in, "MAP_COASTLINE_RESOLUTION", "MEDIUM", "automatic");
    coastlines(in);
    return true;  //< @note return value was missing, what should it return?
}
bool MagPlus::coastlines(magics::MagRequest& in) {
    MagLog::dev() << "add coastlines" << endl;


    replace_string(in, "_NAME", "", "Coastlines");
    replace_string(in, "_CLASS", "", "MCOAST");
    CoastlinesWrapper helper;

    helper.set(in);

    top()->push_back(helper.object());
    setIconInfo(in, *helper.object());
    MagLog::dev() << top() << endl;
    MagLog::dev() << *helper.object() << endl;

    return false;  // do not exit
}
bool MagPlus::tephigrid(magics::MagRequest& in) {
    MagLog::dev() << "add Tephi Grid" << endl;

    replace_string(in, "_NAME", "", "Thermogrid");
    replace_string(in, "_CLASS", "", "MTHERMOGRID");
    TephiGridWrapper helper;

    // EmagramGridWrapper helper;

    helper.set(in);

    top()->push_back(helper.object());
    setIconInfo(in, *helper.object());
    MagLog::dev() << top() << endl;
    MagLog::dev() << *helper.object() << endl;

    return false;  // do not exit
}
bool MagPlus::axis(magics::MagRequest& in) {
    return false;  // do not exit
    MagLog::dev() << "add axis" << endl;
    string orientation = in("AXIS_ORIENTATION");
    Axis* axis         = 0;
    if (magCompare(orientation, "vertical"))
        axis = new VerticalAxis();
    else
        axis = new HorizontalAxis();

    AxisWrapper helper(axis);
    helper.set(in);

    top()->push_back(axis);
    MagLog::dev() << *axis << "\n";

    return false;  // do not exit
}

bool MagPlus::import(magics::MagRequest& in) {
    ImportObjectHandlerWrapper object;
    object.set(in);
    top()->push_back(object.object());
    return false;  // do not exit
}

bool MagPlus::raster(magics::MagRequest& in) {
    MagLog::dev() << "import a raster object" << endl;

    in.print();

    in("IMPORT_FILE_NAME") = in("IMPORT_FILE_PATH");
    in("IMPORT_FORMAT")    = in("IMPORT_FILE_TYPE");

    ImportActionWrapper object;
    ImportPlotWrapper visdef;

    object.set(in);
    visdef.set(in);
    setIconInfo(in, *object.object());
    setIconInfo(in, *visdef.object());


    VisualAction* action = new VisualAction();
    top()->push_back(action);
    push(action);
    top()->data(object.object());
    top()->visdef(visdef.object());
    pop();

    return false;  // do not exit
}

bool MagPlus::binning(magics::MagRequest& in) {
    MagLog::dev() << "add binning" << endl;
    in.print();

    BinningObjectWrapper binning;
    binning.set(in);
    top()->binning(binning.object());
    return true;
}

bool MagPlus::grib(magics::MagRequest& in) {
#ifdef HAVE_GRIB
    MagLog::dev() << "add grib" << endl;
    in.print();

    VisualAction* action = new VisualAction();
    top()->push_back(action);
    push(action);

    GribDecoderWrapper grib;
    grib.set(in);
    setIconInfo(in, *grib.object());
    top()->data(grib.object());
#else
    MagLog::dev() << "can NOT add grib (disabled in Magics!)" << endl;
#endif
    return false;  // do not exit
}

bool MagPlus::table(magics::MagRequest& in) {
    MagLog::dev() << "add table" << endl;

    in.print();
    // First set the table_filename...
    magics::MagRequest& data = in.getSubRequest("TABLE_DATA");
    if (data) {
        if (data.getVerb() == "TABLE" || data.getVerb() == "NOTE" || data.getVerb() == "GEOPOINTS") {
            string x             = data("PATH");
            in("TABLE_FILENAME") = x;
        }

        else if (data.getVerb() == "TABLE_READER") {
            magics::MagRequest& d = data.getSubRequest("DATA");
            if (d) {
                in("TABLE_FILENAME") = d("PATH");
            }
            else {
                in("TABLE_FILENAME") = data("TABLE_FILENAME");
            }
        }
    }

    if (geographical_) {
        VisualAction* action = new VisualAction();
        top()->push_back(action);
        push(action);
        // Create Data
        TableDecoderWrapper xy;
        in.print();
        magics::MagRequest& bin = in.getSubRequest("TABLE_BINNING");
        if (bin) {
            in("TABLE_BINNING") = "on";
            xy.set(in);
            bin("TABLE_BINNING") = "on";
            xy.set(bin);
        }
        else {
            in("TABLE_BINNING") = "off";
            xy.set(in);
            bin("TABLE_BINNING") = "off";
            xy.set(bin);
        }
        xy.set(data);
        setIconInfo(in, *xy.object());
        top()->data(xy.object());
    }
    else {
        VisualAction* action = new VisualAction();
        top()->push_back(action);
        push(action);
        // Create Data
        TableDecoderWrapper xy;
        magics::MagRequest& bin = in.getSubRequest("TABLE_BINNING");
        if (bin) {
            in("TABLE_BINNING") = "on";
            xy.set(in);
            data("TABLE_BINNING") = "on";
            xy.set(data);
            bin("TABLE_BINNING") = "on";
            xy.set(bin);
        }
        else {
            in("TABLE_BINNING") = "off";
            xy.set(in);
            data("TABLE_BINNING") = "off";
            xy.set(data);
            bin("TABLE_BINNING") = "off";
            xy.set(bin);
        }


        setIconInfo(in, *xy.object());
        top()->data(xy.object());
    }


    return false;  // do not exit
}

bool MagPlus::input(magics::MagRequest& in) {
    MagLog::dev() << "add input" << endl;
    in.print();
    if (geographical_) {
        VisualAction* action = new VisualAction();
        top()->push_back(action);
        push(action);
        // Create Data
        InputDataWrapper xy;
        magics::MagRequest& bin = in.getSubRequest("INPUT_BINNING");
        if (bin) {
            in("INPUT_BINNING") = "on";
            xy.set(in);
            bin("INPUT_BINNING") = "on";
            xy.set(bin);
        }
        else {
            in("INPUT_BINNING") = "off";
            xy.set(in);
            bin("INPUT_BINNING") = "off";
            xy.set(bin);
        }
        setIconInfo(in, *xy.object());
        top()->data(xy.object());
    }
    else {
        VisualAction* action = new VisualAction();
        top()->push_back(action);
        push(action);
        // Create Data
        InputDataWrapper xy;
        magics::MagRequest& bin = in.getSubRequest("INPUT_BINNING");
        if (bin) {
            in("INPUT_BINNING") = "on";
            xy.set(in);
            bin("INPUT_BINNING") = "on";
            xy.set(bin);
        }
        else {
            in("INPUT_BINNING") = "off";
            xy.set(in);
            bin("INPUT_BINNING") = "off";
            xy.set(bin);
        }
        setIconInfo(in, *xy.object());
        top()->data(xy.object());
    }


    return false;  // do not exit
}

void MagPlus::setIconInfo(magics::MagRequest& mv, MetviewIcon& object) {
    string iconname  = get(mv, "_NAME", "");
    string iconclass = get(mv, "_CLASS", "");
    if (iconclass.empty())
        iconclass = get(mv, "_VERB", "");
    string iconid = get(mv, "_ID", "");
    object.icon(iconname, iconclass, iconid);
    if (layer_.empty())
        layer_ = "UNKNOW";
    object.layerInfo(visibility_, zindex_, transparency_, id_, layer_);
}

bool MagPlus::gribloop(magics::MagRequest& in) {
#ifdef HAVE_GRIB
    MagLog::dev() << "add gribloop" << endl;
    in.print();
    string loop("loop");
    string mode = get(in, "GRIB_VISIT_MODE", loop);

    if (!magCompare(mode, loop))
        // we assume it is not an animation...
        return grib(in);

    string file = get(in, "GRIB_INPUT_FILE_NAME", "");


    in("GRIB_LOOP_PATH")    = file.c_str();
    VisualAnimation* action = new VisualAnimation();
    top()->push_back(action);
    push(action);

    GribLoopWrapper grib;
    grib.set(in);
    setIconInfo(in, *grib.object());


    action->loop(grib.object());
#else
    MagLog::dev() << "can NOT add gribloop (disabled in Magics!)" << endl;
#endif
    return false;  // do not exit
}

bool MagPlus::rasterloop(magics::MagRequest& in) {
    MagLog::dev() << "add rasterloop" << endl;
    in.print();

    VisualAnimation* geoloop = new VisualAnimation();
    top()->push_back(geoloop);
    push(geoloop);

    ImportPlotWrapper visdef;

    visdef.set(in);

    setIconInfo(in, *visdef.object());

    ImportLoop* loop = new ImportLoop();
    setIconInfo(in, *loop);
    geoloop->loop(loop);
    geoloop->visdef(visdef.object());

    in.countValues("RASTERS");

    magics::MagRequest& rasters = in.getSubRequest("RASTERS");
    rasters.print();

    // At this point we do not know the exact type,
    // later we refine it
    loop->setInfo("_datatype", "RASTERLOOP");

    bool first = true;

    while (rasters) {
        string name   = rasters("LAYERS");
        string format = rasters("IMPORT_FILE_TYPE");
        string path   = rasters("IMPORT_FILE_PATH");
        string type   = rasters("IMPORT_FILE_TYPE");
        string time   = rasters("TIME");  // Should not work for WMS

        rasters("IMPORT_FILE_NAME")  = path;
        rasters("IMPORT_FORMAT")     = rasters("IMPORT_FILE_TYPE");
        rasters("IMPORT_VALID_TIME") = rasters("TIME");

        ImportActionWrapper object;
        object.set(rasters);

        visdef.set(rasters);
        object.object()->icon(name, format);

        // WMS related part

        string service_name = rasters("SERVICE");

        if (service_name == "WMS") {
            string service_title = rasters("SERVICE_TITLE");
            string url           = rasters("URL");
            string title         = rasters("TITLE");
            string description   = rasters("DESCRIPTION");
            string legend        = rasters("LEGEND");
            string logo          = rasters("LOGO");
            string dimName       = rasters("DIM_NAME");
            string dimValue      = rasters("DIM_VALUE");

            if (first) {
                loop->setInfo("_datatype", "RASTERLOOP_WMS");
                loop->setInfo("service_name", service_name);
                loop->setInfo("service_title", service_title);
                loop->setInfo("url", url);
                loop->setInfo("name", name);
                loop->setInfo("title", title);
                loop->setInfo("description", description);
                loop->setInfo("legend", legend);
                loop->setInfo("logo", logo);
                loop->setInfo("dimName", dimName);
                loop->setInfo("dimValue", dimValue);
            }

            object.object()->setInfo("_datatype", "RASTERLOOP_WMS");
            object.object()->setInfo("service_name", service_name);
            object.object()->setInfo("service_title", service_title);
            object.object()->setInfo("url", url);
            object.object()->setInfo("name", name);
            object.object()->setInfo("title", title);
            object.object()->setInfo("description", description);
            object.object()->setInfo("legend", legend);
            object.object()->setInfo("logo", logo);
            object.object()->setInfo("dimName", dimName);
            object.object()->setInfo("dimValue", dimValue);

            // Temporal dimensions
            vector<string> dimNameLst, dimValueLst;

            std::stringstream ssN(dimName);
            std::string item;

            while (std::getline(ssN, item, '/')) {
                dimNameLst.push_back(item);
            }

            std::stringstream ssV(dimValue);
            while (std::getline(ssV, item, '/')) {
                dimValueLst.push_back(item);
            }

            if (dimNameLst.size() == dimValueLst.size()) {
                for (unsigned int i = 0; i < dimNameLst.size(); i++) {
                    object.object()->setInfo(dimNameLst[i], dimValueLst[i]);

                    if (dimNameLst[i] == "TIME" && dimValueLst[i].size() >= 9) {
                        string dataDate = dimValueLst[i].substr(0, 8);
                        string dataTime = dimValueLst[i].substr(8);

                        object.object()->setInfo("date", dataDate);
                        object.object()->setInfo("dataDate", dataDate);
                        object.object()->setInfo("time.dataDate", dataDate);
                        object.object()->setInfo("validityDate", dataDate);
                        object.object()->setInfo("time.validityDate", dataDate);

                        object.object()->setInfo("time", dataTime);
                        object.object()->setInfo("dataTime", dataTime);
                        object.object()->setInfo("time.dataTime", dataTime);
                        object.object()->setInfo("validityTime", dataTime);
                        object.object()->setInfo("time.validityTime", dataTime);
                    }
                    else if (dimNameLst[i] == "DIM_RUN" && dimValueLst[i].size() >= 9) {
                        string dataDate = dimValueLst[i].substr(0, 8);
                        string dataTime = dimValueLst[i].substr(8);

                        object.object()->setInfo("date", dataDate);
                        object.object()->setInfo("dataDate", dataDate);
                        object.object()->setInfo("time.dataDate", dataDate);

                        object.object()->setInfo("time", dataTime);
                        object.object()->setInfo("dataTime", dataTime);
                        object.object()->setInfo("time.dataTime", dataTime);
                    }
                    else if (dimNameLst[i] == "DIM_FORECAST") {
                        object.object()->setInfo("step", dimValueLst[i]);
                        object.object()->setInfo("stepRange", dimValueLst[i]);
                        object.object()->setInfo("time.stepRange", dimValueLst[i]);
                    }
                    else if (dimNameLst[i] == "ELEVATION") {
                        object.object()->setInfo("level", dimValueLst[i]);
                        object.object()->setInfo("vertical.level", dimValueLst[i]);
                    }
                }
            }
        }


        loop->add(object.object());

        rasters.advance();

        first = false;
    }
    pop();
    return false;  // do not exit
}


#ifdef HAVE_ODB
bool MagPlus::geoodb(magics::MagRequest& in) {
    MagLog::dev() << "add geo odb" << endl;
    in.print();
    string path(in("ODB_FILENAME"));
    if (path == "OFF") {
        magics::MagRequest& odb = in.getSubRequest("ODB_DATA");
        path                    = string(odb("PATH"));
    }
    in("ODB_FILENAME") = path.c_str();

    static map<string, string> types;
    if (types.empty()) {
        types["ODB_GEO_POINTS"]  = "geopoint";
        types["ODB_GEO_VECTORS"] = "geovector";
    }

    in("ODB_TYPE")       = types[in.getVerb()].c_str();
    VisualAction* action = new VisualAction();
    top()->push_back(action);
    push(action);

    OdaGeoDecoderWrapper geoodb;
    geoodb.set(in);
    setIconInfo(in, *geoodb.object());
    top()->data(geoodb.object());

    // Meta-data
    geoodb.object()->initInfo();

    return false;  // do not exit
}

bool MagPlus::xyodb(magics::MagRequest& in) {
    MagLog::dev() << "add xy odb" << endl;
    in.print();
    string path(in("ODB_FILENAME"));
    if (path == "OFF") {
        magics::MagRequest& odb = in.getSubRequest("ODB_DATA");
        path                    = string(odb("PATH"));
    }
    in("ODB_FILENAME") = path.c_str();

    static map<string, string> types;
    if (types.empty()) {
        types["ODB_XY_POINTS"]  = "xypoint";
        types["ODB_XY_VECTORS"] = "xyvector";
        types["ODB_XY_BINNING"] = "xybinning";
    }

    in("ODB_TYPE")       = types[in.getVerb()].c_str();
    VisualAction* action = new VisualAction();
    top()->push_back(action);
    push(action);

    OdaXYDecoderWrapper xyodb;

    magics::MagRequest& bin = in.getSubRequest("ODB_BINNING");
    if (bin) {
        in("ODB_BINNING")  = "on";
        bin("ODB_BINNING") = "on";
    }
    else {
        in("ODB_BINNING")  = "off";
        bin("ODB_BINNING") = "off";
    }
    xyodb.set(in);
    xyodb.set(bin);

    setIconInfo(in, *xyodb.object());
    top()->data(xyodb.object());
    geographical_ = false;

    // Meta-data
    xyodb.object()->initInfo();

    return false;  // do not exit
}
#endif

#ifdef HAVE_NETCDF
static map<string, string> nctypes;

void checknctypes() {
    if (nctypes.empty()) {
        nctypes["NETCDF_POINTS"]     = "xypoint";
        nctypes["NETCDF_VECTORS"]    = "vector";
        nctypes["NETCDF_MATRIX"]     = "matrix";
        nctypes["NETCDF_XY_POINTS"]  = "xypoint";
        nctypes["NETCDF_XY_VECTORS"] = "vector";
        nctypes["NETCDF_XY_MATRIX"]  = "matrix";
        nctypes["ARRAY"]             = "matrix";
        nctypes["MATRIX"]            = "complex_matrix";

        nctypes["NETCDF_GEOPOINTS"]   = "geopoint";
        nctypes["NETCDF_GEOVECTORS"]  = "geovector";
        nctypes["NETCDF_GEOMATRIX"]   = "geomatrix";
        nctypes["NETCDF_GEO_POINTS"]  = "geopoint";
        nctypes["NETCDF_GEO_VECTORS"] = "geovector";
        nctypes["NETCDF_GEO_MATRIX"]  = "geomatrix";
    }
}

bool MagPlus::netcdf(magics::MagRequest& in) {
    in.print();
    checknctypes();
    string path = get(in, "NETCDF_FILENAME", "OFF");
    if (path == "OFF") {
        magics::MagRequest& netcdf = in.getSubRequest("NETCDF_DATA");
        path                       = string(netcdf("PATH"));
    }

    in("NETCDF_FILENAME") = path.c_str();
    static map<string, string> types;


    copy_string(in, "NETCDF_X_POSITION_VARIABLE", "NETCDF_X_VARIABLE");
    copy_string(in, "NETCDF_Y_POSITION_VARIABLE", "NETCDF_Y_VARIABLE");

    in.print();

    string type       = get(in, "NETCDF_POSITION_TYPE", in.getVerb());
    in("NETCDF_TYPE") = nctypes[type].c_str();

    VisualAnimation* action = new VisualAnimation();
    top()->push_back(action);
    push(action);

    NetcdfDecoderWrapper geonet;
    geonet.set(in);

    NetcdfLoop* loop = new NetcdfLoop(geonet.object());
    setIconInfo(in, *geonet.object());
    setIconInfo(in, *loop);
    action->loop(loop);

    return false;  // do not exit
}


Data* MagPlus::createnetcdf(magics::MagRequest& in) {
    // Extract the path ..
    MagLog::dev() << "add xy netcdf" << endl;
    in.print();
    checknctypes();
    string path = get(in, "NETCDF_FILENAME", "OFF");
    if (path == "OFF") {
        magics::MagRequest& netcdf = in.getSubRequest("NETCDF_DATA");
        path                       = string(netcdf("PATH"));
    }
    in("NETCDF_FILENAME") = path.c_str();

    string type = get(in, "NETCDF_POSITION_TYPE", in.getVerb());
    copy_string(in, "NETCDF_X_POSITION_VARIABLE", "NETCDF_X_VARIABLE");
    copy_string(in, "NETCDF_Y_POSITION_VARIABLE", "NETCDF_Y_VARIABLE");


    NetcdfDecoderWrapper netcdf;
    netcdf.set(in);
    netcdf.object()->initInfo();

    return netcdf.object();
}
#endif  // NetCDF

Data* MagPlus::createGeopoints(magics::MagRequest& in) {
    // Extract the path ..
    magics::MagRequest& record = in.getSubRequest("RECORD");

    string path               = record("PATH");
    in("GEO_INPUT_FILE_NAME") = path;


    GeoPointsDecoderWrapper geopoints;
    geopoints.set(in);


    // Meta-data
    geopoints.object()->initInfo();
    return geopoints.object();
}


bool MagPlus::geopoints(magics::MagRequest& in) {
#ifdef test_dataloop
    in.read("/tmp/cgs/jira/MAGP-268/dataloop");
    dataloop(in);
    return false;
#endif

    // Extract the path ..
    magics::MagRequest& record = in.getSubRequest("RECORD");

    string path               = record("PATH");
    in("GEO_INPUT_FILE_NAME") = path;
    in.print();
    VisualAction* action = new VisualAction();
    top()->push_back(action);
    push(action);

    GeoPointsDecoderWrapper geopoints;
    geopoints.set(in);
    setIconInfo(in, *geopoints.object());
    top()->data(geopoints.object());

    // Meta-data
    geopoints.object()->initInfo();

    return false;  // do not exit
}

bool MagPlus::geojson(magics::MagRequest& in) {
    VisualAction* action = new VisualAction();
    top()->push_back(action);
    push(action);

    GeoJSonWrapper geo;
    geo.set(in);
    setIconInfo(in, *geo.object());
    top()->data(geo.object());

    // Meta-data
    geo.object()->initInfo();

    return false;  // do not exit
}

bool MagPlus::bufr(magics::MagRequest& in) {
#ifdef HAVE_BUFR
    /*
    // Extract the path ..
    magics::MagRequest record = in("RECORD");

    in("GEO_INPUT_FILE_NAME") = record("PATH");
    */
    in.print();
    VisualAction* action = new VisualAction();
    top()->push_back(action);
    push(action);

    ObsDecoderWrapper obs;
    obs.set(in);
    top()->data(obs.object());
    setIconInfo(in, *obs.object());
#endif
    return false;  // do not exit
}
bool MagPlus::symbol(magics::MagRequest& in) {
    if (in.countValues("SYMBOL_INPUT_MARKER_LIST")) {
        in("SYMBOL_MARKER") = in("SYMBOL_INPUT_MARKER_LIST");
    }
    string verb = in.getVerb();
    if (verb == "PSYMBPLUS") {
        in("SYMBOL_TABLE_MODE") = "advanced";
        in("SYMBOL_TYPE")       = "marker";
    }

    FortranAutomaticLegendVisitor* node = new FortranAutomaticLegendVisitor();
    LegendMethod* method                = new ContinuousLegendMethod();
    node->method_                       = unique_ptr<LegendMethod>(method);
    node->getReady();
    // top()->legend(node);
    if (geographical_) {
        SymbolPlottingWrapper symbol;
        symbol.set(in);
        setIconInfo(in, *symbol.object());
        MagLog::dev() << "add symbol" << *symbol.object() << endl;
        top()->visdef(symbol.object());
        pop();
    }
    else {
        SymbolPlottingWrapper symbol;
        symbol.set(in);
        setIconInfo(in, *symbol.object());
        MagLog::dev() << "add symbol" << *symbol.object() << endl;
        top()->visdef(symbol.object());
        pop();
    }

    return false;  // do not exit
}

bool MagPlus::graph(magics::MagRequest& in) {
    GraphPlottingWrapper graph;
    graph.set(in);
    MagLog::dev() << "add graph" << *graph.object() << endl;
    top()->visdef(graph.object());
    pop();
    return false;  // do not exit
}

bool MagPlus::obs(magics::MagRequest& in) {
#ifdef HAVE_BUFR
    ObsPlottingWrapper visdef;

    visdef.set(in);

    MagLog::dev() << "add obs" << *visdef.object() << endl;
    top()->visdef(visdef.object());
    pop();


#endif
    return false;  // do not exit
}
bool MagPlus::dataloop(magics::MagRequest& in) {
    MagLog::dev() << "add generic dataloop" << endl;
    in.print();

    VisualAnimation* loop = new VisualAnimation();
    top()->push_back(loop);
    push(loop);


    DataList* data = new DataList();
    setIconInfo(in, *data);
    loop->loop(data);


    magics::MagRequest& entries = in.getSubRequest("ENTRIES");


    // At this point we do not know the exact type,
    // later we refine it
    loop->setInfo("_datatype", "DATALOOP");


    while (entries) {
        string verb = entries.getVerb();

        map<string, DataCreator>::iterator creator = dataCreators_.find(verb);
        if (creator != dataCreators_.end()) {
            magics::MagRequest& entry = entries.justOneRequest();
            Data* x                   = (this->*creator->second)(entry);
            data->add(x);
        }
        MagLog::warning() << "Ignore entry " << verb << endl;
        entries.advance();
    }

    return false;  // do not exit
}

bool MagPlus::wind(magics::MagRequest& in) {
    WindWrapper wind;
    wind.set(in);
    setIconInfo(in, *wind.object());
    MagLog::dev() << "add wind" << *wind.object() << endl;
    if (currentMulti_) {
        currentMulti_->push_back(wind.object());
    }
    else {
        top()->visdef(wind.object());
        pop();
    }

    return false;  // do not exit
}

bool MagPlus::visdef(magics::MagRequest& in) {
    MagLog::dev() << "found visdef" << endl;

    MagRequest& visdefs = in.getSubRequest("ACTIONS");

    visdefs.print();
    bool dopop = false;
    while (visdefs) {
        string verb = visdefs.getVerb();
        {
            map<string, ObjectCreator>::iterator creator = sceneHandler_->find(verb);
            if (creator != sceneHandler_->end()) {
                magics::MagRequest& visdef = visdefs.justOneRequest();
                // we keep the action
                BasicSceneObject* action = top();
                (this->*creator->second)(visdef);
                // we pout it back!
                push(action);
                dopop = true;
            }
        }
        visdefs.advance();
    }
    if (dopop)
        pop();
    MagLog::dev() << "<---end visdef" << endl;
    return false;
}

bool MagPlus::multi(magics::MagRequest& in) {
    MultiVisdef* multi = new MultiVisdef();

    top()->visdef(multi);
    MagRequest& visdef1 = in.getSubRequest("1D_VISDEF");
    MagRequest& visdef2 = in.getSubRequest("2D_VISDEF");
    pop();
    visdef1.print();
    visdef2.print();
    while (visdef1) {
        currentMulti_ = multi->oneDimension();
        string verb   = visdef1.getVerb();
        {
            map<string, ObjectCreator>::iterator creator = sceneHandler_->find(verb);
            if (creator != sceneHandler_->end()) {
                magics::MagRequest& visdef = visdef1.justOneRequest();
                (this->*creator->second)(visdef);
            }
        }
        visdef1.advance();
    }


    visdef2.print();
    while (visdef2) {
        currentMulti_ = multi->twoDimension();
        string verb   = visdef2.getVerb();
        {
            map<string, ObjectCreator>::iterator creator = sceneHandler_->find(verb);
            if (creator != sceneHandler_->end()) {
                magics::MagRequest& visdef = visdef2.justOneRequest();
                (this->*creator->second)(visdef);
            }
        }
        visdef2.advance();
    }
    currentMulti_ = 0;
    ;
    MagLog::dev() << "<---end visdef" << endl;
    return false;
}


bool MagPlus::contour(magics::MagRequest& in) {
    MagLog::dev() << "add contour" << endl;

    // replace(in, "CONTOUR_LABEL_HEIGHT", 0.3, 0.2);
    string legend = get(in, "CONTOUR_LEGEND", "ON");

    ContourWrapper contour;
    contour.set(in);
    setIconInfo(in, *contour.object());
    if (currentMulti_)
        currentMulti_->push_back(contour.object());
    else {
        top()->visdef(contour.object());
        pop();
    }

    return false;  // do not exit
}

bool MagPlus::ptext(magics::MagRequest& in) {
    MagLog::dev() << "add Text" << endl;
    in.print();
    sceneCreators_["MLEGEND"] = &MagPlus::ignore;

    replace(in, "TEXT_REFERENCE_CHARACTER_HEIGHT", 2.0, "TEXT_FONT_SIZE", 0.3);
    replace_string(in, "TEXT_COLOUR", "BLUE", "navy");
    in("TEXT_HTML") = "on";
    text(in);
    legend(in);

    return false;  // do not exit
}

bool MagPlus::text(magics::MagRequest& in) {
    MagLog::dev() << "add Text-->" << endl;
    in.print();
    MagLog::dev() << "<--add Text" << endl;
    string mode = get(in, "TEXT_MODE", "automatic");


    in("TEXT_HTML") = "on";

    replace_default(in, "TEXT_FONT_SIZE", "0.3");
    replace_default(in, "TEXT_COLOUR", "navy");

    TextVisitor* node;
    if (magCompare(mode, "positional"))
        node = new FortranPositionalTextVisitor();
    else
        node = new FortranAutomaticTextVisitor();

    TextVisitorWrapper helper(node);
    helper.set(in);
    setIconInfo(in, *helper.object());
    top()->text(node);


    return false;  // do not exit
}


bool MagPlus::legend(magics::MagRequest& in) {
    MagLog::dev() << "add legend-->" << endl;
    in.print();
    MagLog::dev() << "<--add legend" << endl;
    string mode = get(in, "LEGEND_BOX_MODE", "automatic");
    replace_default(in, "LEGEND_TEXT_COLOUR", "navy");
    replace_default(in, "LEGEND_DISPLAY_TYPE", "continuous");
    replace_default(in, "LEGEND_TEXT_FONT_SIZE", "0.2");
    LegendVisitor* legend = nullptr;
    if (magCompare(mode, "positional")) {
        legend = new FortranPositionalLegendVisitor();
    }
    else
        legend = new FortranAutomaticLegendVisitor();
    LegendVisitorWrapper helper(legend);
    helper.set(in);
    setIconInfo(in, *helper.object());
    top()->legend(legend);
    return false;  // do not exit
}

bool MagPlus::ignore(magics::MagRequest&) {
    return false;  // do not exit
}

bool MagPlus::device(magics::MagRequest& in) {
    MagLog::dev() << "add device" << endl;
    in.print();
    XmlNode* driver = 0;
    if (!in.countValues("FORMAT"))
        return false;
    string fmt = in("FORMAT");


    string format(fmt);

    if (format == "POSTSCRIPT") {
        map<string, string> attributes;
        attributes["output_fullname"] = string(in("FILE"));

        driver = new XmlNode("ps", attributes);
    }

    output_.set(*driver, drivers_);
    if (driver)
        delete (driver);
    return false;  // do not exit
}


void MagPlus::execute(magics::MagRequest& in) {
    in.print();

    try {
        // Start from a fersh tree!
#ifndef MAG_NEXT
        if (root_) {
            delete root_;
            root_      = 0;
            superpage_ = -1;
            drivers_.clear();
            mode_ = interactif;
        }
        sceneHandler_ = &sceneCreators_;
#else
        mvMode_       = (root_) ? update : creation;
        sceneHandler_ = (root_) ? &sceneUpdators_ : &sceneCreators_;

#endif

        while (in) {
            string verb = in.getVerb();
            MagLog::dev() << "create-->" << verb << endl;
            {
                map<string, ObjectCreator>::iterator creator = sceneHandler_->find(verb);
                if (creator != sceneHandler_->end()) {
                    magics::MagRequest& request = in.justOneRequest();
                    if ((this->*creator->second)(request))
                        return;
                }
            }
            {
                map<string, ObjectCreator>::iterator creator = driverCreators_.find(verb);
                if (creator != driverCreators_.end()) {
                    magics::MagRequest& request = in.justOneRequest();
                    if ((this->*creator->second)(request))
                        return;
                }
            }
            in.advance();
        }
        if (!root_) {
            MagLog::warning() << "Sorry, nothing to to display!" << endl;
            return;
        }
        ASSERT(root_);

        root_->getReady();
        drivers_.setDriversWidth(root_->absoluteWidth());
        drivers_.setDriversHeight(root_->absoluteHeight());
        root_->mode(mode_);

        root_->execute();

        drivers_.openDrivers();
        drivers_.dispatch(root_->root());
        drivers_.closeDrivers();
        MetaDataVisitor::collect();
    }
    catch (MagicsException& e) {
        /*! \todo Why is this MagMagException empty???  */
        MagLog::error() << "Something went really wrong!" << e << endl;
    }
}


void MagPlus::notify(MagicsEvent& event) {
    MagLog::dev() << "NOTIFY---" << event << endl;
    for (vector<MagicsObserver*>::iterator observer = observers_.begin(); observer != observers_.end(); ++observer)
        event.notify(**observer);
}

void MagPlus::unregisterObserver(MagicsObserver* observer) {
    observers_.erase(
        std::remove_if(observers_.begin(), observers_.end(), bind2nd(std::equal_to<MagicsObserver*>(), observer)),
        observers_.end());
}

void setDouble(const string& key, const ParamJSon& json, MagRequest& out) {
    ParamJSon::const_iterator param = json.find(key);
    if (param != json.end())
        out(key) = tonumber(param->second);
}

void setString(const string& key, const ParamJSon& json, MagRequest& out) {
    ParamJSon::const_iterator param = json.find(key);
    if (param != json.end())
        out(key) = param->second;
}
void MagPlus::decode(MagRequest& out, const string& json) {
    ParamJSon params(json);
    /*
        '{"subpage_lower_left_latitude" : "40.8424",\
        "subpage_lower_left_longitude" : "-20.5033",\
        "subpage_map_area_definition" : "corners",\
        "subpage_map_hemisphere" : "north",\
        "subpage_map_projection" : "polar_stereographic",\
        "subpage_map_vertical_longitude" : "0",\
        "subpage_upper_right_latitude" : "62.073",\
        "subpage_upper_right_longitude" : "28.3054"}'
    */
    setDouble("subpage_lower_left_latitude", params, out);
    setDouble("subpage_lower_left_longitude", params, out);
    setString("subpage_map_area_definition", params, out);
    setString("subpage_map_hemisphere", params, out);
    setString("subpage_map_projection", params, out);
    setDouble("subpage_map_vertical_longitude", params, out);
    setDouble("subpage_upper_right_latitude", params, out);
    setDouble("subpage_upper_right_longitude", params, out);
}

//_____________________________________________________________________

#ifdef STANDALONE
int main(int argc, char** argv) {
    MvApplication theApp(argc, argv);
    MagPlusService magplus;
    theApp.run();
}
#endif
