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

#include "CartesianView.h"
#include "DataObject.h"
#include "MvDecoder.h"
#include "MvLog.h"
#include "ObjectList.h"
#include "Page.h"
#include "PlotMod.h"
#include "PmContext.h"
#include "Root.h"
#include "SubPage.h"

#include "MvRequestUtil.hpp"

// Class CartesianViewFactory - builds service views
class CartesianViewFactory : public PlotModViewFactory
{
    // Virtual Constructor - Builds a new CartesianView
    PlotModView* Build(Page& page,
                       const MvRequest& contextRequest,
                       const MvRequest& setupRequest) override
    {
        return new CartesianView(page, contextRequest, setupRequest,
                                 "BACKGROUND_HORIZONTAL_AXIS", "BACKGROUND_VERTICAL_AXIS", "CartesianView");
    }

public:
    CartesianViewFactory() :
        PlotModViewFactory("CartesianView") {}
};

static CartesianViewFactory CartesianViewFactoryInstance;

CartesianView::CartesianView(Page& owner,
                             const MvRequest& viewRequest,
                             const MvRequest& setupRequest,
                             const std::string& hAxisName,
                             const std::string& vAxisName,
                             const std::string& viewName) :
    PlotModView(owner, viewRequest, setupRequest),
    hAxisName_(hAxisName),
    vAxisName_(vAxisName),
    viewName_(viewName)
{
    // If this constructor was called due to a data plotting request then
    // both axes min/max values should be defined by the data.
    // Once the data request is decoded later, the axes min/max values should
    // be updated. This is important for the zoom procedure because the first
    // values in the zoom stack should contain the correct values, not the
    // default ones, as initialized below.
    const char* origin = viewRequest("_ORIGIN");
    if (origin && strcmp(origin, "DataBuilder") == 0) {
        viewRequest_("X_AUTOMATIC") = "ON";
        viewRequest_("Y_AUTOMATIC") = "ON";
    }

    // Create AREA parameter to be used by the Projection (SetProjection)
    MvRequest viewRequest1 = viewRequest_;
    viewRequest1("AREA") = viewRequest1("Y_MIN");
    viewRequest1("AREA") += viewRequest1("X_MIN");
    viewRequest1("AREA") += viewRequest1("Y_MAX");
    viewRequest1("AREA") += viewRequest1("X_MAX");

    // Set projection
    viewRequest1("MAP_PROJECTION") = "CARTESIANVIEW";
    owner.SetProjection(viewRequest1);
}

CartesianView::~CartesianView() = default;

void CartesianView::GetBackgroundParameters(std::vector<std::string>& vback)
{
    // Get map projection
    std::string sproj;
    if (const char* prCh = (const char*)viewRequest_("MAP_PROJECTION"))
        sproj = std::string(prCh);

    if (sproj.empty())
        sproj = "cartesian";

    // Get background parameters
    if (sproj == "cartesian") {
        vback.emplace_back("HORIZONTAL_AXIS");
        vback.emplace_back("VERTICAL_AXIS");
    }
    else if (sproj == "taylor") {
        vback.emplace_back("HORIZONTAL_AXIS");
        vback.emplace_back("VERTICAL_AXIS");
        vback.emplace_back("TAYLOR_GRID");
    }

    return;
}

std::string
CartesianView::Name()
{
    int id = Owner().Id();
    return (const char*)ObjectInfo::ObjectName(viewRequest_, "CartesianView", id);
}

