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

// File MvIconDataBase
// Gilberto Camara - ECMWF Jun 97
//
// .NAME:
//  MvIconDataBase
//
// .AUTHOR:
//  Gilberto Camara and Fernando Ii
//
// .SUMMARY:
//  Implements a data base of Data Units and VisDefs
//  and a m:n relation between a DataUnit and a VisDef
//
//
#include "MvIconDataBase.h"

#include <iostream>

#include <Assertions.hpp>
#include <MvPath.hpp>
#include "MvLayer.h"
#include "MvRequestUtil.hpp"
#include "PlotModConst.h"
#include "Root.h"
#include "ObjectList.h"

MvIconDataBase::MvIconDataBase(const MvIconDataBase& old) :
    dataunitList_(old.dataunitList_),
    visdefList_(old.visdefList_),
    textList_(old.textList_),
    legendList_(old.legendList_),
    importList_(old.importList_)
{
}

MvIconDataBase::~MvIconDataBase()
{
    // Remove all data links
    DeleteLink();
}

// If presentable id is > 0, do the insertion into the presentable/
// dataunit relation also.
bool MvIconDataBase::InsertDataUnit(MvIcon& newDataUnit, int presentableId)
{
    bool retVal = InsertIfNew(newDataUnit, dataunitList_, presentableDataUnitRelation_, presentableId);

    RelationInsertIfNew(presentableDataUnitRelation_, presentableId, newDataUnit.Id());

    return retVal;
}

bool MvIconDataBase::InsertVisDef(MvIcon& newVisDef)
{
    return InsertIfNew(newVisDef, visdefList_, presentableVisDefRelation_);
}

bool MvIconDataBase::RetrieveVisDefList(const MvIcon& dataUnit, MvIconList& visdefList)
{
    // Start from the beginning of the dataunit-visdef pairs
    this->DataUnitVisDefRelationRewind();

    MvIcon tmpVisDef;
    bool found = false;
    visdefList.clear();  // initialise list

    // Retrieve the VisDefs associated to the DataUnit
    while (this->NextVisDefByDataUnitId(dataUnit.Id(), tmpVisDef)) {
        visdefList.push_back(tmpVisDef);
        found = true;
    }

    return found;
}

bool MvIconDataBase::NextVisDefByDataUnitId(const int dataunitId, MvIcon& theVisDef)
{
    while (duvdRelationCursor_ != dataUnitVisDefRelation_.end()) {
        // DataUnits have a unique icon id
        // find the one on the list
        if ((*duvdRelationCursor_).first == dataunitId) {
            // Retrieve the visdef Id
            const int visdefId = (*duvdRelationCursor_).second;

            // Increment the relation Cursor (to point to next item)
            ++duvdRelationCursor_;

            // Retrieve the visdef
            // return RetrieveVisDef(visdefId, theVisDef);
            return RetrieveIconFromList(DB_VISDEF, visdefId, theVisDef);
        }
        ++duvdRelationCursor_;
    }

    return false;
}

#if 0
bool
MvIconDataBase::NextDataUnit ( MvIcon& theDataUnit )
{
	if (duListCursor_ != dataunitList_.end())
	{
		theDataUnit = *duListCursor_;

		++duListCursor_;          // to point to next element
		return true;
	}
	return false;
}
#endif

bool MvIconDataBase::RemoveAllVisDefsByDataUnit(MvIcon& theDataUnit, const MvRequest* vd)
{
    bool found = false;

    auto duvdCursor = dataUnitVisDefRelation_.begin();
    MvRelationCursor foundCursor;

    MvIcon currentDataUnit;
    while (duvdCursor != dataUnitVisDefRelation_.end()) {
        // DataUnits have a unique icon id. Find the one on the list.
        // RetrieveDataUnit((*duvdCursor).first,currentDataUnit);
        RetrieveIconFromList(DB_DATAUNIT, (*duvdCursor).first, currentDataUnit);

        if (currentDataUnit.Id() == theDataUnit.Id() ||
            currentDataUnit.ParentId() == theDataUnit.Id()) {
            // Keep a copy of the found iterator
            foundCursor = duvdCursor;

            // Increment, so we can continue our search
            ++duvdCursor;

            MvIcon visDef;
            // RetrieveVisDef ( (*foundCursor).second, visDef );
            RetrieveIconFromList(DB_VISDEF, (*foundCursor).second, visDef);

            // Do not remove if they are not of the same class
            if (!vd || ObjectInfo::CheckVisDefClass(*vd, visDef.Request())) {
                // delete the relation pair
                dataUnitVisDefRelation_.erase(foundCursor);

                // Remove visdef from list
                RemoveIconFromList(DB_VISDEF, (*foundCursor).second);

                // indicate that visdef is related to dataunit
                found = true;
            }
        }
        else
            ++duvdCursor;
    }

    return found;
}

void MvIconDataBase::RemoveDataUnit(MvIcon& theDataUnit)
{
    // Remove the visdef from the visdef-data unit relation
    RemoveAllVisDefsByDataUnit(theDataUnit);

    // Remove data unit from the Presentable-DataUnit Relation
    RemoveAllDataUnitsFromPresRel(theDataUnit);

    // Remove data unit from List
    RemoveIconFromList(DB_DATAUNIT, theDataUnit.Id());
}

void MvIconDataBase::RemoveDataUnit(const int presentableId)
{
    // Retrieve list of DataUnits by presentable ID
    MvIconList dataUnitList;
    RetrieveIcon(PRES_DATAUNIT_REL, presentableId, dataUnitList);

    // Remove DataUnits
    auto duListCursor = dataUnitList.begin();
    while (duListCursor != dataUnitList.end()) {
        RemoveDataUnit(*duListCursor);
        ++duListCursor;
    }
}

