/***************************** 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 "Presentable.h"

#include <sstream>

#include <Assertions.hpp>
#include "MvDecoder.h"
#include "DrawingPriority.h"
#include "GraphicsEngine.h"
#include "MvLayer.h"
#include "ObjectList.h"
#include "PmContext.h"
#include "Root.h"
#include "Task.h"

int Presentable::treeNodeCounter_ = 0;

// -- METHOD :  Default Constructor
//
// -- INPUT  :  (none)
//
// -- OUTPUT :  (none)
Presentable::Presentable() :
    myRequest_(),
    presentableId_(treeNodeCounter_++),
    myParent_(nullptr),
    myLocation_(0., 0., 1., 1.),
    hasMatch_(false),
    matchingInfo_(MvRequest("EMPTY")),
    isVisible_(false),
    hasNewpage_(false),
    pendingDrawings_(0),
    needsRedrawing_(true),
    removeData_(true),
    hasDrawTask_(false),
    droppedHere_(false),
    refresh_(false)
{
    // Empty
}

// -- METHOD :  Constructor
//
// -- PURPOSE:  Build a new presentable from a request
//
// -- INPUT  :  request
//
// -- OUTPUT :

Presentable::Presentable(const MvRequest& inRequest) :
    myRequest_(inRequest),
    presentableId_(treeNodeCounter_++),
    myParent_(nullptr),
    myLocation_(inRequest),
    hasMatch_(false),
    matchingInfo_(MvRequest("EMPTY")),
    isVisible_(false),
    hasNewpage_(false),
    pendingDrawings_(0),
    needsRedrawing_(true),
    //	inAnimation_     ( false ),
    removeData_(true),
    hasDrawTask_(false),
    droppedHere_(false),
    refresh_(false)
{
    // Empty
}

// -- METHOD :  Copy constructor

Presentable::Presentable(const Presentable& old) :
    myRequest_(old.myRequest_),
    presentableId_(treeNodeCounter_++),
    myParent_(nullptr),
    myLocation_(old.myLocation_),
    hasMatch_(old.hasMatch_),
    matchingInfo_(old.matchingInfo_),
    isVisible_(old.isVisible_),
    hasNewpage_(false),
    pendingDrawings_(0),
    needsRedrawing_(true),
    //	inAnimation_      ( false              ),
    removeData_(true),
    hasDrawTask_(false),
    droppedHere_(false),
    refresh_(old.refresh_)
{
    // Ignore the observers for now, should not be copied,
    // as this copy constructor is at the moment
    // for non-interactive devices. Consequently, device
    // data is not copied.
    // Childlist will be rebuilt in DuplicateChildren function.
}

void Presentable::SetRequest(const MvRequest& in)
{
    myRequest_ = in;
    myLocation_ = in;
}

bool Presentable::HasDrawTask()
{
    if (myParent_)
        return hasDrawTask_ | myParent_->HasDrawTask();
    else
        return hasDrawTask_;
}

#if 0
void Presentable::DuplicateChildren(const Presentable& old)
{
  MvIconDataBase& dataBase = IconDataBase();
  MvIconDataBase& oldDB = old.IconDataBase();
  // Now go through the childlist and copy construct
  // all the children
  MvChildConstIterator ii = old.childList_.begin();
  MvIconList vdList,duList,ptextList;

  MvListCursor jj;
  for ( ; ii != old.childList_.end(); ii++ )
    {

      Presentable *oldPresentable = *ii;
      Presentable *newPresentable = oldPresentable->Clone();
      Insert(newPresentable);

      newPresentable->DuplicateChildren(*oldPresentable);

      // Now get everything attached to old and attach
      // it to new also.
      // Empty lists, functions below just add data.
      vdList.erase(vdList.begin(),vdList.end());
      duList.erase(duList.begin(),duList.end());
      ptextList.erase(ptextList.begin(),ptextList.end());

      // VisDefs
      oldDB.VisDefListByPresentableId(oldPresentable->Id(),
				 vdList);
      for ( jj = vdList.begin(); jj != vdList.end(); jj++ )
	dataBase.PresentableVisDefRelation(newPresentable->Id(),
					   (*jj).Id() );

      // Data units
      oldDB.DataUnitListByPresentableId(oldPresentable->Id(),
					 duList);
      for ( jj = duList.begin(); jj != duList.end(); jj++ )
	{
	  MvIcon visDef;
	  dataBase.PresentableDataUnitRelation(newPresentable->Id(),
					       (*jj).Id() );

	  // Then attach any visdefs for this data unit.
	   oldDB.DataUnitVisDefRelationRewind();
	   while ( oldDB.NextVisDefByDataUnitId((*jj).Id(),visDef) )
	     dataBase.DataUnitVisDefRelation((*jj).Id(),visDef.Id() );
	}


      // Ptexts 
      oldDB.PTextListByPresentableId(oldPresentable->Id(),
				     ptextList);
      for ( jj = ptextList.begin(); jj != ptextList.end();
	    jj++ )
	dataBase.PresentablePTextRelation(newPresentable->Id(),
					  (*jj).Id() );
    }

   // I am the SuperPage, check VisDefs and Texts associated with me
   if ( this->FindSuperPage () == this )
   {
	// Clean auxiliary lists
        vdList.erase(vdList.begin(),vdList.end());
        ptextList.erase(ptextList.begin(),ptextList.end());

	// VisDefs
        oldDB.VisDefListByPresentableId(old.Id(), vdList);
        for ( jj = vdList.begin(); jj != vdList.end(); jj++ )
	    dataBase.PresentableVisDefRelation(Id(), (*jj).Id() );

        // Ptexts 
        oldDB.PTextListByPresentableId(old.Id(), ptextList);
        for ( jj = ptextList.begin(); jj != ptextList.end(); jj++ )
	    dataBase.PresentablePTextRelation(Id(), (*jj).Id() );
   }
}
#endif

// -- METHOD : Destructor
//
// -- PURPOSE: Remove the non-automatic members of the presentable
//
// -- INPUT  :
//
// -- OUTPUT :
Presentable::~Presentable()
{
    // First of all, remove the presentable from the parent list
    // the presentable may be anywhere in the hierarchy
    if (myParent_)
        myParent_->Remove(this);

    // Then, for all its children, delete them
    auto child = childList_.begin();
    while (child != childList_.end()) {
        // Before deletion, make the parent point to NULL
        // This is necessary because otherwise the top
        // statement would generate a recursive call
        (*child)->myParent_ = nullptr;  // Don't get called back

        // Create a temporary pointer to store the child's address
        Presentable* pt = (*child);

        // Remove the child from the list
        childList_.erase(child++);

        // Ok, now the child's parent points to NULL and we can
        // safely call the child's destructor
        delete pt;
    }
}

std::string
Presentable::Name()
{
    std::ostringstream ostr;
    ostr << "Presentable" << Id();
    return ostr.str();
}

// -- METHOD :  MacroName
//
// -- PURPOSE:  Provide the presentable's macro name (in which
//              spaces are changed to underscores)
// -- INPUT  :  (none)
//
// -- OUTPUT :  A valid name for macro
std::string
Presentable::MacroName()
{
    std::string str = (const char*)ObjectInfo::SpaceToUnderscore(this->Name().c_str());
    return str;
}

// --   METHOD  :  Insert
// --   PURPOSE :  Include the presentable in my child list
//
// --   INPUT   :  A pointer to a presentable

void Presentable::Insert(Presentable* p)
{
    require(p != nullptr);
    require(p->myParent_ == nullptr);

    // Test if child is at a lower depth than parent
    childList_.push_back(p);

    p->myParent_ = this;

    // Notify the change
    NotifyObservers();
}

// --   METHOD  : Remove
// --   PURPOSE : Erase the presentable from my child
//                (should be used in connection with delete)

void Presentable::Remove(Presentable* p)
{
    require(p != nullptr);
    require(p->myParent_ == this);  // Am I his father ??

    // Test if child is at a lower depth than parent
    // Remove the child from the tree
    auto child = std::find(childList_.begin(), childList_.end(), p);

    ensure((*child) == p);
    childList_.erase(child);

    //  The child no longer has a father
    //  (a.k.a. "abandon the child")
    p->myParent_ = nullptr;

    // Notify the change
    NotifyObservers();
}

struct DropFunctor
{
    PmContext& context_;
    DropFunctor(PmContext& context) :
        context_(context) {}

    void operator()(Presentable* p)
    {
        context_.RewindToAfterDrop();
        p->Drop(context_);
    }
};

void Presentable::Drop(PmContext& context)
{
    std::for_each(childList_.begin(), childList_.end(), DropFunctor(context));
}

void Presentable::DropSimulate(PmContext& context)
{
    if (!context.InRequest())
        return;

    for (auto& ii : childList_)
        ii->Drop(context);
}

#if 0

// -- METHOD :  
//
// -- PURPOSE:   
//
// -- INPUT  :
//
// -- OUTPUT : 
void
Presentable::SetVisibility ( bool visibility )
{
	isVisible_ = visibility;
}

// -- Methods used for Matching 


// --   METHOD  : DataInserted
// --   PURPOSE : Indicates that the presentable (or one of its
//                children) is on a tree branch with dataObjects
//
void 
Presentable::DataInserted()
{

}

// --   METHOD  : DataRemoved
// --   PURPOSE : Indicates that the presentable contains no dataObjects

void 
Presentable::DataRemoved()
{

}
#endif

// --   METHOD :   HasData
// --   PURPOSE:   Informs if the presentable has a dataObject
//
bool Presentable::HasData()
{
    // Retrieve the Icon Data Base
    MvIconDataBase& iconDataBase = this->IconDataBase();

    return iconDataBase.PresentableHasData(presentableId_);
}

// --   METHOD :   ChildHasData
// --   PURPOSE:   Informs if the presentable has a child with a dataObject
//
bool Presentable::ChildHasData()
{
    // Retrieve the Icon Data Base
    MvIconDataBase& iconDataBase = this->IconDataBase();

    // Search all the children
    // OBS: It would be better to use MvChildConstIterator. However
    // HP compiler gives an error.
    MvChildIterator child;
    for (child = childList_.begin(); child != childList_.end(); ++child) {
        if (iconDataBase.PresentableHasData((*child)->Id()))
            return true;
    }

    return false;
}

// --   METHOD  : InitMatching
// --   PURPOSE : Resets all presentables' children to be unmatched
//
struct InitMatchingFunctor
{
    void operator()(Presentable* p) { p->NotMatched(); }
};

void Presentable::InitMatching()
{
    std::for_each(childList_.begin(), childList_.end(), InitMatchingFunctor());
}

// --   METHOD  : DrawChildren
// --   PURPOSE : Tell all my children to draw themselves
struct DrawFunctor
{
    void operator()(Presentable* p) { p->Draw(); }
};

void Presentable::DrawChildren()
{
    std::for_each(childList_.begin(), childList_.end(), DrawFunctor());
}


// --   METHOD  : RemoveAllDataChildren
// --   PURPOSE : Tell all my children to remove data
struct RemoveAllDataFunctor
{
    void operator()(Presentable* p) { p->RemoveAllData(); }
};

void Presentable::RemoveAllDataChildren()
{
    std::for_each(childList_.begin(), childList_.end(), RemoveAllDataFunctor());
}

void Presentable::RemoveMyData()
{
    MvIconDataBase& iconDataBase = this->IconDataBase();

    // Remove Data Units
    MvIconList duList;
    // if ( iconDataBase.DataUnitListByPresentableId ( this->Id(), duList ) )
    if (iconDataBase.RetrieveIcon(PRES_DATAUNIT_REL, this->Id(), duList)) {
        MvListCursor duCursor;
        for (duCursor = duList.begin(); duCursor != duList.end(); ++duCursor) {
            MvIcon& theDataUnit = *(duCursor);
            this->RemoveIcon(theDataUnit);
            iconDataBase.RemoveDataUnit(theDataUnit);
        }
    }

    // Remove PTexts
    MvIconList ptList;
    // if ( iconDataBase.TextListByPresentableId ( this->Id(), ptList ) )
    if (iconDataBase.RetrieveIcon(PRES_TEXT_REL, this->Id(), ptList)) {
        MvListCursor ptCursor;
        for (ptCursor = ptList.begin(); ptCursor != ptList.end(); ++ptCursor) {
            MvIcon& thePText = *(ptCursor);
            this->RemoveIcon(thePText);
        }
    }
}

#if 0
// --   METHOD  : RemoveAllVisDefChildren
// --   PURPOSE : Tell all my children to remove visdefs
struct RemoveAllVisDefFunctor {
	void operator()(Presentable* p) { p->RemoveAllVisDef(); }
};

void
Presentable::RemoveAllVisDefChildren()
{
    std::for_each ( childList_.begin() ,childList_.end(), RemoveAllVisDefFunctor() );
}

void
Presentable::RemoveAllVisDef()
{
	RemoveAllVisDefChildren();
	RemoveMyVisDef();
}

void
Presentable::RemoveMyVisDef( const MvRequest* cl)
{
	MvIconDataBase&   iconDataBase = this->IconDataBase();

	// Remove VisDefs related to DataUnits
	MvIconList vdList,duList;
	if ( iconDataBase.DataUnitListByPresentableId ( this->Id(), duList ) )
	{
		iconDataBase.RetrieveVisDefList ( duList, vdList );
		RemoveMyVisDef (vdList,cl);
	}

	// Remove VisDefs from Presentable
	vdList.clear();
	if ( iconDataBase.VisDefListByPresentableId ( this->Id(), vdList ) )
		RemoveMyVisDef (vdList,cl);
}

void
Presentable::RemoveMyVisDef (MvIconList& iconList, const MvRequest* req)
{
	MvListCursor  lCursor;
	for ( lCursor = iconList.begin(); lCursor != iconList.end(); ++lCursor)
	{
		MvIcon& theVisDef = *( lCursor );

		// Remove visdef if they are of the same class
		// Exception: PCONT(NORMAL) and PCONT(ISOTACHS)
		// are assumed to belong to different classes
		if ( req )
		{
			if (!ObjectInfo::CheckVisDefClass(*req,theVisDef.Request()) )
				continue;
		}

		this->RemoveIcon ( theVisDef );
	}
}

// --   METHOD  : RemoveAllPTextChildren
// --   PURPOSE : Tell all my children to remove ptexts
struct RemoveAllPTextFunctor {
	void operator()(Presentable* p) { p->RemoveAllPText(); }
};

void
Presentable::RemoveAllPTextChildren()
{
    std::for_each ( childList_.begin() ,childList_.end(), RemoveAllPTextFunctor() );
}

void
Presentable::RemoveAllPText()
{
	RemoveAllPTextChildren();
	RemoveMyPText();
}

void
Presentable::RemoveMyPText()
{
	MvIconDataBase&   iconDataBase = this->IconDataBase();

	// Remove PTexts from Presentable
	MvIconList ptList;
	if ( iconDataBase.PTextListByPresentableId ( this->Id(), ptList ) )
	{
		MvListCursor  lCursor;
		for ( lCursor = ptList.begin(); lCursor != ptList.end(); ++lCursor)
		{
			MvIcon& thePText = *( lCursor );
			this->RemoveIcon ( thePText );
		}
	}
}

// --   METHOD  : SaveChildren
// --   PURPOSE : Tell all my children to save themselves 
struct SaveFunctor {
	void operator()(Presentable* p) { p->Save(); }
};

void
Presentable::SaveChildren()
{
    std::for_each ( childList_.begin(), childList_.end(), SaveFunctor () );
}
#endif

// --   METHOD  : FindBranch
// --   PURPOSE : Find a branch of the tree which has a node Id
// --   INPUT   : Presentable Id ( unique )
Presentable*
Presentable::FindBranch(const int treeNodeId) const
{
    require(treeNodeId > 0);

    // Check for the current node
    if (treeNodeId == presentableId_)
        return (Presentable*)this;

    Presentable* node = nullptr;

    // Look at the descendents
    MvChildConstIterator child;
    for (child = childList_.begin(); child != childList_.end(); ++child) {
        if ((node = (*child)->FindBranch(treeNodeId)) != nullptr)
            return (Presentable*)node;
    }

    return nullptr;
}

// --   METHOD  : FindBranchesByRequest
// --   PURPOSE : Find branches of the tree which have an specific request
// --   INPUT   : String requestName, std::vector<Presentable*>& presVec
void Presentable::FindBranchesByRequest(std::string requestName, std::vector<Presentable*>& presVec)
{
    // Check for the current node
    const char* name = myRequest_.getVerb();
    if (name && strcmp(name, requestName.c_str()) == 0)
        presVec.push_back((Presentable*)this);

    // Look at the descendents
    MvChildIterator child;
    for (child = childList_.begin(); child != childList_.end(); ++child) {
        (*child)->FindBranchesByRequest(requestName, presVec);
    }
}

Presentable*
Presentable::FindBranchByIconId(int iconId, int& dataId) const
{
    // Retrieve the Icon Data Base
    MvIconDataBase& iconDataBase = this->IconDataBase();

    // Find the presentable Id related to the icon Id
    int presId = iconDataBase.FindPresentableId(iconId, dataId);
    if (presId < 0)
        return nullptr;

    // Find the presentable related to the given node id
    Presentable* pres = this->FindBranch(presId);

    return pres;
}

int Presentable::FindBranchIdByIconId(int iconId, int& dataId)
{
    Presentable* pres = this->FindBranchByIconId(iconId, dataId);
    return (pres ? pres->Id() : -1);
}


#if 0
// --   METHOD  : FindBranch
// --   PURPOSE : Find a branch of the tree which has an specific View
// --   INPUT   : String viewName
Presentable*
Presentable::FindBranch (std::string viewName)
{
	// Check for the current node
        if ( FindView(viewName) )
		return (Presentable*)this;

	// Look at the descendents
	Presentable* node = 0;
	MvChildConstIterator child;
	for (child = childList_.begin(); child != childList_.end(); ++child)
	{
		if ( ( node = (*child)->FindBranch (viewName)) != 0 )
			return (Presentable*) node;
	}
	return 0;
}
#endif

// --   METHOD  : FindSuperPage
// --   PURPOSE : Find the superpage of the tree which has a node Id
// --   INPUT   : Presentable Id ( unique )
Presentable*
Presentable::FindSuperPage(int /*index*/)
{
    // Go up the tree, until a superpage is found
    // ( all superpages are children of the root )
    if (myParent_->Id() == 0)
        return (Presentable*)this;
    else
        return myParent_->FindSuperPage();
}