void CartesianView::Drop(PmContext& context)
{
    MvIconDataBase& dataBase = Owner().IconDataBase();

    // Process the drop
    MvRequest dropRequest = context.InRequest();
    MvIconList duList;
    while (dropRequest) {
        // Taylor grid
        // This test needs to be before checking the visdefs below
        // because this icon is a visdef related to the View and not
        // to the DataUnit (similar to ThermoGrid and Axis)
        if (ObjectList::IsTaylorGrid(dropRequest.getVerb())) {
            // Add definition to the View request
            viewRequest_("TAYLOR_GRID") = dropRequest.justOneRequest();
            dropRequest.advance();
            continue;
        }

        // VisDefs are processed in one single goal. This is because the processing
        // of a single or a group of visdefs is different. It is assumed that  visdefs
        // always come after a dataunit (if it exists).
        MvRequest vdRequestList;
        if (this->RetrieveVisdefList(dropRequest, vdRequestList)) {
            Owner().InsertVisDef(vdRequestList, duList);

            // Clear dataUnitList variable to avoid a possible visdef in the next
            // iteraction to be associate to an old dataUnit.
            if (!duList.empty())
                duList.clear();

            if (!dropRequest)  // no more drops
                break;
        }

        MvRequest request = dropRequest.justOneRequest();
        const char* verb = request.getVerb();
        if (ObjectList::IsDataUnit(verb) == true) {
            MvIcon dataUnit = dataBase.InsertDataUnit(request, Owner().Id());
            duList.push_back(dataUnit);
            ParentDataUnitId(dataUnit.Id());  // save to be used when returning from service
            DecodeDataUnit(dataUnit);

            // Update the View, if it is a default one
            this->UpdateAxisTypeView(request);

            dropRequest.advance();
            continue;
        }

        if (ObjectList::IsVisDefAxis(verb))
            this->ProcessAxis(request);  // this->ReplaceAxis(request);

        // Replace the view (if applicable)
        else if (ObjectList::IsView(verb) == true)
            this->UpdateViewWithReq(request);

        else if ((const char*)verb == PLOTSUPERPAGE) {
            context.AdvanceTo(PLOTSUPERPAGE);
            return;
        }
        else if ((const char*)verb == NEWPAGE) {
            context.AdvanceTo(NEWPAGE);
            return;
        }
        else if (verb == Cached("DRAWING_PRIORITY")) {
            Owner().SetDrawPriority(request);

            // Redraw this page
            Owner().RedrawIfWindow();
        }
        else
            Owner().InsertCommonIcons(request);

        // This request is not a dataUnit. Clear dataUnitList variable
        // to avoid a possible visdef in the next iteraction to be
        // associate to an old dataUnit.
        if (!duList.empty())
            duList.clear();

        dropRequest.advance();
    }

    // Consume all of the input request (the service will use it)
    context.AdvanceToEnd();
}

// Update the View, if it is a default one.
// If the Data request sets X/Y axis type to "DATE" then the default
// View request needs to be updated accordingly. This is because Magics
// receives the Data request after the View request and it needs to
// know at the View level if one or both axes are of type "DATE".
void CartesianView::UpdateAxisTypeView(MvRequest& reqData)
{
    // Return if it is not a default view
    if (!(const char*)viewRequest_("_DEFAULT"))
        return;

    // Update X axis type. Assumes that there is only one parameter
    // with sufix equals to "_X_TYPE"
    MvRequest reqAux("XY_TYPE");
    std::string sufix = "_X_TYPE";
    metview::CopySomeParameters(reqData, reqAux, sufix);
    const char* param = (const char*)reqAux.getParameter(0);
    if (param && strcmp(reqAux(param), "DATE") == 0)
        viewRequest_("X_AXIS_TYPE") = "DATE";

    // Update Y axis type. Assumes that there is only one parameter
    // with sufix equals to "_Y_TYPE"
    reqAux.clean();
    reqAux.setVerb("XY_TYPE");
    sufix = "_Y_TYPE";
    metview::CopySomeParameters(reqData, reqAux, sufix);
    param = (const char*)reqAux.getParameter(0);
    if (param && strcmp(reqAux(param), "DATE") == 0)
        viewRequest_("Y_AXIS_TYPE") = "DATE";
}

void CartesianView::ProcessAxis(MvRequest& axisRequest)
{
#if 0
    // Two main procedures:
    // A) if the icon came from a Macro then keep it in the same position
    //    as defined in the Plot command
    // B) if the icon came from a Drop then replace the one defined in
    //    the View request

    // Icon from a Macro
    if ( PlotMod::Instance().CalledFromMacro() )
    {
        MvIconDataBase& dataBase = Owner().IconDataBase();
        coastRequest("_PLOTTING_ORDER") = foundDU ? "FOREGROUND" : "BACKGROUND";
        MvIcon icon(coastRequest,true);
        dataBase.InsertIcon( PRES_VISDEF_REL, Owner().Id(), icon, -1, false );
    }
    else // icon from a Drop
        viewRequest_ ( "COASTLINES" ) = coastRequest;

    // Redraw this page
//    Owner().EraseDraw();
    Owner().RedrawIfWindow();
#else
    MvIconDataBase& dataBase = Owner().IconDataBase();
    MvIcon icon(axisRequest, true);
    dataBase.InsertIcon(PRES_VISDEF_REL, Owner().Id(), icon, -1, false);
#endif
}