void MvIconDataBase::RemoveDataUnit()
{
    // Remove all DataUnits
    auto duListCursor = dataunitList_.begin();
    MvListCursor foundCursor;

    while (duListCursor != dataunitList_.end()) {
        // Keep a copy of the found iterator
        foundCursor = duListCursor;

        // Increment, so we can continue our search
        ++duListCursor;

        // Remove the visdef from the visdef-data unit relation
        RemoveAllVisDefsByDataUnit(*foundCursor);

        // Remove data unit from the Presentable-DataUnit Relation
        RemoveAllDataUnitsFromPresRel(*foundCursor);

        dataunitList_.erase(foundCursor);
    }
}

bool MvIconDataBase::RemoveAllDataUnitsFromPresRel(MvIcon& theDataUnit)
{
    bool found = false;

    auto prduCursor = presentableDataUnitRelation_.begin();
    MvRelationCursor foundCursor;

    MvIcon currentDataUnit;
    while (prduCursor != presentableDataUnitRelation_.end()) {
        // RetrieveDataUnit((*prduCursor).second,currentDataUnit);
        RetrieveIconFromList(DB_DATAUNIT, (*prduCursor).second, currentDataUnit);
        if (currentDataUnit.Id() == theDataUnit.Id() ||
            currentDataUnit.ParentId() == theDataUnit.Id()) {
            // Keep a copy of the found iterator
            foundCursor = prduCursor;

            // Increment, so we can continue our search
            ++prduCursor;

            // Delete the relation pair
            presentableDataUnitRelation_.erase(foundCursor);

            // Indicate that visdef is related to presentable
            found = true;
        }
        else
            ++prduCursor;
    }

    return found;
}

bool MvIconDataBase::RemoveDataUnitFromPresRel(MvIcon& theDataUnit, int presentableId)
{
    bool found = false;

    auto prduCursor = presentableDataUnitRelation_.begin();
    MvRelationCursor foundCursor;

    MvIcon currentDataUnit;
    while (prduCursor != presentableDataUnitRelation_.end()) {
        // RetrieveDataUnit((*prduCursor).second,currentDataUnit);
        RetrieveIconFromList(DB_DATAUNIT, (*prduCursor).second, currentDataUnit);
        if ((currentDataUnit.Id() == theDataUnit.Id() ||
             currentDataUnit.ParentId() == theDataUnit.Id()) &&
            (*prduCursor).first == presentableId) {
            // Keep a copy of the found iterator
            foundCursor = prduCursor;

            // delete the relation pair
            presentableDataUnitRelation_.erase(foundCursor);

            // indicate that visdef is related to presentable
            found = true;
            break;
        }
        else
            ++prduCursor;
    }

    return found;
}

void MvIconDataBase::RemoveVisDef(const int presentableId, const MvRequest* vd)
{
    // Remove VisDefs from DataUnit relation
    MvIconList dataUnitList;
    if (RetrieveIcon(PRES_DATAUNIT_REL, presentableId, dataUnitList))
        RemoveAllVisDefsByDataUnitList(dataUnitList, vd);

    // Remove VisDefs from Presentable relation
    RemoveVisDefFromPresRel(presentableId, vd);
}

void MvIconDataBase::RemoveVisDefFromPresRel(const int presentableId, const MvRequest* vd)
{
    auto prvdCursor = presentableVisDefRelation_.begin();
    MvRelationCursor foundCursor;

    while (prvdCursor != presentableVisDefRelation_.end()) {
        // Presentable have a unique icon id
        // find the one on the list
        if ((*prvdCursor).first == presentableId) {
            foundCursor = prvdCursor;

            // Increment the relation Cursor (to point to next item)
            ++prvdCursor;

            // Check if class is given
            MvIcon theVisDef;
            // if (vd && RetrieveVisDef((*foundCursor).second,theVisDef))
            if (vd && RetrieveIconFromList(DB_VISDEF, (*foundCursor).second, theVisDef)) {
                // Do not remove if they are not of the same class
                if (!ObjectInfo::CheckVisDefClass(*vd, theVisDef.Request()))
                    continue;
            }

            // Delete visdef from VisDefList
            RemoveIconFromList(DB_VISDEF, (*foundCursor).second);

            // Delete the visdef-presentable pair
            presentableVisDefRelation_.erase(foundCursor);
        }
        else
            ++prvdCursor;
    }
}

void MvIconDataBase::RemoveVisDef()
{
    bool found;

    // Remove all VisDefs
    auto vdListCursor = visdefList_.begin();
    MvListCursor foundCursor;
    while (vdListCursor != visdefList_.end()) {
        // Keep a copy of the found iterator
        foundCursor = vdListCursor;

        // Increment, so we can continue our search
        ++vdListCursor;

        // Remove the visdef from the visdef-data unit relation
        found = RemoveIconFromRel(DATAUNIT_VISDEF_REL, (*foundCursor).Id());

        // If the visdef is not related to a data unit, then
        // it must be related to a presentable
        if (found == false)
            RemoveIconFromRel(PRES_VISDEF_REL, (*foundCursor).Id());

        // delete the relation pair
        visdefList_.erase(foundCursor);
    }
}

void MvIconDataBase::RemoveAllVisDefsByDataUnitList(MvIconList& dataUnitList, const MvRequest* vd)
{
    auto duListCursor = dataUnitList.begin();
    MvIcon theDataUnit;

    while (duListCursor != dataUnitList.end()) {
        theDataUnit = *duListCursor;

        RemoveAllVisDefsByDataUnit(theDataUnit, vd);

        ++duListCursor;
    }
}

#if 0
void
MvIconDataBase::PresentableVisDefRelation (const int presentableId, const int visdefId)
{
  RelationInsertIfNew(presentableVisDefRelation_,presentableId,visdefId);
}
#endif

bool MvIconDataBase::VisDefListByDataUnitAndPresentableId(const int presentableId_, const char* dataUnitClass, int ndflag, MvIconList& visdefList)
{
    // Instantiate auxiliary variables
    MvIcon tmpVisDef;
    bool success = false;

    // Rewind the Presentable-VisDef relation
    this->PresentableVisDefRelationRewind();

    // Loop through the database and find all visdefs associated to
    // the presentable id
    while (this->NextVisDefByPresentableId(presentableId_, tmpVisDef)) {
        // Accept all valid visdefs
        if (ObjectList::CheckValidVisDef(dataUnitClass, tmpVisDef.IconClass(), ndflag)) {
            success = true;
            visdefList.push_back(tmpVisDef);
        }
    }

    return success;
}