void Presentable::PendingDrawingsAdd()
{
    require(myParent_ != nullptr);
    myParent_->PendingDrawingsAdd();
}

void Presentable::PendingDrawingsRemove()
{
    require(myParent_ != nullptr);
    myParent_->PendingDrawingsRemove();
}

#if 0
bool
Presentable::DrawingIsFinished()
{
	require ( myParent_ != 0 );
	return  myParent_->DrawingIsFinished ();
}

Location
Presentable::GetDrawingArea ()
{
	require ( myParent_ != 0 );
	return  myParent_->GetDrawingArea ();
}

void
Presentable::SetDrawingArea ( Location loc )
{
	require ( myParent_ != 0 );
	myParent_->SetDrawingArea ( loc );
}
#endif

void Presentable::GetAllRequests(MvRequest& fullReq, bool checkIconStatus)
{
    Presentable* sp = this->FindSuperPage();
    sp->GetAllRequests(fullReq, checkIconStatus);
}

// -- METHOD  :  DefaultVisDefList
//
// -- PURPOSE : This method will retrieve the default visdef list
//              associated to a Presentable
//
//         If there is no default visdef associated this Presentable
//         this method will go up the tree until the Root treeNode
//         (the top of the tree), which in this case, the user's default
//         visdef list will be retrieved.
//         This method also checks the type of the visdef to be retrieved:
//         NDIM_FLAG = 0 (no check), 1 (1-D data), 2 (2-D data).
bool Presentable::DefaultVisDefList(const char* className, MvIconList& visdefList, int ndim_flag, int type)
{
    require(className != nullptr);

    bool usingDefault = false;
    bool found = false;

    // Retrieve the Icon Data Base
    MvIconDataBase& iconDataBase = this->IconDataBase();

    // Try to find a visdef associated to the presentable
    visdefList.clear();  // initialise list
    if (type == GETBYVISDEF) {
        MvIconList tmpList;
        iconDataBase.RetrieveIcon(PRES_VISDEF_REL, presentableId_, tmpList);
        MvListCursor ii;
        MvIcon currentVisDef;
        for (ii = tmpList.begin(); ii != tmpList.end(); ii++) {
            currentVisDef = *ii;
            if (currentVisDef.Request().getVerb() == className)
                visdefList.push_back(currentVisDef);
        }
        found = visdefList.size() > 0;
    }
    else
        found = iconDataBase.VisDefListByDataUnitAndPresentableId(presentableId_, className, ndim_flag, visdefList);

    // If not found, try to find Visdefs up in the tree
    if (!found)
        usingDefault = myParent_->DefaultVisDefList(className, visdefList, ndim_flag, type);
    else  // If Visdef(s) are found, check if there is at least one valid.
    {     // Be careful, only make this check if type is GETBYDATAUNIT
        if (type != GETBYVISDEF && !ObjectList::CheckValidVisDefList(className, visdefList))
            usingDefault = myParent_->DefaultVisDefList(className, visdefList, ndim_flag, type);
    }

    ensure(visdefList.size() > 0);

    // FAMI022012 Remove this code temporarily. In the new visualisation scheme
    // the DataObject::DrawDataVisDef() function is in charge to insert Visdefs
    // into the DataBase
#if 0
	// It is a Visdef default and I am the SuperPage
	// Save Visdefs default in the DataBase
	if ( usingDefault && this->FindSuperPage () == this )
//	if ( usingDefault && dynamic_cast <SuperPage> ( *this ) )
	{
		MvListCursor  vdCursor;
		for ( vdCursor = visdefList.begin(); vdCursor != visdefList.end(); ++vdCursor)
		{
			MvIcon& visDef = *( vdCursor );

			// Do not add PAXIS default to the database. This
			// is to avoid PAXIS be shown in the Contents
			// (Display Level). We need to rewrite the Drop 
			// behavior of PAXIS. It is not working properly at
			// moment, because the definition of PAXIS inside
			// the Vertical Profile View is causing some problems. 
//			if ( !ObjectList::IsAxis(visDef.Request().getVerb()) )
				iconDataBase.InsertVisDef ( visDef, Id() );
		}
	}
#endif

    return usingDefault;
}