void CartesianView::ReplaceAxis(MvRequest& axisRequest)
{
    // Get axis orientation
    const char* cori = axisRequest("AXIS_ORIENTATION");
    if (!cori)
        cori = "HORIZONTAL";  // default

    // Unset previous axis definition from the view, if exists
    std::string sorient = cori + (std::string) "_AXIS";
    MvRequest newView = viewRequest_;
    newView.unsetParam(sorient.c_str());

    // Add new axis definition and update the view
    newView(sorient.c_str()) = axisRequest;
    Owner().UpdateView(newView);
}

// -- METHOD: DecodeDataUnit
//
// -- PURPOSE:
//    . Extract the data and the metadata from a request
//    . Provides the proper axis and graph definition according
//      to the metada
//
void CartesianView::DecodeDataUnit(MvIcon& givenUnit)
{
    //	MvIconDataBase&   dataBase = Owner().IconDataBase();

    Owner().InitMatching();

    // Build a new data decoder, which will provide information
    // about the data
    std::unique_ptr<Decoder> decoder(DecoderFactory::Make(givenUnit.Request()));
    ensure(decoder.get() != nullptr);

    int subpageId = 0;
    double minX = 0., maxX = 0., minY = 0., maxY = 0.;

    while (decoder->ReadNextData()) {
        MvRequest dataRequest = decoder->Request();

        decoder->GetMinMax(minX, maxX, minY, maxY);

        // Insert the dataunit in the data base
        //		MvIcon dataUnit = dataBase.InsertDataUnit ( dataRequest );
        //		dataUnit.ParentId ( this->ParentDataUnitId () );
        givenUnit.ParentId(this->ParentDataUnitId());

        // Pass the data and metadata info to the page
        MatchingInfo dataInfo = decoder->CreateMatchingInfo();

        //		Owner().InsertDataUnit ( dataUnit.Id(), 0, 0, dataInfo, subpageId );
        Owner().InsertDataUnit(givenUnit.Id(), 0, 0, dataInfo, subpageId);
    }
}

void CartesianView::DescribeYourself(ObjectInfo& description)
{
    // Convert my request to macro
    std::set<Cached> skipSet;
    description.ConvertRequestToMacro(viewRequest_, PUT_END, MacroName().c_str(), viewName_.c_str(), skipSet);
}

void CartesianView::DescribeAxis(ObjectInfo& description,
                                 MvRequest& axisRequest,
                                 const Cached& axisName)
{
    description.ConvertRequestToMacro(axisRequest, PUT_END, axisName, "maxis");
}

MvIconList
CartesianView::InsertDataRequest(MvRequest& dropRequest)
{
    MvIconDataBase& dataBase = Owner().IconDataBase();

    // Process the request
    MvIconList duList;
    while (dropRequest) {
        MvRequest dataUnitRequest = dropRequest.justOneRequest();
        std::string verb = dataUnitRequest.getVerb();

        if (verb == NETCDF) {
            MvIcon tmpIcon(dataUnitRequest);
            DecodeDataUnit(tmpIcon);

            MvIcon parentDataUnit;
            dataBase.RetrieveIconFromList(DB_DATAUNIT, this->ParentDataUnitId(), parentDataUnit);
            parentDataUnit.SvcRequest(dataUnitRequest);

            // Add dataUnit icon to the output list
            duList.push_back(parentDataUnit);
        }

        dropRequest.advance();
    }

    return duList;
}