bool MvIconDataBase::NextVisDefByPresentableId(const int presentableId, MvIcon& theVisDef)
{
    while (prvdRelationCursor_ != presentableVisDefRelation_.end()) {
        // Presentable have a unique icon id
        // find the one on the list
        if ((*prvdRelationCursor_).first == presentableId) {
            // Retrieve the visdef Id
            const int visdefId = (*prvdRelationCursor_).second;

            // Increment the relation Cursor (to point to next item)
            ++prvdRelationCursor_;

            // Retrieve the visdef
            // return RetrieveVisDef(visdefId, theVisDef);
            return RetrieveIconFromList(DB_VISDEF, visdefId, theVisDef);
        }
        ++prvdRelationCursor_;
    }

    return false;
}

bool MvIconDataBase::PresentableHasData(const int presentableId)
{
    // Instantiate auxiliary variables
    MvIcon tmpDataUnit;

    // Rewind the Presentable-DataUnit relation
    this->PresentableDataUnitRelationRewind();

    // Retrieve the first dataunit associated to the Presentable Id
    return this->NextDataUnitByPresentableId(presentableId, tmpDataUnit);
}

bool MvIconDataBase::NextDataUnitByPresentableId(const int presentableId, MvIcon& theDataUnit)
{
    while (prduRelationCursor_ != presentableDataUnitRelation_.end()) {
        // Presentable have a unique icon id
        // find the one on the list
        if ((*prduRelationCursor_).first == presentableId) {
            // Retrieve the dataunit Id
            const int dataunitId = (*prduRelationCursor_).second;

            // Increment the relation Cursor (to point to next item)
            ++prduRelationCursor_;

            // Retrieve the dataunit
            return RetrieveIconFromList(DB_DATAUNIT, dataunitId, theDataUnit);
        }
        ++prduRelationCursor_;
    }

    return false;
}

MvIcon
MvIconDataBase::InsertDataUnit(const MvRequest& dataunitRequest, int presentableId, int index)
{
    // Copy the grib Request into a new DataUnit
    MvIcon newDataUnit(dataunitRequest, true);
    newDataUnit.StackingOrder(index);

    // If the data existed before, we get rid of old drawings to make sure
    // redraw is done properly
    if (!this->InsertDataUnit(newDataUnit, presentableId)) {
        if (presentableId > 0) {
            // Erase the segments to force redrawing
            Presentable* treeNode = Root::Instance().FindBranch(presentableId);
            bool oldStatus = treeNode->RemoveData();
            treeNode->RemoveData(false);
            treeNode->RemoveIcon(newDataUnit);
            treeNode->RemoveData(oldStatus);
        }
    }
    else {
        MvLayer layer(newDataUnit.IconName());
        MvIcon icon(layer.Request(), true);
        this->InsertIcon(ICON_LAYER_REL, newDataUnit.Id(), icon, index);
    }

    return newDataUnit;
}

void MvIconDataBase::InsertVisDef(MvIcon& visDef, const int nodeId, MvIcon& dataUnit)
{
    require(nodeId > 0);
    require(dataUnit.Id() >= 0);

    int id = 0;
    MvRelation* relation = nullptr;  // Pointer to avoid any copying of maps

    // VisDefs that come after a DataUnit are assigned
    // to that DataUnit (or else they
    // are assigned to the presentable)
    // NB Don't check validity of drop. This is responsibility of caller.
    if (dataUnit.Id() > 0) {
        id = dataUnit.Id();
        relation = &dataUnitVisDefRelation_;
    }
    else {
        id = nodeId;
        relation = &presentableVisDefRelation_;
    }

    // Put the visdef in the DataBase and in relation if it's not there
    if (this->InsertIfNew(visDef, visdefList_, *relation, id))
        RelationInsertIfNew(*relation, id, visDef.Id());

    return;
}

MvIcon
MvIconDataBase::InsertVisDef(const MvRequest& visdefRequest, const int nodeId, MvIcon& dataUnit)
{
    require(nodeId > 0);
    require(dataUnit.Id() >= 0);

    // Copy the visdef Request into a new VisDef
    MvIcon visDef(visdefRequest, true);

    // Insert visdef into the database
    this->InsertVisDef(visDef, nodeId, dataUnit);

    return visDef;
}

void MvIconDataBase::InsertVisDef(MvIcon& visDef, const int nodeId)
{
    require(nodeId > 0);

    // Put the visdef in the DataBase and in relation if it's not there
    if (this->InsertIfNew(visDef, visdefList_, presentableVisDefRelation_, nodeId))
        RelationInsertIfNew(presentableVisDefRelation_, nodeId, visDef.Id());

    return;
}

// -----------------------------------------------------------------
// -- Methods for the Layers
// -----------------------------------------------------------------

bool MvIconDataBase::LayerInfo(int layerId, const char* param, int value)
{
    // Find the icon layer given its id
    MvIcon theLayer;
    if (!this->RetrieveIconFromList(DB_LAYER, layerId, theLayer))
        return false;

    // Update icon information
    MvRequest req = theLayer.Request();
    req(param) = value;
    theLayer.SaveRequest(req);

    return true;
}

bool MvIconDataBase::IconInfo(int iconId, const char* param, int value)
{
    // Find the icon layer given an icon id
    MvIcon theLayer;
    if (!this->RetrieveIcon(ICON_LAYER_REL, iconId, theLayer))
        return false;

    // Update icon information
    MvRequest req = theLayer.Request();
    req(param) = value;
    theLayer.SaveRequest(req);

    return true;
}

