/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "Presentable.h"

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

int Presentable::treeNodeCounter_ = 0;

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

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

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

// -- METHOD :  Copy constructor

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

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

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

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

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

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

      newPresentable->DuplicateChildren(*oldPresentable);

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

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

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

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


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

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

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

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

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

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

		// Create a temporary pointer to store the child's address
		Presentable* pt = (*child);
		
          // Remove the child from the list
		childList_.erase(child++);

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

	set<PresentableObserver*> o = observers_;
	observers_.clear();

	for(set<PresentableObserver*>::iterator j= o.begin(); j != o.end(); ++j)
		(*j)->Dead(this);
}

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

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

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

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

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

	p->myParent_ = this;

	// Notify the change
	NotifyObservers();
}

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

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

     // Test if child is at a lower depth than parent
	// Remove the child from the tree
	MvChildIterator child = find (childList_.begin(), childList_.end(), p);
	
	ensure ((*child) == p);
	childList_.erase(child);

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

	// Notify the change
	NotifyObservers();
}

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

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

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

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

	for (MvChildIterator ii = childList_.begin(); ii != childList_.end(); ii++)
		(*ii)->Drop(context);
}

#if 0

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

// -- Methods used for Matching 


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

}

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

void 
Presentable::DataRemoved()
{

}
#endif

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

	return iconDataBase.PresentableHasData ( presentableId_ );
}

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

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

	return false;
}

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

		this->RemoveIcon ( theVisDef );
	}
}

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

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

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

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

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

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

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

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

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

	Presentable* node = 0;

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

	return 0;
}

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

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


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

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

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

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

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

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

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

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

// -- METHOD  :  DefaultVisDefList
//
// -- PURPOSE : This method will retrieve the default visdef list 
//              associated to a presentable
//
//         If there is no default visdef associated the presentable
//         this method will go up the tree
//         until the Root treeNode (the top of the tree)
//         that will retrieve the user's default visdef list
bool
Presentable::DefaultVisDefList ( const char* className, MvIconList& visdefList, int type)
{
   require (className != 0);
 
   bool usingDefault = false;
   bool found = false;

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

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

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

   ensure(visdefList.size() > 0);

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

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

	return usingDefault;
}

// -- METHOD  :  RetrieveTextList
//
// -- PURPOSE : This method will retrieve the Text list associated to a presentable.
//              If there is no Text associated to the presentable, this method will
//              go up the tree until the Root treeNode (the top of the tree) that will
//              retrieve the user's default texts.
bool
Presentable::RetrieveTextList ( MvIconList& textList )
{
   require (textList.size() == 0);
   bool usingDefault = false;

   // Try to find a ptext associated to the presentable
   MvIconDataBase& dataBase = this->IconDataBase();
   if (!dataBase.RetrieveIcon ( PRES_TEXT_REL, presentableId_, textList ))
      usingDefault = myParent_->RetrieveTextList(textList);

   ensure(textList.size() > 0);

   return usingDefault;
}

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

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

   return usingDefault;
}

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

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

   return found;
}

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

DeviceData*
Presentable::ReleaseDeviceData ( )
{
	require ( deviceData_.get() != 0 );

	return deviceData_.release();
}

#if 0
// -- METHOD :  GetDevice
//
// -- PURPOSE:  Retrieve the device associated to the presentable
//
// -- INPUT  :  (none)
//
// -- OUTPUT :  a const reference to the device
Device&
Presentable::GetDevice() const 
{
	return myParent_->GetDevice();
}
#endif

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

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

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

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


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

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

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

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

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

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

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

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

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

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

	// Include here all the information on tree contents

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

	// Include the contents of my children

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

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

	Rectangle rect = child.GetLocation();

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

	return PaperSize ( width, height );
}

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

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