#if 0
void CartesianView::HandleAxisData ( MvIcon &dataUnit, double minx,double maxx,
				     double miny, double maxy)
{
	MvRequest dataRequest = dataUnit.Request();
	MvIconDataBase& dataBase = Owner().IconDataBase();

	MvRequest xAxis = viewRequest_.getSubrequest( hAxisName_.c_str() );
	MvRequest yAxis = viewRequest_.getSubrequest( vAxisName_.c_str() );

	if ( FlipMinMax() )
	{
		double temp = miny;
		miny = maxy;
		maxy = temp;
	}

	if ( !xAxis) xAxis.setVerb("PAXIS");
	if ( !yAxis)
	{
		yAxis.setVerb("PAXIS");
		yAxis("AXIS_ORIENTATION") = "VERTICAL";
	}

	if ( ! (const char *)xAxis("AXIS_MIN_VALUE")  )
		xAxis("AXIS_MIN_VALUE") = minx;

	if ( ! ( const char *)xAxis("AXIS_MAX_VALUE") )
		xAxis("AXIS_MAX_VALUE") = maxx;

	if ( ! ( const char *)yAxis("AXIS_MIN_VALUE") )
		yAxis("AXIS_MIN_VALUE") = miny;

	if ( ! ( const char *)yAxis("AXIS_MAX_VALUE") )
		yAxis("AXIS_MAX_VALUE") = maxy;

	dataRequest = dataRequest + xAxis + yAxis;
	dataBase.UpdateDataUnit(dataUnit.Id(), dataRequest );
}
#endif

void CartesianView::Draw(SubPage* subPage)
{
    MvIconDataBase& dataBase = Owner().IconDataBase();

    DrawingPriority& tmpDrawingPriority = Owner().GetDrawPriority();  // Get from Page

    // Get the data units for this subpage.
    std::list<DataObject*> doList;
    subPage->GetDataObjects(doList);
    std::list<DataObject*>::iterator ii;
    DataObject* dataObject = nullptr;
    DrawPriorMap drawPriorMap;
    DrawPriorMap::iterator j;

    //  MvIconList  duList;
    std::multimap<int, MvIcon> duMap;
    MvIcon dataUnit;

    std::map<int, MvRequest> reqMap;
    //	AxisInfoMap::iterator axisii;
    //	double MinX,MaxX,MinY,MaxY;
    //	bool first = true;
    for (ii = doList.begin(); ii != doList.end(); ii++) {
        dataObject = (*ii);

        if (!dataBase.RetrieveIconFromList(DB_DATAUNIT, dataObject->DataUnitId(), dataUnit))
            continue;

        // duList.push_back(dataUnit);
        reqMap[dataUnit.Id()] = dataUnit.Request();

        //		axisii = axisInfoMap_.find(dataUnit.Id() );
        //		if ( axisii == axisInfoMap_.end() )
        //			continue;

        //		CheckMinMax(MinX,MaxX,MinY,MaxY,(*axisii).second,first);

        // Retrieve visdef associated to this dataunit
        MvIconList vdList;
        dataObject->RetrieveMyVisDefList(dataUnit, vdList);

        // Unfortunately the data unit ( if it's a curve) will need
        // to know which visdef it has in order for magics translator
        // to set the correct values ( BAR...VALUES, CURVE..VALUES etc).
        // So we need to add a GRAPH_TYPE field to dataunit request
        // for the benefit of the translator.
        std::string type = "PGRAPH_GRAPH_TYPE_";
        type += CheckDataRequest(dataUnit, vdList);
        int drawPrior = tmpDrawingPriority.GetPriority(type);
        duMap.insert(std::pair<const int, MvIcon>(drawPrior, dataUnit));

        DrawPriorMap tmpMap = dataObject->DrawPriority(vdList);
        for (j = tmpMap.begin(); j != tmpMap.end(); ++j)
            drawPriorMap.insert(*j);
    }

    // Have all the data units, the min/max values for the subpage, and
    // have saved all therequests for the dataunits. All the dataunits/vd's
    // was added by DrawPriorMap code above.

    // However, we need to cheat with the axis. Get axislist for subpage,
    // set  min/max if not hardcoded, and put the axis on the end of the
    // dataunit's request. If we're interactively, all requests must have
    //  the axis tagged on, otherwise Magics tries to make it's own, but
    //  for print, only one data unit should have axis.
    //	MvIconList axisList;
    //	MvRequest fullAxisRequest;
    //	MvRequest compare("PAXIS");
    //	bool defaultVd = GetVisDef(*subPage,compare,axisList);

    //	if ( defaultVd )
    //		axisList.erase(axisList.begin(), axisList.end() );

    //	FindAxis(fullAxisRequest,axisList,MinX,MaxX,MinY,MaxY);

#if 0  // F0410
	first = true;
	for (  std::multimap<int,MvIcon>::iterator jj = duMap.begin(); jj != duMap.end(); jj++ )
	{
		bool addAxis = ( first || subPage->IsAWindow() );
		MergeAxisInfo((*jj).second, fullAxisRequest,addAxis);
		first = false;
	}
#endif

    // Do the drawing
    CommonDraw(subPage, drawPriorMap);

#if 0  // FAMI 20100922 Is this needed in Metview 4?
	CommonDraw(subPage, drawPriorMap, reqMap);
	// Reset the data requests
	std::map<int,MvRequest>::iterator kk;
	for ( kk = reqMap.begin(); kk != reqMap.end(); kk++ )
	{
		if ( ! dataBase.RetrieveDataUnit ((*kk).first, dataUnit) )
			continue;

		dataUnit.SaveRequest((*kk).second);
	}
#endif
}