int MvIconDataBase::LayerInfo(int layerId, const char* param)
{
    // Find the icon layer given its id
    MvIcon theLayer;
    if (!this->RetrieveIconFromList(DB_LAYER, layerId, theLayer))
        return -1;

    // Get icon information
    return (int)theLayer.Request()(param);
}

int MvIconDataBase::IconInfo(int iconId, const char* param)
{
    // Find the icon layer given an icon id
    MvIcon theLayer;
    if (!this->RetrieveIcon(ICON_LAYER_REL, iconId, theLayer))
        return -1;

    // Get icon layer information
    return (int)theLayer.Request()(param);
}

bool MvIconDataBase::RetrieveIconFromLayer(int layerId, MvIcon& icon)
{
    // Find the unique icon associated to the input layer
    auto relCursor = iconLayerRelation_.begin();
    while (relCursor != iconLayerRelation_.end()) {
        // Icon has a unique id; find the one on the list
        if ((*relCursor).second == layerId) {
            // Retrieve the icon Id
            const int iconId = (*relCursor).first;

            // Retrieve the icon from the DataBase List
            return FindIcon(iconId, icon);
        }
        ++relCursor;
    }

    return false;
}

// -----------------------------------------------------------------
// -- General Remove routine
// -----------------------------------------------------------------

void MvIconDataBase::RemoveAllData()
{
    RemoveDataUnit();             // remove all DataUnits
    RemoveVisDef();               // remove all VisDefs
    RemoveIcon(PRES_TEXT_REL);    // remove all Texts
    RemoveIcon(PRES_LEGEND_REL);  // remove all Legends
    RemoveIcon(PRES_IMPORT_REL);  // remove all Import objects
}

void MvIconDataBase::RemovePresentable(const int presentableId)
{
    // Remove all icons by Presentable Id
    RemoveDataUnit(presentableId);
    RemoveVisDef(presentableId);
    RemoveIcon(PRES_TEXT_REL, presentableId);
    RemoveIcon(PRES_LEGEND_REL, presentableId);
    RemoveIcon(PRES_IMPORT_REL, presentableId);
    RemoveIcon(PRES_VIEW_REL, presentableId);
}

void MvIconDataBase::Print()
{
    // Print lists
    std::cout << "MvIconDataBase Contents" << std::endl;
    std::string mlist[DB_COUNT__] = {"DB_DATAUNIT", "DB_VISDEF", "DB_TEXT", "DB_LEGEND", "DB_LAYER", "DB_IMPORT", "DB_VIEW"};
    for (unsigned int i = 0; i < DB_COUNT__; i++) {
        std::cout << mlist[i] << std::endl;
        MvIconList* iconList = this->IconList(i);
        auto lCursor = iconList->begin();
        while (lCursor != iconList->end()) {
            std::cout << " " << (*lCursor).Id();
            ++lCursor;
        }
        std::cout << " " << std::endl;
    }

    // Print relations
    std::cout << " " << std::endl;
    std::string mrel[REL_COUNT__] = {"PRES_DATAUNIT_REL", "PRES_VISDEF_REL", "PRES_TEXT_REL", "PRES_LEGEND_REL", "PRES_IMPORT_REL", "PRES_VIEW_REL", "PRES_LAYER_REL", "DATAUNIT_VISDEF_REL", "ICON_LAYER_REL"};
    for (unsigned int i = 0; i < REL_COUNT__; i++) {
        if (i == PRES_LAYER_REL)  // print this 'special' relation later
            continue;

        std::cout << mrel[i] << std::endl;
        MvRelation* rel = this->IconRelation(i);
        auto rCursor = rel->begin();
        while (rCursor != rel->end()) {
            std::cout << " " << (*rCursor).first;
            std::cout << " " << (*rCursor).second << std::endl;
            ++rCursor;
        }
        std::cout << " " << std::endl;
    }

    // Print Presentable-Layer relation
    std::cout << mrel[PRES_LAYER_REL] << std::endl;
    auto mCursor = presentableLayerRelation_.begin();
    while (mCursor != presentableLayerRelation_.end()) {
        std::cout << " " << (*mCursor).first;
        MvIconList iconList = (*mCursor).second;
        auto listCursor = iconList.begin();
        while (listCursor != iconList.end()) {
            MvIcon tmpIcon = *listCursor;
            std::cout << tmpIcon.Id();
            ++listCursor;
        }
        std::cout << std::endl;
        ++mCursor;
    }

    std::cout << " " << std::endl;
}

bool MvIconDataBase::InsertIfNew(MvIcon& icon, MvIconList& list,
                                 MvRelation& relation, int iconId)
{
    MvRequest newRequest = icon.Request();

    // It is important that the icon Id is added to the request:
    // newRequest("_ID") = xx .
    // This will be used later when the request is passed to Magics

    // If no name, we can't check it's existence. Add it and return true
    const char* name = newRequest("_NAME");
    if (!name) {
        CreateLink(newRequest);
        icon.SaveRequest(newRequest);
        list.push_back(icon);
        return true;
    }

    // Loop through the list to find a match
    bool found = false;
    auto ii = list.begin();
    for (; ii != list.end(); ii++) {
        MvIcon currentIcon = *ii;
        MvRequest currentRequest = currentIcon.Request();
        const char* currentName = currentRequest("_NAME");

        // Found a match on name. As the data units are propagated downwards
        // there may be more with same name. Check that this is the one
        // belonging to correct icon.
        if (currentName && !strcmp(currentName, name)) {
            if (iconId == 0 || (iconId > 0 && ExistsInRelation(relation, iconId, currentIcon.Id()))) {
                CreateLink(newRequest);
                currentIcon.SaveRequest(newRequest);
                icon = currentIcon;
                found = true;
                break;
            }
        }
    }

    if (!found) {
        CreateLink(newRequest);
        icon.SaveRequest(newRequest);
        list.push_back(icon);
        return true;
    }

    return false;
}

void MvIconDataBase::RelationInsertIfNew(MvRelation& relation,
                                         const int firstId, const int secondId)
{
    if (firstId == 0 || secondId == 0)
        return;

    if (!ExistsInRelation(relation, firstId, secondId))
        relation.insert(MvEntryPair(firstId, secondId));
}