bool Presentable::RetrieveIconsFromLayer(int layerId, MvIconList& iconList)
{
    // Find the unique icon associated to the input layer
    MvIconDataBase& dataBase = this->IconDataBase();
    MvIcon icon;
    if (!dataBase.RetrieveIconFromLayer(layerId, icon))
        return false;

    // If it is a dataunit icon then retrieve the connected visdefs too
    iconList.empty();
    if (ObjectList::IsDataUnit(icon.IconClass())) {
        // First try to find visdefs directly connected to this dataunit
        // (e.g. visdefs & dataunit dropped together).
        if (!dataBase.RetrieveIcon(DATAUNIT_VISDEF_REL, icon.Id(), iconList)) {
            // Visdef connected to the dataunit not found. Go up the tree.
            // Find presentable branch related to the dataunit
            int dummy = 0;
            Presentable* pres = this->FindBranchByIconId(icon.Id(), dummy);
            pres->DefaultVisDefList(icon.IconClass(), iconList);
        }
    }

    iconList.push_front(icon);

    return true;
}

int Presentable::RetrieveNumberIconsFromLayer(int layerId)
{
    MvIconList iconList;
    this->RetrieveIconsFromLayer(layerId, iconList);

    return iconList.size();
}

// -- METHOD  :  RetrieveTextList
//
// -- PURPOSE : This method will retrieve the Text list associated to a presentable.
//              There are two types of Text: Title and Annotation. One Page can have
//              only one Title type but many Annotations.
//              If there is no Title associated to this Presentable, this method will
//              go up the tree until the Root treeNode (the top of the tree) where the
//              user's default Title will be retrieved.
//              In paralel, if there is no Annotation associated to this Presentable,
//              this method will go up the tree until the Root treeNode. If it does not
//              find any Annotation, no defaul icon will be retrieved. There is no
//              requirement for a Page to have an Annotation icon.
//
bool Presentable::RetrieveTextList(MvIconList& textList)
{
    bool usingDefault = RetrieveTextTitle(textList);

    RetrieveTextAnnotation(textList);

    ensure(textList.size() > 0);

    return usingDefault;
}