void CartesianView::DrawBackground()
{
    // Retrieve background request
    MvRequest backList;
    if (this->RetrieveBackground(backList) == 0)
        return;  // nothing to be plotted

    // Loop all requests
    GraphicsEngine& ge = Owner().GetGraphicsEngine();
    while (backList) {
        // Draw the layer info
        MvRequest req = backList.justOneRequest();
        MvIcon icon(req, true);
        Owner().DrawLayerInfo(icon.Id(), "AXIS LAYER");

        // Ask the graphics engine to draw the coastlines
        ge.Draw(req, true);

        backList.advance();
    }

    Owner().SetProjection(viewRequest_);
}

int CartesianView::RetrieveBackground(MvRequest& backReq)
{
    // Inquire the background parameters according to the projection
    std::vector<std::string> vback;
    this->GetBackgroundParameters(vback);
    if (vback.size() == 0)
        return 0;  // no background parameters

    // Get icons using the following order: View, DataBase, default
    // Retrieve icons from the View
    std::vector<std::string> vbackAux;
    int ncount = 0;
    int nc = vback.size();
    for (int i = 0; i < nc; i++) {
        if ((const char*)viewRequest_(vback[i].c_str())) {
            backReq = viewRequest_.getSubrequest(vback[i].c_str());
            ncount++;
        }
        else
            vbackAux.push_back(vback[i]);
    }

    if (vbackAux.empty())
        return ncount;  // all background parameters taken from the View

    // Retrieve icons from the DataBase
    MvIconDataBase& dataBase = Owner().IconDataBase();
    MvIconList iconList;
    if (dataBase.RetrieveIcon(PRES_VISDEF_REL, Owner().Id(), iconList)) {
        MvListCursor ii;
        for (ii = iconList.begin(); ii != iconList.end(); ii++) {
            MvRequest currentReq = (*ii).Request();
            currentReq.print();
        }
    }

    return ncount;
}

#if 0
int
CartesianView::RetrieveForeground( MvRequest& foreReq )
{
   // Get icons from the Database first. If there is none then get the icon
   // from the View.
   int ncount = this->RetrieveBackForeground("FOREGROUND",foreReq);
   if ( ncount )
      return ncount;

   // Retrieve icon from the View
   foreReq = viewRequest_.getSubrequest ("COASTLINES");

   // Account for land-sea shading. If they are setted to ON then 
   // they were previously drawn on the Background by default. 
   const char* onoff = (const char*)foreReq ( "MAP_COASTLINE_LAND_SHADE" );
   if ( onoff && strcmp(onoff,"ON") == 0 )
      foreReq ("MAP_COASTLINE_LAND_SHADE") = "OFF";

   onoff = (const char*)foreReq ( "MAP_COASTLINE_SEA_SHADE" );
   if ( onoff && strcmp(onoff,"ON") == 0 )
      foreReq ("MAP_COASTLINE_SEA_SHADE") = "OFF";

   // Add extra information
   if ( !(const char*)foreReq("_ID") )
   {
      MvIcon icon(foreReq,true);     //generate _ID and save it
      foreReq = icon.Request();
   }

   return 1;
}