bool MvIconDataBase::ExistsInRelation(MvRelation& relation, const int firstId, const int secondId)
{
    // Check if this key and value is already there
    auto lower_ii = relation.lower_bound(firstId);
    auto upper_ii = relation.upper_bound(firstId);

    bool found = false;
    while (lower_ii != relation.end() && lower_ii != upper_ii) {
        if ((*lower_ii).second == secondId) {
            found = true;
            break;
        }
        lower_ii++;
    }

    return found;
}

void MvIconDataBase::CreateLink(MvRequest& req)
{
    // If there is no path, do no create a link
    const char* mpath = (const char*)(req("PATH"));
    if (mpath == nullptr)
        return;

    // Check if the link has to be created
    const char* createLink = (const char*)req("_CREATE_LINK");
    if (createLink && strcmp(createLink, "NO") == 0)
        return;

    std::string spath = (const char*)mpath;
    std::string slink = tempnam(getenv("METVIEW_TMPDIR"), "link");

    if (symlink(spath.c_str(), slink.c_str()) != 0)  //-- create a soft link
    {
        marslog(LOG_WARN, (char*)"MvIconDataBase::CreateLink: unable to create a link");

        return;  //-- nothing to do => bail out!
    }

    //-- make sure that soft link is not a dangling ptr!
    bool flag = true;
    if (!FileCanBeOpened(slink.c_str(), "r")) {
        // Try to use parameter _PATH
        if ((const char*)req("_PATH")) {
            remove(slink.c_str());
            spath = (const char*)req("_PATH");
            symlink(spath.c_str(), slink.c_str());
            if (!FileCanBeOpened(slink.c_str(), "r"))
                flag = false;
        }
        else
            flag = false;
    }
    if (!flag) {
        marslog(LOG_WARN, (char*)"Created soft link is dangling: %s", slink.c_str());

        return;  //-- nothing to do => bail out!
    }

    req("PATH") = slink.c_str();
    linkList_.push_back(slink.c_str());

    return;
}

void MvIconDataBase::DeleteLink()
{
    auto listIter = linkList_.begin();

    while (listIter != linkList_.end()) {
        std::string mpath = (*listIter);
        if (unlink(mpath.c_str()))
            marslog(LOG_WARN, (char*)"MvIconDataBase::DeleteLink : Error : Try to unlink a non existing file : %s", mpath.c_str());

        ++listIter;
    }
}

// -----------------------------------------------------------------
// -- General methods
// -----------------------------------------------------------------

// Retrieve the relation pair variable
MvRelation* MvIconDataBase::IconRelation(const int iconType)
{
    if (iconType == DATAUNIT_VISDEF_REL)
        return &dataUnitVisDefRelation_;
    else if (iconType == PRES_VISDEF_REL)
        return &presentableVisDefRelation_;
    else if (iconType == PRES_DATAUNIT_REL)
        return &presentableDataUnitRelation_;
    else if (iconType == PRES_TEXT_REL)
        return &presentableTextRelation_;
    else if (iconType == PRES_LEGEND_REL)
        return &presentableLegendRelation_;
    else if (iconType == ICON_LAYER_REL)
        return &iconLayerRelation_;
    else if (iconType == PRES_IMPORT_REL)
        return &presentableImportRelation_;
    else if (iconType == PRES_VIEW_REL)
        return &presentableViewRelation_;
    else {
        marslog(LOG_WARN, (char*)"Wrong type value for Icon Relation: %d", iconType);
        return nullptr;
    }
}

// Retrieve the icon object list
MvIconList* MvIconDataBase::IconList(const int iconType)
{
    if (iconType == DB_DATAUNIT)
        return &dataunitList_;
    else if (iconType == DB_VISDEF)
        return &visdefList_;
    else if (iconType == DB_TEXT)
        return &textList_;
    else if (iconType == DB_LEGEND)
        return &legendList_;
    else if (iconType == DB_LAYER)
        return &layerList_;
    else if (iconType == DB_IMPORT)
        return &importList_;
    else if (iconType == DB_VIEW)
        return &viewList_;
    else {
        marslog(LOG_WARN, (char*)"Wrong type value for Icon List: %d", iconType);
        return nullptr;
    }
}

// Retrieve the correspondent icon type
int MvIconDataBase::IconType(const int iconRelType)
{
    if (iconRelType == DATAUNIT_VISDEF_REL)
        return DB_VISDEF;
    else if (iconRelType == PRES_VISDEF_REL)
        return DB_VISDEF;
    else if (iconRelType == PRES_DATAUNIT_REL)
        return DB_DATAUNIT;
    else if (iconRelType == PRES_TEXT_REL)
        return DB_TEXT;
    else if (iconRelType == PRES_LEGEND_REL)
        return DB_LEGEND;
    else if (iconRelType == ICON_LAYER_REL)
        return DB_LAYER;
    else if (iconRelType == PRES_IMPORT_REL)
        return DB_IMPORT;
    else if (iconRelType == PRES_VIEW_REL)
        return DB_VIEW;
    else {
        marslog(LOG_WARN, (char*)"Pair Icon Relation and Icon Type not defined: %d", iconRelType);
        return -1;
    }
}

void MvIconDataBase::InsertIcon(const int iconRelType, const int iconRelId, MvIcon& icon, bool bLayer, int index)
{
    // Insert icon
    this->InsertIcon(iconRelType, iconRelId, icon);

#if 1
    // Create a new Layer entry
    if (bLayer) {
        MvLayer layer;
        layer.StackingOrder(index);
        // layer.IconId(icon.Id());
        MvIcon iconLayer(layer.Request(), true);
        //      this->InsertMapIcon( ICON_LAYER_REL,icon.Id(),iconLayer,index );
        this->InsertLayerIcon(iconRelId, iconLayer, index);
    }
#endif
}