bool Presentable::RetrieveTextTitle(MvIconList& textList)
{
    bool usingDefault = false;

    // Retrieve Text icons associated to this presentable
    MvIconDataBase& dataBase = this->IconDataBase();
    MvIconList tList;
    int nIcons = dataBase.RetrieveIcon(PRES_TEXT_REL, presentableId_, tList);

    // Find a Title
    if (nIcons) {
        // Check if type is Title
        MvListCursor lCursor;
        for (lCursor = tList.begin(); lCursor != tList.end(); ++lCursor) {
            if (ObjectList::IsVisDefTextTitle((*lCursor).Request())) {
                textList.push_back((*lCursor));
                return false;
            }
        }
    }

    // Title not found. Search up to the tree.
    usingDefault = myParent_->RetrieveTextTitle(textList);

    ensure(textList.size() > 0);
    return usingDefault;
}

void Presentable::RetrieveTextAnnotation(MvIconList& textList)
{
    // Retrieve Text icons associated to this presentable
    MvIconDataBase& dataBase = this->IconDataBase();
    MvIconList tList;
    int nIcons = dataBase.RetrieveIcon(PRES_TEXT_REL, presentableId_, tList);

    // Find Annotation icons
    bool found = false;
    if (nIcons) {
        MvListCursor lCursor;
        for (lCursor = tList.begin(); lCursor != tList.end(); ++lCursor) {
            // Check if type is Annotation
            if (ObjectList::IsVisDefTextAnnotation((*lCursor).Request())) {
                textList.push_back((*lCursor));
                found = true;
            }
        }
    }

    // If no Annotation icons was not found then search up to the tree
    if (!found)
        myParent_->RetrieveTextAnnotation(textList);

    return;
}

// -- METHOD  :  RetrieveLegend
//
// -- PURPOSE : This method will retrieve the Legend associated to a presentable.
//              If there is no Legend associated to the presentable, this method will
//              go up the tree until the Root treeNode (the top of the tree)
//              that will retrieve the user's default legend.
bool Presentable::RetrieveLegend(MvIcon& iconLeg)
{
    bool usingDefault = false;

    // Try to find a legend associated to the presentable
    MvIconDataBase& dataBase = this->IconDataBase();
    if (!dataBase.RetrieveIcon(PRES_LEGEND_REL, presentableId_, iconLeg))
        usingDefault = myParent_->RetrieveLegend(iconLeg);

    return usingDefault;
}