int
CartesianView::RetrieveBackForeground( const char* stype, MvRequest& req )
{
   // Get icons from the database (if any)
   req.clean();
   MvIconDataBase& dataBase = Owner().IconDataBase();
   MvIconList iconList;
   if ( dataBase.RetrieveIcon ( PRES_VISDEF_REL, Owner().Id(), iconList ) == 0 )
      return 0;

   // Save the requested type icons
   int ncount = 0;
   MvListCursor ii;
   for ( ii = iconList.begin(); ii != iconList.end(); ii++ )
   {
      MvRequest currentReq = (*ii).Request();
      if ( ObjectList::IsVisDefBackForeground(currentReq.getVerb()) )
      {
         if ( (const char*)currentReq("_PLOTTING_ORDER") &&
              (std::string(stype) == (const char*)currentReq("_PLOTTING_ORDER")) )
         {
            req = req + currentReq;
            ncount++;
         }
      }
   }

   return ncount;
}
#endif

#if 0
void CartesianView::CheckAxisRequest ( MvRequest &axis,double minX,double maxX,
				       double minY, double maxY, bool &foundX,
				       bool &foundY )
{
	double minVal, maxVal;
	bool isVertical =  ( axis( "AXIS_ORIENTATION" ) == Cached ("VERTICAL" ) );

	if ( isVertical)
		foundY = true;
	else 
		foundX = true;

	// Just return if axis max value is set or max/min values not set
	if ( (minX == maxX && minY == maxY) || (const char *)axis("AXIS_MAX_VALUE") )
		return;

	if ( isVertical )
	{
		if ( FlipMinMax() )
		{
			double temp = minY;
			minVal = maxY;
			maxVal = temp;
		}
		else
		{
			maxVal = maxY;
			minVal = minY;
		}
	}
	else
	{
		maxVal = maxX;
		minVal = minX;
	}

	axis("AXIS_MIN_VALUE") = minVal;
	axis("AXIS_MAX_VALUE") = maxVal;
}

void CartesianView::CheckMinMax ( double &minX,double &maxX,
				  double& minY, double &maxY,
				  AxisInfo& currAI, bool& first )
{
	if ( first )
	{
		minX = currAI.xMin_;
		maxX = currAI.xMax_;
		minY  = currAI.yMin_;
		maxY = currAI.yMax_;
		first = false;
	}
	else 
	{
		if ( currAI.xMin_ < minX ) minX = currAI.xMin_;
		if ( currAI.xMax_ > maxX ) maxX = currAI.xMax_;
		if ( currAI.yMin_ < minY ) minY = currAI.yMin_;
		if ( currAI.yMax_ > maxY ) maxY = currAI.yMax_;
	}
}

void CartesianView::FindAxis ( MvRequest &fullAxisRequest, MvIconList &axisList,
			       double minX,double maxX,double minY, double maxY)
{
	bool foundX = false, foundY = false;
	MvListCursor axisCursor;

	if ( axisList.size()  > 0  )
	{
		for (axisCursor = axisList.begin(); axisCursor != axisList.end(); axisCursor++ )
		{
			MvRequest oneAxis = (*axisCursor).Request();
			CheckAxisRequest(oneAxis,minX,maxX,minY,maxY,foundX,foundY);
			fullAxisRequest = fullAxisRequest + oneAxis;
		}
	}

	if ( ! foundX )
	{
		MvRequest xAxis = viewRequest_.getSubrequest( hAxisName_.c_str() );
		CheckAxisRequest(xAxis,minX,maxX,minY,maxY,foundX,foundY);

		fullAxisRequest = fullAxisRequest + xAxis;
	}

	if ( ! foundY )
	{
		MvRequest yAxis = viewRequest_.getSubrequest( vAxisName_.c_str() );
		CheckAxisRequest(yAxis,minX,maxX,minY,maxY,foundX, foundY);

		fullAxisRequest =  fullAxisRequest + yAxis;
	}
}

