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

#include "MvIconDataBase.h"
#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;

	MvRelationCursor 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
	MvListCursor duListCursor = dataUnitList.begin();
	while ( duListCursor != dataUnitList.end() )
	{
		RemoveDataUnit (*duListCursor);
		++duListCursor;
	}
}

void
MvIconDataBase::RemoveDataUnit ( )
{
	// Remove all DataUnits
	MvListCursor 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;

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

	MvRelationCursor 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 )
{
	MvRelationCursor 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
   MvListCursor 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 )
{
   MvListCursor 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;
   MvRelation *relation; // 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
   MvRelationCursor 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
   cout << "MvIconDataBase Contents" << endl;
   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++)
   {
      cout << mlist[i] << endl;
      MvIconList* iconList = this->IconList(i);
      MvListCursor lCursor = iconList->begin();
      while (lCursor != iconList->end())
      {
         cout << " " << (*lCursor).Id();
         ++lCursor;
      }
      cout << " " << endl;
   }

   // Print relations
   cout << " " << endl;
   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;

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

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

   cout << " " << 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;
   MvListCursor 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
  MvRelationCursor lower_ii = relation.lower_bound(firstId);
  MvRelationCursor 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 == 0)
      return;

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

   string spath = (const char*)mpath;
   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 ()
{
   list <string>::iterator listIter = linkList_.begin();

   while ( listIter != linkList_.end() )
   {
      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 NULL;
    }
}

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

// 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;
         MvListCursor 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
      {
         MvListCursor 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
            MvListCursor 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
            MvListCursor 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;
                    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;
      MvListCursor 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
   {
      MvListCursor 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
        MvListCursor 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;
                    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
    MvRelationCursor 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
   MvRelationCursor 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
    MvListCursor 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
    MvRelationCursor 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;
   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
   MvRelationCursor 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
           iconRelation->erase ( foundCursor );

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

       ++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
   MvListCursor listCursor = iconList->begin();
   while ( listCursor != iconList->end() )
   {
      if ( (*listCursor).Id() == iconId )
      {
         iconList->erase ( listCursor );
         break;
      }
      ++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
   MvRelationCursor 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
   MvListCursor 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 == NULL )
      return false;

   // Icons have a unique id.
   // Find the one on the list and update its request
   MvListCursor 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);
      MvListCursor 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);
      MvListCursor lCursor = iconList->begin();
      while (lCursor != iconList->end())
      {
         if ( id == (*lCursor).Id() )
         {
            icon = *lCursor;
            return true;
         }

         ++lCursor;
      }
   }

   return false;
}

bool
MvIconDataBase::FindIcon( const string& name, MvIcon& icon )
{
   // Loop over all icon list types
   for ( unsigned int i = 0; i < DB_COUNT__; i++)
   {
      MvIconList* iconList = this->IconList(i);
      MvListCursor 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);
      MvRelationCursor 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;
   MvRelationCursor 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
}