// -- METHOD  :  RetrieveImportList
//
// -- PURPOSE : This method will retrieve the Import list. If there is no
//              Import icon here it will go up to the tree until reaches the
//              Root treeNode (the top of the tree). It returns false if no
//              Import icons are found.
bool Presentable::RetrieveImportList(MvIconList& importList)
{
    bool found = true;

    // Try to find Import icons associated to the presentable
    MvIconDataBase& dataBase = this->IconDataBase();
    if (!dataBase.RetrieveIcon(PRES_IMPORT_REL, presentableId_, importList))
        found = myParent_->RetrieveImportList(importList);

    return found;
}

// -- METHOD : SetDeviceData
//
// -- PURPOSE: Store the device information for later use
//             ( note the use of a "smart pointer")
//
// -- INPUT  : pointer to a device data
//
// -- OUTPUT : (none)
void Presentable::SetDeviceData(DeviceData* devdata)
{
    require(devdata != nullptr);
    deviceData_ = std::unique_ptr<DeviceData>(devdata);
}

DeviceData*
Presentable::ReleaseDeviceData()
{
    require(deviceData_.get() != nullptr);
    return deviceData_.release();
}

// -- METHOD :  GetGraphics Engine
//
// -- PURPOSE:  Retrieve the grapics engine associated to the presentable
//
GraphicsEngine&
Presentable::GetGraphicsEngine() const
{
    require(myParent_ != nullptr);
    return myParent_->GetGraphicsEngine();
}

PlotModView&
Presentable::GetView() const
{
    require(myParent_ != nullptr);
    return myParent_->GetView();
}

// -- METHOD :  GetCanvas
//
// -- PURPOSE:  Retrieve the canvas associated to the presentable
//
Canvas&
Presentable::GetCanvas() const
{
    require(myParent_ != nullptr);
    return myParent_->GetCanvas();
}

#if 0
bool
Presentable::CanScroll()
{
	require ( myParent_ != 0 );
	return myParent_->CanScroll();
}


// -- METHOD :  GetCanvases
//
// -- PURPOSE:  Return list of canvas, get from Page or EditMapPage
CanvasList
Presentable::GetCanvases () 
{
	require ( myParent_ != 0 );
	return myParent_->GetCanvases ();
}
#endif

DrawingPriority&
Presentable::GetDrawPriority()
{
    require(myParent_ != nullptr);
    return myParent_->GetDrawPriority();
}

// -- METHOD :  Visitor
//
// -- PURPOSE:  Implement the visitor pattern by allowing the
//              visitor to visit all my children
// -- INPUT  :  a visitor
//
struct VisitFunctor
{
    Visitor& visitor_;
    VisitFunctor(Visitor& visitor) :
        visitor_(visitor) {}
    void operator()(Presentable* p) { p->Visit(visitor_); }
};

void Presentable::VisitChildren(Visitor& v)
{
    std::for_each(childList_.begin(), childList_.end(), VisitFunctor(v));
}

#if 0
struct ContentsFunctor {
	MvRequest& request_;
	ContentsFunctor ( MvRequest& request) : request_ ( request ) {}
	void operator()(Presentable* p) { p->ChildrenContents ( request_ ); }
};

void
Presentable::ChildrenContents( MvRequest& request )
{
    std::for_each ( childList_.begin(), childList_.end(), ContentsFunctor ( request ) );
}

void
Presentable::IncludeContents ( MvRequest& request )
{
	request = request + myRequest_;
}

MvRequest
Presentable::ContentsRequest ()
{
	MvRequest dropRequest ("DROP");

	dropRequest ("DROP_ID") = presentableId_; // not really necessary

	dropRequest ("_INTERNAL") = "YES"; // information for later use

	// Include here all the information on tree contents

	// Include my contents
	this->IncludeContents ( dropRequest );

	// Include the contents of my children

	this->ChildrenContents ( dropRequest );
 
return dropRequest;
}
#endif

PaperSize
Presentable::GetChildSize(Presentable& child)
{
    PaperSize size = this->GetMySize();

    Rectangle rect = child.GetLocation();

    double height = size.GetHeight() * (rect.bottom - rect.top);
    double width = size.GetWidth() * (rect.right - rect.left);

    return {width, height};
}

#if 0
void
Presentable::Print()
{
	myParent_->Print();
}

void
Presentable::PrintPreview()
{
	myParent_->PrintPreview();
}

void
Presentable::Undo()
{
	MvChildIterator child;

	for ( child = childList_.begin(); child != childList_.end(); ++child)
	{
		(*child)->Undo();		
	}
}

void
Presentable::Redo()
{
	MvChildIterator child;

	for ( child = childList_.begin(); child != childList_.end(); ++child)
	{
		(*child)->Redo();		
	}
}

void
Presentable::Reset()
{
	MvChildIterator child;

	for ( child = childList_.begin(); child != childList_.end(); ++child)
	{
		(*child)->Reset();		
	}
}
#endif