// Erase any data units that have been deleted
void CartesianView::UpdateAxisInfo()
{
	AxisInfoMap::iterator ii = axisInfoMap_.begin(), currentii;
	MvIconDataBase&   dataBase = Owner().IconDataBase();

	MvIcon currentDataUnit;
	while (  ii != axisInfoMap_.end() )
	{
		currentii = ii;
		++ii;

		if (  !dataBase.RetrieveDataUnit ((*currentii).first, currentDataUnit) )
			axisInfoMap_.erase(currentii);
	}
}
#endif

// This will not work correctly if there are more than one pgraph of different
// types attached to a curve. The data unit can only have one pgraph field, which
// is set according to the first pgraph found. To plot the same data unit with
// different pgraph types, drop it again and attach different pgraphs to the
// different data units.
const char* CartesianView::CheckDataRequest(MvIcon& dataUnit, MvIconList& vdList)
{
    auto ii = vdList.begin();
    const char* graphType = nullptr;
    for (; ii != vdList.end(); ii++) {
        MvIcon visdef = *ii;

        if (visdef.Request().getVerb() != Cached("PGRAPH"))
            continue;

        graphType = visdef.Request()("GRAPH_TYPE");
        MvRequest dataRequest = dataUnit.Request();
        if (graphType)
            dataRequest("GRAPH_TYPE") = graphType;
        else
            dataRequest.unsetParam("GRAPH_TYPE");

        dataUnit.SaveRequest(dataRequest);
        break;
    }

    return graphType ? graphType : "CURVE";
}

bool CartesianView::UpdateViewWithReq(MvRequest& viewRequest)
{
    // Changing to another View is not allowed
    if (strcmp(viewRequest_.getVerb(), viewRequest.getVerb()) != 0) {
        // Changing View is disabled at the moment
        MvLog().popup().err() << "Changing View (" << viewRequest_.getVerb() << " to "
                              << viewRequest.getVerb() << ") is currently disabled";
        return false;
    }

    // Update view
    Owner().UpdateView(viewRequest);

    return true;
}

#if 0
void
CartesianView::MergeAxisInfo(MvIcon &currentdu,MvRequest &axis, bool addAxis)
{
	MvRequest currentReq = currentdu.Request(); 
	while ( axis )
	{
		MvRequest oneAxis = axis.justOneRequest();
		bool isVertical = ( axis( "AXIS_ORIENTATION" ) == Cached ("VERTICAL" ) );
		const char *axisType = oneAxis("AXIS_TYPE");
		if ( axisType )
		{
			if ( isVertical )
				currentReq("_YAXIS_TYPE") = axisType;
			else
				currentReq("_XAXIS_TYPE") = axisType;
		}

		axis.advance();
	}

	axis.rewind();

	if ( addAxis )
		currentReq = currentReq + axis;

	currentdu.SaveRequest(currentReq);
}

void
CartesianView::ReplaceArea ( const Location& coordinates, int izoom)
{
     // Handle the original request. If the view was created due to a data visualisation
     // request then send back the original request. The computation of the x/y min/max
     // values is provided by Magics. Otherwise, input parameter 'coordinates' should 
     // contain the original x/y min/max values, defined initially in the View request.
     if ( izoom == 0 )
     {
          const char* origin = viewRequest_("_ORIGIN");
          if ( origin && strcmp(origin,"DataBuilder") == 0 )
          {
               viewRequest_("X_AUTOMATIC") = "ON";
               viewRequest_("Y_AUTOMATIC") = "ON";
               return;
          }
     }

     viewRequest_ ( "Y_MIN" ) = coordinates.Bottom();
     viewRequest_ ( "Y_MAX" ) = coordinates.Top();
     viewRequest_ ( "X_MIN" ) = coordinates.Left();
     viewRequest_ ( "X_MAX" ) = coordinates.Right();

     viewRequest_ ( "X_AUTOMATIC" ) = "OFF";
     viewRequest_ ( "Y_AUTOMATIC" ) = "OFF";
}
#endif