void MvIconDataBase::InsertLayerIcon(const int presId, MvIcon& layer, int index)
{
    // Check if Layer already exists
    // Retrieve list of Layers by Presentable Id
    MvIconList layerList;
    if (this->RetrieveIcon1(presId, layerList)) {
        // if index = 0 then add it in the beginning (background)
        // if index > list.size then add it in the end (foreground)
        // if index < 0 then add it in the end but before foreground
        // otherwise, add it in the position indicated by index
        int size = layerList.size();
        if (index == 0 || size == 0)
            layerList.push_front(layer);
        else if (index > size)
            layerList.push_back(layer);
        else if (index < 0) {
            bool found = false;
            auto it = layerList.end();
            for (int i = size; i != 0; --i) {
                --it;
                if (this->LayerStackingOrder((*it).Id()) != 999) {
                    layerList.insert(++it, layer);
                    found = true;
                    break;
                }
            }
            if (!found)  // add as a first element
                layerList.push_front(layer);
        }
        else {
            auto it = layerList.begin();
            for (int i = 1; i < index; i++)
                ++it;
            layerList.insert(it, layer);
        }
    }
}

void MvIconDataBase::InsertIcon(const int iconRelType, const int iconRelId, MvIcon& icon, int index, bool exclusive)
{
    require(iconRelId > 0);

    // Get the associated iconType from the icon relation type variable
    int iconType = IconType(iconRelType);

    // Retrieve list of icons by relation ID
    MvIconList iconList;
    if (this->RetrieveIcon(iconRelType, iconRelId, iconList)) {
        // There are icons already associated to this relation
        // Check the flag indicating how to insert the new icon.
        // If exclusive then remove all existing icons with the same
        // Class before inserting the new one.
        if (exclusive) {
            // this->RemoveIcon(iconRelType,iconRelId);

            // Scan list of icons checking icons with same Class
            auto listCursor = iconList.begin();
            while (listCursor != iconList.end()) {
                MvIcon tmpIcon = *listCursor;
                const char* iclass1 = tmpIcon.Request()("_CLASS");
                const char* iclass2 = icon.Request()("_CLASS");
                if (iclass1 && iclass2 && strcmp(iclass1, iclass2) == 0) {
                    // Remove icon from the list
                    this->RemoveIcon(iconRelType, iconRelId, tmpIcon.Request()("_ID"));
                }
                ++listCursor;
            }
        }
        else {
            // Scan list of icons to check if icon already exists
            auto listCursor = iconList.begin();
            while (listCursor != iconList.end()) {
                MvIcon tmpIcon = *listCursor;
                if (tmpIcon == icon) {
                    // The icon exists in list. Update it taking into consideration the
                    // drawing order info.
                    MvRequest req;
                    metview::CopyParametersAndStackingOrder(tmpIcon.Request(), icon.Request(), index, req);
                    (*listCursor).SaveRequest(req);

                    return;
                }
                ++listCursor;
            }
        }
    }

    // Put the new icon in the correspondent List
    this->InsertIcon(iconType, icon, index);

    // Put the pair IconRel - Icon in the Relation
    this->InsertIconRelation(iconRelType, iconRelId, icon.Id());

    return;
}

void MvIconDataBase::InsertIcon(const int iconType, const MvIcon& icon, int index)
{
    // if index = 0 then add it in the beginning (background)
    // if index > list.size then add it in the end (foreground)
    // if index < 0 then add it in the end but before foreground
    // otherwise, add it in the position indicated by index
    MvIconList* list = this->IconList(iconType);
    int size = list->size();
    if (index == 0 || size == 0)
        list->push_front(icon);
    else if (index > size)
        list->push_back(icon);
    else if (index < 0) {
        bool found = false;
        auto it = list->end();
        for (int i = size; i != 0; --i) {
            --it;
            if (this->IconStackingOrder((*it).Id()) != 999) {
                list->insert(++it, icon);
                found = true;
                break;
            }
        }
        if (!found)  // add as a first element
            list->push_front(icon);
    }
    else {
        auto it = list->begin();
        for (int i = 1; i < index; i++)
            ++it;
        list->insert(it, icon);
    }
}

void MvIconDataBase::InsertIconText(const int iconRelId, MvIcon& icon, int index)
{
    require(iconRelId > 0);

    // Get the associated iconType from the icon relation type variable
    int iconRelType = PRES_TEXT_REL;
    int iconType = IconType(iconRelType);

    // Flag indicating if the input icon is TITLE or POSITIONAL
    bool btitle = true;
    MvRequest iconRequest = icon.Request();
    if ((const char*)iconRequest("TEXT_MODE") &&
        strcmp((const char*)iconRequest("TEXT_MODE"), "POSITIONAL") == 0)
        btitle = false;

    // Retrieve list of icons by relation ID
    MvIconList iconList;
    if (this->RetrieveIcon(iconRelType, iconRelId, iconList)) {
        // Find the icon on the list that is Text-Title
        auto listCursor = iconList.begin();
        while (listCursor != iconList.end()) {
            MvIcon tmpIcon = *listCursor;
            MvRequest tmpRequest = tmpIcon.Request();
            if ((const char*)tmpRequest("TEXT_MODE") &&
                strcmp((const char*)tmpRequest("TEXT_MODE"), "POSITIONAL") == 0) {
                // Icon is Positional
                if (!btitle && tmpIcon == icon) {
                    // The icon exists in list. Update it taking into consideration the
                    // drawing order info.
                    MvRequest req;
                    metview::CopyParametersAndStackingOrder(tmpIcon.Request(), iconRequest, index, req);
                    (*listCursor).SaveRequest(req);

                    return;
                }
            }
            else  // found a Text-Title
            {
                if (btitle)  // input icon is also Title
                {
                    RemoveIcon(iconRelType, iconRelId, tmpIcon.Id());
                    break;
                }
            }

            ++listCursor;
        }
    }

    // Put the new icon in the correspondent List
    this->InsertIcon(iconType, icon, index);

    // Put the pair IconRel - Icon in the Relation
    this->InsertIconRelation(iconRelType, iconRelId, icon.Id());

    return;
}