void Presentable::DescribeDrops(ObjectInfo& myDescription, MacroVisDefMap& vdMap)
{
    // Retrieve the Icon Data Base
    MvIconDataBase& dataBase = this->IconDataBase();

    // Get an indexed name for the page. Need to be called even if
    // there are no drops, beacuse it updates the index.
    std::string macroIndexName = this->MacroPlotIndexName();

    // 1) Describe Texts
    //    Find any texts associated with the presentable
    MvIconList iconList;
    dataBase.RetrieveIcon(PRES_TEXT_REL, Id(), iconList);

    // Loop over text icons
    bool first = true;
    std::string ptextDrops;
    MvListCursor lCursor;
    for (lCursor = iconList.begin(); lCursor != iconList.end(); ++lCursor) {
        MvIcon icon = *lCursor;
        MvRequest iconRequest = icon.Request();

        // Skip icon according to its status
        if ((const char*)iconRequest("_SKIP_MACRO") ||
            (const char*)iconRequest("_DEFAULT") ||
            !dataBase.IconVisibility(icon.Id()))
            continue;

        // Add comment
        if (first) {
            myDescription.PutNewLine("");
            myDescription.PutNewLine("# Title and annotations descriptions");
            first = false;
        }

        // Save icon name and its stacking order info
        std::string sdesc = (const char*)myDescription.Convert(iconRequest);
        ptextDrops = ptextDrops + ", " + sdesc;
    }

    // 2) Describe Legends
    //    Find legend associated with the presentable
    iconList.clear();
    dataBase.RetrieveIcon(PRES_LEGEND_REL, Id(), iconList);

    // Loop over legend icons
    first = true;
    std::string mlegDrops;
    for (lCursor = iconList.begin(); lCursor != iconList.end(); ++lCursor) {
        MvIcon icon = *lCursor;
        MvRequest iconRequest = icon.Request();

        // Skip icon according to its status
        if ((const char*)iconRequest("_SKIP_MACRO") ||
            (const char*)iconRequest("_DEFAULT") ||
            !dataBase.IconVisibility(icon.Id()))
            continue;

        // Add comment
        if (first) {
            myDescription.PutNewLine("");
            myDescription.PutNewLine("# Legend description");
            first = false;
        }

        // Save icon name and its stacking order info
        std::string sdesc = (const char*)myDescription.Convert(iconRequest);
        mlegDrops = mlegDrops + ", " + sdesc;
    }

    // 3) Describe isolated Visdefs (not connected to the data)
    //    Find the list of Visdefs associated to the presentable
    iconList.clear();
    dataBase.RetrieveIcon(PRES_VISDEF_REL, Id(), iconList);

    // Loop over visdefs
    std::string listDrops;
    MvIcon dummy;
    DescribeVisDefList(myDescription, iconList, dummy, listDrops, true, vdMap);

    // 4) Describe dataunits and associated visdefs
    //    Find the list of data units associated to the presentable
    iconList.clear();
    dataBase.RetrieveIcon(PRES_DATAUNIT_REL, Id(), iconList);

    // Loop over dataunits
    auto du = iconList.begin();
    while (du != iconList.end()) {
        // If dataunit is not visible then process the next one
        MvIcon currentDataUnit = *du;
        int visible = dataBase.IconVisibility(currentDataUnit.Id());
        if (visible != 1) {
            ++du;
            continue;
        }

        // Convert dataunit to macro code
        if (!(int)currentDataUnit.Request()("_SKIP_MACRO") && visible == 1) {
            std::string dataUnitName = (const char*)myDescription.ConvertDataUnitToMacro(currentDataUnit);
            if (dataUnitName.empty()) {
                ++du;
                continue;
            }

            // Save icon name and its stacking order info
            listDrops = listDrops + std::string(", ") + dataUnitName;

            int use_mode = currentDataUnit.Request()("_USE_MODE");
            if (use_mode) {
                ++du;
                continue;
            }
        }

        // Find all visdefs associated to the data unit even if it
        // is tagged as _SKIP_MACRO. This is to cope with requests
        // built by some applications, e.g., XSection:
        // (netcdf_xy_matrix, mcont, netcdf_xy_points, mgraph).
        // In this case, althought the netcdf_xy_points request has
        // a tag _SKIP_MACRO, the mgraph request must be processed.
        MvIconList visdefList;
        dataBase.RetrieveVisDefList((*du), visdefList);
        DescribeVisDefList(myDescription, visdefList, *du, listDrops, true, vdMap);
        ++du;
    }

    // 5) Handle Drawing Priority (is it needed?)
    if (this->FindSuperPage() != this)  // Not a SuperPage
    {
        // Describe priority
        DrawingPriority& pageDrawPriority = this->GetDrawPriority();
        std::string priorName = pageDrawPriority.DescribeYourself(myDescription, MacroName());

        if (!priorName.empty())
            listDrops = ptextDrops + mlegDrops + listDrops + "," + priorName;
    }

    // 6) Finalise macro code
    //    Skip superpage, but should produce the plot command for a
    //    map without data
    if (this->FindSuperPage() != this) {
        // Set build layout function
        myDescription.PutNewLine("");
        myDescription.PutNewLine("# Call function to build layout (defined at end of macro)");
        myDescription.PutNewLine("dw = build_layout()");

        // Set plot command
        myDescription.SetPlotAction();
        myDescription.PutNewLine("");
        myDescription.PutNewLine("# Plot command");
        std::string str = "plot ( " + macroIndexName + ptextDrops + mlegDrops + listDrops + " )";
        myDescription.PutNewLine(str.c_str());
    }
}

void Presentable::DescribeDrops(ObjectInfo& myDescription, MvRequest& iconsReq)
{
    // Retrieve the Icon Data Base
    //   MvIconDataBase& dataBase = this->IconDataBase();

    // Get an indexed name for the page. Need to be called even if
    // there are no drops, beacuse it updates the index.
    std::string macroIndexName = this->MacroPlotIndexName();

    // Main loop: describe drops
    myDescription.PutNewLine("");
    std::string listDrops;
    while (iconsReq) {
        // Skip icon according to its status
        if ((const char*)iconsReq("_SKIP_MACRO") ||
            (const char*)iconsReq("_DEFAULT") ||
            ObjectList::IsView(iconsReq.getVerb()))  // View was already described elsewhere
        {
            iconsReq.advance();
            continue;
        }

        // Convert icon to Macro code and save its name.
        // Certain type of icons needs a customised function.
        MvRequest req = iconsReq.justOneRequest();
        std::string sdesc;
        std::string iclass = (const char*)ObjectInfo::IconClass(req);
        if (ObjectList::IsDataUnit(iclass.c_str())) {
            MvIcon ireq(req, true);
            sdesc = (const char*)myDescription.ConvertDataUnitToMacro(ireq);
        }
        else
            sdesc = (const char*)myDescription.Convert(req);

        if (!sdesc.empty())
            listDrops = listDrops + ", " + sdesc;

        iconsReq.advance();
    }

    // Finalise macro code.
    // Skip superpage, but should produce the plot command for a
    // map without data
    if (this->FindSuperPage() != this) {
        // Set build layout function
        myDescription.PutNewLine("");
        myDescription.PutNewLine("# Call function to build layout (defined at end of macro)");
        myDescription.PutNewLine("dw = build_layout()");

        // Set plot command
        myDescription.SetPlotAction();
        myDescription.PutNewLine("");
        myDescription.PutNewLine("# Plot command");
        std::string str = "plot ( " + macroIndexName + listDrops + " )";
        myDescription.PutNewLine(str.c_str());
    }
}

bool Presentable::DescribeVisDefList(ObjectInfo& myDescription,
                                     MvIconList& visdefList, MvIcon& dataUnit,
                                     std::string& listDrops, bool addToDrops,
                                     MacroVisDefMap& vdMap)
{
    // Retrieve the Icon Data Base
    MvIconDataBase& dataBase = this->IconDataBase();

    // Loop over visdefs
    bool found = false;
    auto vd = visdefList.begin();
    while (vd != visdefList.end()) {
        // Skip visdef which is target not to be plotted
        MvIcon currentvd = *vd;
        MvRequest currentvdRequest = currentvd.Request();
        if ((const char*)currentvdRequest("_SKIP_MACRO") || (const char*)currentvdRequest("_DEFAULT")) {
            ++vd;
            continue;
        }

        // Only generate the definition if it hasn't been done before
        auto ii = vdMap.find(currentvd.Id());
        std::string visDefName;
        if (ii == vdMap.end()) {
            myDescription.PutNewLine("");
            visDefName = myDescription.Convert(currentvdRequest);
            vdMap[currentvd.Id()] = visDefName;
        }
        else
            visDefName = (*ii).second.c_str();

        // Save icon name and its stacking order info
        currentvdRequest.rewind();
        if ((dataUnit.Id() == 0 || CheckValidVisDef(dataUnit, currentvdRequest)) &&
            addToDrops &&
            dataBase.IconVisibility(currentvd.Id())) {
            found = true;
            listDrops = listDrops + std::string(", ") + visDefName;
        }
        ++vd;
    }

    return found;
}