void
Presentable::Undo()
{
	MvChildIterator child;

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

void
Presentable::Redo()
{
	MvChildIterator child;

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

void
Presentable::Reset()
{
	MvChildIterator child;

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

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

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

   // Find any texts associated with the presentable
   MvIconList ptextList;
   dataBase.RetrieveIcon ( PRES_TEXT_REL, Id(), ptextList );

   // Describe texts
   bool first = true;
   string ptextDrops;
   MvListCursor lCursor;
   for ( lCursor = ptextList.begin(); lCursor != ptextList.end(); ++lCursor)
   {
      MvIcon icon = *lCursor;
      MvRequest iconRequest = icon.Request();
      if ( (const char*)iconRequest("_SKIP_MACRO") || (const char*)iconRequest("_DEFAULT") )
         continue;

      if ( first )
      {
         myDescription.PutNewLine ( "" );
         myDescription.PutNewLine ( "# Title and annotations descriptions" );
         first = false;
      }
      string sdesc = (const char*)myDescription.Convert(iconRequest);
      ptextDrops = ptextDrops + ", " + sdesc;
   }

   // Find legend associated with the presentable
   MvIconList mlegList;
   dataBase.RetrieveIcon ( PRES_LEGEND_REL, Id(), mlegList );

   // Describe legend
   first = true;
   string mlegDrops;
   for ( lCursor = mlegList.begin(); lCursor != mlegList.end(); ++lCursor)
   {
      MvIcon icon = *lCursor;
      MvRequest iconRequest = icon.Request();
      if ( (const char*)iconRequest("_SKIP_MACRO") || (const char*)iconRequest("_DEFAULT") )
         continue;

      if ( first )
      {
         myDescription.PutNewLine ( "" );
         myDescription.PutNewLine ( "# Legend description" );
         first = false;
      }
      string sdesc = (const char*)myDescription.Convert(iconRequest);
      mlegDrops = mlegDrops + ", " + sdesc;
   }

   // Find the list of Visdefs associated to the presentable
   MvIconList visdefList;
   dataBase.RetrieveIcon ( PRES_VISDEF_REL, Id(), visdefList );

   // Describe isolated visdefs (not connected to the data)
   string listDrops;
   MvIcon dummy;
   DescribeVisDefList(myDescription,visdefList,dummy,listDrops,true,vdMap);

   // Find the list of data units associated to the presentable
   MvIconList dataUnitList;
   dataBase.RetrieveIcon ( PRES_DATAUNIT_REL, Id(), dataUnitList );

   // Describe dataunits and associated visdefs
   MvListCursor du = dataUnitList.begin();
   while ( du != dataUnitList.end() )
   {
      MvIcon currentDataUnit = *du;
      if ( !(int)currentDataUnit.Request()("_SKIP_MACRO")  )
      {
         // Convert the data unit to macro
         string dataUnitName = (const char*)myDescription.ConvertDataUnitToMacro ( currentDataUnit );
         if ( dataUnitName.empty() )
         {
            ++du;
            continue;
         }

         listDrops = listDrops + string(", ") + dataUnitName;

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

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

   if ( this->FindSuperPage () != this ) // Not a SuperPage
   {
      // Describe priority
      DrawingPriority& pageDrawPriority = this->GetDrawPriority ();
      string priorName = pageDrawPriority.DescribeYourself ( myDescription, MacroName() );

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

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

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

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

	return myParent_->PageIndex ();
}

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

	return myParent_->PaperPageIndex ();
}

void
Presentable::PaperPageIndex ( int paperPageIndex )
{
	ensure ( myParent_ != 0);

	myParent_->PaperPageIndex ( paperPageIndex );
}

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

bool Presentable::DescribeVisDefList( ObjectInfo &myDescription,
                                      MvIconList& visdefList, MvIcon &dataUnit,
                                      string &listDrops, bool addToDrops,
                                      MacroVisDefMap &vdMap )
{
   bool found = false;
   MvListCursor vd = visdefList.begin();
   while ( vd != visdefList.end() )
   {
      MvIcon currentvd = *vd;
      MvRequest currentvdRequest = currentvd.Request();
      if ( (const char*)currentvdRequest("_SKIP_MACRO")  || (const char*)currentvdRequest("_DEFAULT") )
      {
         ++vd;
         continue;
      }

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

      currentvdRequest.rewind();
      if ( ( dataUnit.Id() == 0 || CheckValidVisDef ( dataUnit, currentvdRequest ) ) &&  addToDrops )
      {
         found = true;
         listDrops = listDrops + string(", ") + visDefName;
      }
      ++vd;
   }

   return found;
}

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

    MvIcon icon(req,true);
    const char* verb = req.getVerb();
    if ( ObjectList::IsVisDefText ( verb ) )
        dataBase.InsertIconText( presentableId_, icon );

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

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

    else  // Nothing to be inserted
        return false;

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

    return true;
}

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

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

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

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

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

    return;
}

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

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

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

   return false;
}

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

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

   // Get data unit list
   MvIconList* iconList = iconDataBase.IconList(DB_DATAUNIT);
   MvListCursor lCursor = iconList->begin();
   if ( lCursor == iconList->end() )
   {
      cout << "NO DATA UNIT AVAILABLE" << endl;
      return;
   }

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

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

      ++lCursor;
   }
}

#if 0
void Presentable::Add(PresentableObserver* o)
{
	observers_.insert(o);
}

void Presentable::Remove(PresentableObserver* o)
{
	observers_.erase(o);
}

void PresentableObserver::Observe(Presentable& p)
{
	p.Add(this);
	observed_.insert(&p);
}

void PresentableObserver::Forget(Presentable& p)
{
	p.Remove(this);
	observed_.erase(&p);
}

void PresentableObserver::ForgetAll()
{
	set<Presentable*>& o = observed_;
	for(set<Presentable*>::iterator j= o.begin(); j != o.end(); ++j)
		(*j)->Remove(this);
	observed_.clear();
}

PresentableObserver::~PresentableObserver()
{
	ForgetAll();
}
#endif

void PresentableObserver::Dead(Presentable* p)
{
	observed_.erase(p);
}