void MvIconDataBase::InsertIconRelation(const int iconRelType, const int iconRelId, const int iconId)
{
    MvRelation* rel = this->IconRelation(iconRelType);
    this->RelationInsertIfNew(*rel, iconRelId, iconId);
}

bool MvIconDataBase::RetrieveIcon(const int iconRelType, const int relId, MvIcon& theIcon)
{
    // Get the correspondent icon relation
    MvRelation* iconRelation = this->IconRelation(iconRelType);

    // Get the correspondent icon type
    int iconType = IconType(iconRelType);

    // Loop through the database and find the unique Layer associated to the Icon
    auto relCursor = iconRelation->begin();
    while (relCursor != iconRelation->end()) {
        // Icon has a unique id; find the one on the list
        if ((*relCursor).first == relId) {
            // Retrieve the icon Id
            const int iconId = (*relCursor).second;

            // Retrieve the icon
            return RetrieveIconFromList(iconType, iconId, theIcon);
        }
        ++relCursor;
    }

    return false;
}

int MvIconDataBase::RetrieveIcon(const int iconRelType, const int iconRelId, MvIconList& iconList)
{
    // Get the correspondent icon relation
    MvRelation* iconRelation = this->IconRelation(iconRelType);

    // Get the correspondent icon type
    int iconType = IconType(iconRelType);

    // Loop through the database and find all icons associated to the Presentable
    auto relCursor = iconRelation->begin();
    while (relCursor != iconRelation->end()) {
        // Find the Presentable
        if ((*relCursor).first == iconRelId) {
            // Retrieve the Icon Id
            const int iconId = (*relCursor).second;

            // Retrieve the icon
            MvIcon tmpIcon;
            if (RetrieveIconFromList(iconType, iconId, tmpIcon))
                iconList.push_back(tmpIcon);
        }
        ++relCursor;
    }

    return iconList.size();
}

bool MvIconDataBase::RetrieveIconFromList(const int iconType, const int iconId, MvIcon& theIcon)
{
    require(iconId >= 0);

    // Get the correspondent icon list
    MvIconList* iconList = this->IconList(iconType);

    // Icons have a unique id; find the one on the list
    auto listCursor = iconList->begin();
    while (listCursor != iconList->end()) {
        if ((*listCursor).Id() == iconId) {
            theIcon = *listCursor;
            return true;
        }
        ++listCursor;
    }
    return false;
}

int MvIconDataBase::RetrieveIcon1(const int /*ii*/, MvIconList& /*iconList*/)
{
    return 1;
}

int MvIconDataBase::RetrieveIcon(const int presentableId, MvIconList& iconList)
{
    // Loop through the database and find all icons associated to a Presentable
    // Loop over all presentable-relations
    iconList.clear();
    for (unsigned int i = 0; i < MV_PREL_COUNT__; i++) {
        if (i == PRES_LAYER_REL)  // ignore this 'special' relation
            continue;

        // Retrieve all icons given a relation
        RetrieveIcon(i, presentableId, iconList);
    }

    return iconList.size();
}

bool MvIconDataBase::CheckIcon(const int iconRelType, const int relId)
{
    // Get the correspondent icon relation
    MvRelation* iconRelation = this->IconRelation(iconRelType);

    // Loop through the database and find the unique Layer associated to the Icon
    auto relCursor = iconRelation->begin();
    while (relCursor != iconRelation->end()) {
        // Icon has a unique id; find the one on the list
        if ((*relCursor).first == relId)
            return true;

        ++relCursor;
    }

    return false;
}

bool MvIconDataBase::RemoveOneIcon(const int iconId)
{
    // Loop over all relations
    bool found = false;
    int iconRelType = 0;
    for (unsigned int i = 0; i < REL_COUNT__; i++) {
        if (i == PRES_LAYER_REL || i == ICON_LAYER_REL)  // ignore these 'special' relations
            continue;

        // Remove icon from relation
        if (this->RemoveIconFromRel(i, iconId)) {
            iconRelType = i;
            found = true;
            break;
        }
    }

    if (!found)
        return false;

    // Remove icon from list
    // First, get the correspondent icon type
    int iconType = IconType(iconRelType);
    this->RemoveIconFromList(iconType, iconId);

    // Remove layer given the icon id
    this->RemoveIcon(ICON_LAYER_REL, iconId);

    return found;
}

void MvIconDataBase::RemoveIcon(const int iconRelType)
{
    // Get the correspondent icon relation
    MvRelation* iconRelation = this->IconRelation(iconRelType);

    // Get the correspondent icon type
    int iconType = IconType(iconRelType);

    // Get the correspondent icon list
    MvIconList* iconList = this->IconList(iconType);

    // Remove all icons
    iconRelation->clear();
    iconList->clear();

#if 0
   MvListCursor listCursor = iconList->begin();
   MvListCursor foundCursor;
   while ( listCursor != iconList->end() )
   {
      // Keep a copy of the found iterator
      foundCursor = listCursor;

      // Increment, so we can continue our search
      ++listCursor;

      // Delete the relation pair
      RemoveRelation ( *foundCursor );

      // Delete icon from List
      iconList->erase ( foundCursor );
   }
#endif
}

void MvIconDataBase::RemoveIcon(const int iconRelType, const int iconRelId, const int iconIdIn)
{
    // Get the correspondent icon relation
    MvRelation* iconRelation = this->IconRelation(iconRelType);

    // Get the correspondent icon type
    int iconType = IconType(iconRelType);

    // Flag indicating if an specific icon or all icons will be deleted
    bool deleteOne = (iconIdIn >= 0) ? true : false;

    // Loop through the database and find all icons associated to the Relation icon
    auto relCursor = iconRelation->begin();
    MvRelationCursor foundCursor;
    while (relCursor != iconRelation->end()) {
        if ((*relCursor).first == iconRelId) {
            // Check if this icon is to be deleted
            if (deleteOne && (*relCursor).second != iconIdIn) {
                ++relCursor;
                continue;
            }

            // Keep a copy of the found iterator
            foundCursor = relCursor;

            // Delete icon from List
            int iconId = (*foundCursor).second;
            RemoveIconFromList(iconType, iconId);

            // Delete the relation pair
            relCursor = iconRelation->erase(foundCursor);

            // Delete entries in the Layer relation too
            if (iconRelType != ICON_LAYER_REL)
                this->RemoveIcon(ICON_LAYER_REL, iconId);
        }
        else
            ++relCursor;
    }
}