int Presentable::PageIndex()
{
    ensure(myParent_ != nullptr);

    return myParent_->PageIndex();
}

int Presentable::PaperPageIndex() const
{
    ensure(myParent_ != nullptr);

    return myParent_->PaperPageIndex();
}

void Presentable::PaperPageIndexV1(int paperPageIndex)
{
    ensure(myParent_ != nullptr);

    myParent_->PaperPageIndexV1(paperPageIndex);
}

#if 0
void
Presentable::EraseDefaultDraw ()
{
   for ( MvChildIterator ii = childList_.begin(); ii != childList_.end(); ii++ )
      (*ii)->EraseDefaultDraw ();
}
#endif

// Process the dropping of common icons
bool Presentable::InsertCommonIcons(const MvRequest& req)
{
    // Retrieve the Icon Data Base
    MvIconDataBase& dataBase = this->IconDataBase();

    // Test if icon came from Contents edition window
    const char* verb = req.getVerb();
    if ((const char*)req("_CONTENTS_ICON_EDITED")) {
        // Icon already exists in the data base, replace it
        int iconId = (int)req("_CONTENTS_ICON_EDITED");
        if (ObjectList::IsVisDefText(verb))
            dataBase.UpdateIcon(DB_TEXT, iconId, req);

        else if (ObjectList::IsVisDefLegend(verb))
            dataBase.UpdateIcon(DB_LEGEND, iconId, req);

        else if (ObjectList::IsVisDefImport(verb))
            dataBase.UpdateIcon(DB_IMPORT, iconId, req);

        else  // Nothing to be inserted
            return false;
    }
    else {
        MvIcon icon(req, true);
        if (ObjectList::IsVisDefText(verb))
            dataBase.InsertIconText(presentableId_, icon);

        else if (ObjectList::IsVisDefLegend(verb))
            dataBase.InsertIcon(PRES_LEGEND_REL, presentableId_, icon, -1, true);

        else if (ObjectList::IsVisDefImport(verb))
            dataBase.InsertIcon(PRES_IMPORT_REL, presentableId_, icon);

        else  // Nothing to be inserted
            return false;
    }

    // Redraw this presentable
    RedrawIfWindow();
    NotifyObservers();

    return true;
}

void Presentable::Draw()
{
    // Draw all my children
    DrawChildren();
}

void Presentable::DrawProlog()
{
    MvChildIterator child;
    for (child = childList_.begin(); child != childList_.end(); ++child)
        (*child)->DrawProlog();
}

void Presentable::DrawTrailerWithReq(MvRequest& fullReq)
{
    // Get children requests
    MvChildIterator child;
    for (child = childList_.begin(); child != childList_.end(); ++child)
        (*child)->DrawTrailerWithReq(fullReq);
}

void Presentable::DrawLayerInfo(const int iconRelId, const char* name)
{
    // Retrieve the Graphics Engine and the data base info
    GraphicsEngine& ge = GetGraphicsEngine();
    MvIconDataBase& dataBase = IconDataBase();

    // Call the Graphics Engine to store the Layer info.
    // If it does not exist, create a default one.
    MvIcon iconLayer;
    if (!dataBase.RetrieveIcon(ICON_LAYER_REL, iconRelId, iconLayer)) {
        MvLayer layer(name);
        MvIcon icon(layer.Request(), true);
        dataBase.InsertIcon(ICON_LAYER_REL, iconRelId, icon);
        ge.Draw(icon.Request());
    }
    else
        ge.Draw(iconLayer.Request());

    return;
}

void Presentable::DrawTask()
{
    // Ask the Root to perform the drawing
    Root::Instance().DrawTask();
}

bool Presentable::CheckDrawTask()
{
    if (this->HasDrawTask())
        return true;

    // Check if any of my children has some task to be performed
    MvChildIterator child;
    for (child = childList_.begin(); child != childList_.end(); ++child)
        if ((*child)->CheckDrawTask())
            return true;

    return false;
}

void Presentable::NotifyObservers()
{
    /*
    std::set<PresentableObserver*> o = observers_;
    for (std::set<PresentableObserver*>::iterator j = o.begin(); j != o.end(); ++j)
        (*j)->Notify(*this);
*/
}

void Presentable::GetDataUnitInfo(MvRequest& out)
{
    // Get icon data base
    MvIconDataBase& iconDataBase = this->IconDataBase();

    // Get data unit list
    MvIconList* iconList = iconDataBase.IconList(DB_DATAUNIT);
    auto lCursor = iconList->begin();
    if (lCursor == iconList->end()) {
        // No data unit available
        return;
    }

    // Process only the first data unit
    // Revise this later if the whole list of data units is required
    // while (lCursor != iconList->end())
    {
        // Build a new data decoder, which will provide information
        // about the data
        std::unique_ptr<Decoder> decoder(DecoderFactory::Make((*lCursor).Request()));
        ensure(decoder.get() != nullptr);

        // Read data headers (one by one)
        while (decoder->ReadNextData()) {
            // Get data unit info
            MvRequest req = decoder->Request();
            out.addValue("DATE", (int)req("DATE"));
            out.addValue("TIME", (int)req("TIME"));
            out.addValue("STEP", (int)req("STEP"));
            out.addValue("EXPVER", (const char*)req("EXPVER"));
        }

        ++lCursor;
    }
}

void Presentable::ProcessCoastlines(MvRequest& coastRequest, bool calledFromMacro, bool foundDU)
{
    // If the input icon came from a Macro and there are
    // any data units before the icon in the plot command:
    //     -we draw the coastlines icon into the foreground
    // In ANY OTHER CASES:
    //  1) split it by creating the FOREGROUND and BACKGROUND icons,
    //     MAP_LAYER_MODE can modify it!!!
    //  2) replace the ones defined in the Page level

    bool exclusive = false;  // true: dropped icon; false: icon came from Macro
    MvRequest backReq, foreReq;
    MvIconDataBase& dataBase = IconDataBase();

    // Icon from a Macro
    if (calledFromMacro) {
        std::string name;
        exclusive = false;
        bool foreground = foundDU;

        const char* layerMode = (const char*)coastRequest("MAP_LAYER_MODE");
        // The whole coastlines icon is rendered into the foreground or
        // background: we treat it in the same way as the icon drops
        if (layerMode &&
            (strcmp(layerMode, "FOREGROUND") == 0 ||
             strcmp(layerMode, "BACKGROUND") == 0)) {
            exclusive = true;
            this->SplitCoastlines(coastRequest, backReq, foreReq);
            // split mode
        }
        else {
            if (foreground) {
                foreReq = coastRequest;
                foreReq.unsetParam("_APPL");  // remove tag stating that this came from Macro
                if ((const char*)foreReq("_NAME"))
                    name = (const char*)foreReq("_NAME");
                else
                    name = "<Coastlines>";

                // Set tags and update request names
                std::string fname;
                fname = name + "(FG)";
                foreReq("_NAME") = fname.c_str();
                foreReq("_PLOTTING_ORDER") = "FOREGROUND";
            }
            else {
                exclusive = true;
                this->SplitCoastlines(coastRequest, backReq, foreReq);
            }
        }
    }
    else  // icon from a Drop
    {
        // Split icon
        exclusive = true;
        this->SplitCoastlines(coastRequest, backReq, foreReq);
    }

    // Create foreground icon and save it in the dataBase
    if (foreReq && !foreReq.isEmpty()) {
        MvIcon icon(foreReq, true);
        dataBase.InsertIcon(PRES_VISDEF_REL, presentableId_, icon, -1, exclusive);
    }

    // Create background icon and save it in the dataBase
    if (backReq && !backReq.isEmpty()) {
        MvIcon icon(backReq, true);
        dataBase.InsertIcon(PRES_VISDEF_REL, presentableId_, icon, -1, false);
    }

    // Redraw this presentable
    RedrawIfWindow();
}

void Presentable::SplitCoastlines(MvRequest& req, MvRequest& backReq, MvRequest& foreReq)
{
    // Split icon coastlines into background and foreground coastlines
    this->CoastlinesBackground(req, backReq);
    this->CoastlinesForeground(req, foreReq);
}

void Presentable::CoastlinesBackground(MvRequest& req, MvRequest& backReq)
{
    backReq.clean();

    const char* layerMode = (const char*)req("MAP_LAYER_MODE");
    // The whole coastlines icon is rendered into the foreground
    if (layerMode && strcmp(layerMode, "FOREGROUND") == 0) {
        return;

        // The whole coastlines icon is rendered into the background
    }
    else if (layerMode && strcmp(layerMode, "BACKGROUND") == 0) {
        backReq = req;
        // Set background tag and update icon name
        std::string name;
        if ((const char*)backReq("_NAME"))
            name = (const char*)backReq("_NAME");
        name += "(BG)";
        backReq("_NAME") = name.c_str();
        backReq("_PLOTTING_ORDER") = "BACKGROUND";
        return;

        // Split mode
        // Build a coastline background icon derived from a coastline icon
        // By default, Coastline will only be plotted on the foreground, unless
        // there is a sea/land shade to be drawn on the background.
    }
    else {
        bool draw = false;
        const char* onoff = (const char*)req("MAP_COASTLINE_LAND_SHADE");
        if (onoff && strcmp(onoff, "ON") == 0)
            draw = true;

        onoff = (const char*)req("MAP_COASTLINE_SEA_SHADE");
        if (onoff && strcmp(onoff, "ON") == 0)
            draw = true;

        // Set Coastline to be drawn on the background
        if (draw) {
            // By default Boundaries, Cities and Rivers are plotted on the Foreground
            backReq = req;
            backReq("MAP_BOUNDARIES") = "OFF";
            backReq("MAP_CITIES") = "OFF";
            backReq("MAP_RIVERS") = "OFF";

            // Unset other drawings
            backReq("MAP_GRID") = "OFF";
            backReq("MAP_LABEL") = "OFF";

            // Set background tag and update icon name
            std::string name;
            if ((const char*)backReq("_NAME"))
                name = (const char*)backReq("_NAME");
            name += "(BG)";
            backReq("_NAME") = name.c_str();
            backReq("_PLOTTING_ORDER") = "BACKGROUND";
        }
    }
}

void Presentable::CoastlinesForeground(MvRequest& req, MvRequest& foreReq)
{
    foreReq.clean();
    const char* layerMode = (const char*)req("MAP_LAYER_MODE");

    // The whole coastlines icon is rendered into the background
    if (layerMode && strcmp(layerMode, "BACKGROUND") == 0) {
        return;

        // The whole coastlines icon is rendered into the foreground
    }
    else if (layerMode && strcmp(layerMode, "FOREGROUND") == 0) {
        foreReq = req;
        std::string name;
        if (const char* nch = (const char*)foreReq("_NAME")) {
            name = std::string(nch);
        }
        name += "(FG)";
        foreReq("_NAME") = name.c_str();
        foreReq("_PLOTTING_ORDER") = "FOREGROUND";

        // Split mode!!!
        // Build a coastline foreground icon derived from a coastline icon
        // Account for land-sea shading. If they are setted to ON then
        // they were previously drawn on the Background by default.
    }
    else {
        foreReq = req;
        const char* onoff = (const char*)req("MAP_COASTLINE_LAND_SHADE");
        if (onoff && strcmp(onoff, "ON") == 0)
            foreReq("MAP_COASTLINE_LAND_SHADE") = "OFF";

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

        // Set foreground tag and update icon name
        std::string name;
        if ((const char*)foreReq("_NAME"))
            name = (const char*)foreReq("_NAME");
        name += "(FG)";
        foreReq("_NAME") = name.c_str();
        foreReq("_PLOTTING_ORDER") = "FOREGROUND";
    }
}

void Presentable::RemoveIconWithId(const int iconId)
{
    // Get icon structure given an id.
    // This is needed because if icon is a Coastlines then its
    // companion (foreground or background) should be deleted too.
    MvIcon icon;
    MvIconDataBase& dataBase = this->IconDataBase();
    if (!dataBase.FindIcon(iconId, icon))
        return;

    // Remove icon from the database
    if (!dataBase.RemoveOneIcon(iconId))
        return;  // icon not deleted

    // If icon is a coastline delete its companion
    MvRequest iconReq = icon.Request();
    std::string verb = (const char*)iconReq.getVerb();
    if (ObjectList::IsVisDefCoastlines(verb.c_str())) {
        // Get icon name
        std::string name = (const char*)iconReq("_NAME");
        if (name.empty())
            return;

        // Get its companion name, if exists
        std::string namec;
        std::size_t found = name.find("(FG)");
        if (found != std::string::npos) {
            namec = name.substr(0, found);
            namec += "(BG)";
        }
        else {
            found = name.find("(BG)");
            if (found != std::string::npos) {
                namec = name.substr(0, found);
                namec += "(FG)";
            }
        }

        // Remove companion icon from the database, if exists
        if (!namec.empty()) {
            // Find icon and remove it
            namec = mbasename(namec.c_str());
            if (dataBase.FindIcon(namec.c_str(), icon))
                dataBase.RemoveOneIcon(icon.Id());
        }
    }

    // Icon was deleted. Redraw the whole map.
    this->HasDrawTask(true);
    this->DrawTask();
}