void MvIconDataBase::RemoveIconFromList(const int iconType, const int iconId)
{
    require(iconId > 0);

    // Get the correspondent icon list
    MvIconList* iconList = this->IconList(iconType);

    // Icons have a unique icon id; find the one on the list
    for (auto listCursor = iconList->begin(); listCursor != iconList->end();) {
        if ((*listCursor).Id() == iconId)
            listCursor = iconList->erase(listCursor);
        else
            ++listCursor;
    }
    return;
}

bool MvIconDataBase::RemoveIconFromRel(const int iconRelType, const int iconId)
{
    bool found = false;

    // Get the correspondent icon relation
    MvRelation* iconRelation = this->IconRelation(iconRelType);

    // Loop through the database and find all icons associated to the Relation icon
    auto relCursor = iconRelation->begin();
    MvRelationCursor foundCursor;
    while (relCursor != iconRelation->end()) {
        if ((*relCursor).second == iconId) {
            // Keep a copy of the found iterator
            foundCursor = relCursor;

            // Increment, so we can continue our search
            //++relCursor;

            // Delete the relation pair
            iconRelation->erase(foundCursor);

            found = true;
        }
        // else
        ++relCursor;
    }

    return found;
}

bool MvIconDataBase::UpdateIcon(const int iconType, const int iconId, const MvRequest& iconRequest)
{
    require(iconId > 0);

    // Get the correspondent icon list
    MvIconList* iconList = this->IconList(iconType);

    // Icons have a unique id; find the one on the list
    auto listCursor = iconList->begin();
    while (listCursor != iconList->end()) {
        if ((*listCursor).Id() == iconId) {
            (*listCursor).SaveRequest(iconRequest);
            return true;
        }
        ++listCursor;
    }

    return false;
}

bool MvIconDataBase::UpdateIcon(const int iconId, MvRequest& iconRequest)
{
    require(iconId > 0);

    // Find the correspondent icon list
    int iconType = this->FindIconTypeId(iconId);
    MvIconList* iconList = this->IconList(iconType);
    if (iconList == nullptr)
        return false;

    // Icons have a unique id.
    // Find the one on the list and update its request
    auto listCursor = iconList->begin();
    while (listCursor != iconList->end()) {
        if ((*listCursor).Id() == iconId) {
            // Save the ID value to the new request
            iconRequest("_ID") = iconId;
            (*listCursor).SaveRequest(iconRequest);
            return true;
        }
        ++listCursor;
    }

    return false;
}

int MvIconDataBase::FindIconTypeId(int id)
{
    // Loop over all icon list types
    for (int i = 0; i < DB_COUNT__; i++) {
        MvIconList* iconList = this->IconList(i);
        auto lCursor = iconList->begin();
        while (lCursor != iconList->end()) {
            if (id == (*lCursor).Id())
                return i;

            ++lCursor;
        }
    }

    return -1;
}

bool MvIconDataBase::FindIcon(int id, MvIcon& icon)
{
    // Loop over all icon list types
    for (unsigned int i = 0; i < DB_COUNT__; i++) {
        MvIconList* iconList = this->IconList(i);
        auto lCursor = iconList->begin();
        while (lCursor != iconList->end()) {
            if (id == (*lCursor).Id()) {
                icon = *lCursor;
                return true;
            }

            ++lCursor;
        }
    }

    return false;
}

bool MvIconDataBase::FindIcon(const std::string& name, MvIcon& icon)
{
    // Loop over all icon list types
    for (unsigned int i = 0; i < DB_COUNT__; i++) {
        MvIconList* iconList = this->IconList(i);
        auto lCursor = iconList->begin();
        while (lCursor != iconList->end()) {
            if (name == (*lCursor).IconName()) {
                icon = *lCursor;
                return true;
            }

            ++lCursor;
        }
    }

    return false;
}

int MvIconDataBase::FindPresentableId(int iconId, int& dataUnitId)
{
    // Loop over all presentable-relations
    for (unsigned int i = 0; i < MV_PREL_COUNT__; i++) {
        if (i == PRES_LAYER_REL)  // ignore this 'special' relation
            continue;

        MvRelation* rel = this->IconRelation(i);
        auto rCursor = rel->begin();
        while (rCursor != rel->end()) {
            if (iconId == (*rCursor).second)
                return (*rCursor).first;

            ++rCursor;
        }
    }

    // Icon is not directly connected to a presentable.
    // Maybe this icon is a visdef connected to a dataunit. In this case
    // returns the presentable connected to its dataunit.
    //
    // Find the dataunit associated to this visdef
    bool found = false;
    int duId = 0;
    auto relCursor = dataUnitVisDefRelation_.begin();
    while (relCursor != dataUnitVisDefRelation_.end()) {
        // Icon has a unique id; find the one on the list
        if ((*relCursor).second == iconId) {
            // Retrieve the dataunit Id
            duId = (*relCursor).first;

            found = true;
            break;
        }
        ++relCursor;
    }

    if (!found)
        return -1;  // dataunit not found, which means presentable not found

    // Find the presentable associated to this dataunit
    relCursor = presentableDataUnitRelation_.begin();
    while (relCursor != presentableDataUnitRelation_.end()) {
        // Icon has a unique id; find the one on the list
        if ((*relCursor).second == duId) {
            dataUnitId = duId;
            return (*relCursor).first;
        }

        ++relCursor;
    }

    return -1;  // presentable not found
}
