/***************************** 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 Page.CC
// Gilberto Camara and Fernando Ii - ECMWF Mar 97
// Lubia Vinhas                    - ECMWF Mar 98

#include "Page.h"

#include <Assertions.hpp>
#include <MvApplication.h>
#include <MvRequestUtil.hpp>

#include "ObjectList.h"
#include "PlotMod.h"
#include "PmContext.h"
#include "SubPage.h"

Page::Page ( MvRequest& pageRequest ):
           Presentable   (pageRequest),
           firstVisible_ (1),
           visibleCount_ (0),
           dataUnitId_   (0),
           dropShouldStay_(false)
{
   // Each page has its own graphics engine
   auto_ptr <GraphicsEngine> eng ( GraphicsEngineFactory::Make ( *this ) );
   engine_ = eng;

   // Find out if there is a view associated with the request
   MvRequest viewRequest = pageRequest.getSubrequest( "VIEW" );
   const char* viewClass = (const char*)viewRequest.getVerb();
   if ( viewClass && ObjectList::IsGeographicalView(viewClass) )
   {
      auto_ptr <PmProjection> proj ( PmProjectionFactory::Make ( viewRequest ) );

      // If AREA is not given, Magics will use the default value
      if ( (const char*)viewRequest("AREA") )
      {
         Location area;
         if ( ! proj.get()->CheckGeodeticCoordinates (area) )
         {
            PlotMod::Instance().MetviewError ( "Some/All Area Coordinates Invalid! Replaced by default values" );
         }
         viewRequest ( "AREA" )  = area.Bottom();
         viewRequest ( "AREA" ) += area.Left();
         viewRequest ( "AREA" ) += area.Top();
         viewRequest ( "AREA" ) += area.Right();

         double vertLong = proj.get()->CheckOriginLongitude ();
         viewRequest ( "MAP_VERTICAL_LONGITUDE" ) = vertLong;
      }
      // Flag to indicate that this is a default area. This will be used by the zoom scheme
      else
      {
         viewRequest("_DEFAULT_AREA") = "YES";
      }

      myRequest_  ( "VIEW" ) = viewRequest;
      pageRequest ( "VIEW" ) = viewRequest;
   }

   // Create a new view request if there isn't any
   if ( !viewClass || ObjectList::IsView ( viewClass ) == false )
   {
      viewRequest = ObjectList::CreateDefaultRequest ( DEFAULTVIEW.c_str(), EXPAND_NO_DEFAULT );
      pageRequest ( "VIEW" ) = viewRequest;
      myRequest_  ( "VIEW" ) = viewRequest;
   }

   // Create a view associated to the page
   auto_ptr <PlotModView> view ( PlotModViewFactory::Make (*this, viewRequest ) );
   myView_ = view;

   // By default, indicates that a drawing needs to be performed later.
   // This default will be overrulled if the dropping icon triggers a call
   // to a Service. In this case, PlotModService::endOfTask should be called
   // and this function should perform the drawing.
   this->HasDrawTask(true);

   // Create the subpages associated to the page
   CreateSubpages();
}

Page::Page ( const Page & old ):
           Presentable     (old),
           firstVisible_   (1),
           visibleCount_   (old.visibleCount_),
           dataUnitId_     (old.dataUnitId_),
           dropShouldStay_ (old.dropShouldStay_),
           drawingPriority_(old.drawingPriority_)
{
   // ZoomStacks not copied
   // Easiest to make new projection from view request
   auto_ptr<PmProjection> proj(  PmProjectionFactory::Make ( old.myView_->ViewRequest() ) );
   projection_ = proj;

   // Each page has its own graphics engine, create one for this page
   auto_ptr <GraphicsEngine> eng ( GraphicsEngineFactory::Make ( *this ) );
   engine_ = eng;

   // Clone the view associated to the page and set the new owner.
   auto_ptr <PlotModView> view ( old.GetView().Clone() ) ;
   myView_ = view;
   myView_->Owner(*this);
}

Page::~Page ()
{
   MvChildList revertedChildList = childList_;
   revertedChildList.reverse ();

   // Get Last and previous to last in child list
   for ( MvChildIterator j = revertedChildList.begin(); j != revertedChildList.end(); ++j )
   {
      if ( ! ( (*j)->GetDeviceData () ) )
         this->RemovePresentable ( *j );
   }

   // Remove Page from DataBase
   this->RemovePageFromDataBase ();
}

string
Page::Name()
{
   return (const char*)ObjectInfo::ObjectName ( myRequest_,"Page",Id() );
}

#if 0
void
Page::Save ()
{
	Cached name = ObjectInfo::ObjectName ( myRequest_,"Page",Id() );
	Cached path = ObjectInfo::ObjectPath ( myRequest_ );

	Cached fullName = path + Cached("/") + name;

	MvRequest genappRequest;
	genappRequest.read ( (const char* ) fullName );

	bool changed = false;

	while  ( genappRequest )
	{
		if ( genappRequest.getVerb() == PLOTPAGE  )
		{
			const char* genappName = genappRequest ( "VIEW" );
			if ( genappName != 0 )
			{
				Cached viewName = mbasename ( genappName );
				Cached newName  = myView_->Name();
				
				if ( ! ( viewName ==  newName ) )
				{
					genappRequest ( "VIEW" ) = (const char*) newName;
					changed = true;
				}
			}

			if ( UpdatePageParameters(genappRequest) )
				changed = true;
		}
		genappRequest.advance();
	}

	if ( changed )
	{
		genappRequest.rewind();
		genappRequest.save ( (const char*) fullName );
	}
}

bool
Page::UpdatePageParameters ( MvRequest& req)
{
	bool changed=false;

       	if ((double)req("TOP") != myLocation_.Top()*100.)
	{
	       	req("TOP") = myLocation_.Top()*100.;
	        changed = true;
	}
       	if ((double)req("BOTTOM") != myLocation_.Bottom()*100.)
	{
		req("BOTTOM") = myLocation_.Bottom()*100.;
	        changed = true;
	}
	if ((double)req("LEFT") != myLocation_.Left()*100.)
	{
		req("LEFT") = myLocation_.Left()*100.;
	        changed = true;
	}
	if ((double)req("RIGHT") != myLocation_.Right()*100.)
	{
		req("RIGHT") = myLocation_.Right()*100.;
	        changed = true;
	}

	return changed;
}
#endif

void	
Page::Draw()
{
	HasDrawTask ( true );
	this->NeedsRedrawing(true);

	// Draw all my children
	DrawChildren();

	this->HasDrawTask ( false );
	this->NeedsRedrawing ( false );
}

void	
Page::DrawProlog()
{
        MvChildIterator child = childList_.begin();
	(*child)->DrawProlog();
}

void
Page::DrawPageHeader()
{
        MvChildIterator child = childList_.begin();
	(*child)->DrawPageHeader ();
}

#if 0
void
Page::DrawNewPage ( Canvas& canvas )
{
	myView_->DrawNewPage( canvas );
}

#if 0 //D
void
Page::DrawFrame ( Canvas& canvas )
{
	myView_->DrawFrame ( canvas );
}
#endif

void
Page::DrawBackground ( Canvas& canvas, bool emptyBack )
{
//FAMI  if ( canvas.HasShared("BACKGROUND") ) return;

//FAMI  canvas.SelectShared("BACKGROUND");

	// Retrieve the graphics engine
	GraphicsEngine& ge = this->GetGraphicsEngine();

	// Set background metafile
	ge.StartPicture ( canvas, "BACKGROUND" );

	// PlotModView NewPage
	myView_->DrawNewPage( canvas );

	if ( ! emptyBack )
		// View background
		myView_->DrawBackground( canvas );

	// PlotModView Frame
//D	myView_->DrawFrame ( canvas );

	// terminate the drawing
	ge.EndPicture ();

	// Set SubPage Drawings Pendings counter
	int subpageId = canvas.PresentableId ();
	Presentable* subpage = Root::Instance().FindBranch ( subpageId );
	subpage->PendingDrawingsAdd();
}

void
Page::DrawForeground ( Canvas& canvas, bool emptyFore )
{
//FAMI  if ( canvas.HasShared ("FOREGROUND") ) return;

//FAMI  canvas.SelectShared("FOREGROUND");

	// Retrieve the graphics engine
	GraphicsEngine& ge = this->GetGraphicsEngine();

	// Set foreground metafile
	ge.StartPicture ( canvas, "FOREGROUND" );

	// PlotModView NewPage
	myView_->DrawNewPage( canvas );

	if ( ! emptyFore )
		// View foreground
		myView_->DrawForeground( canvas );

	// Draw Positional Text on screen
	this->DrawText ( canvas );

	// PlotModView Frame
//D	myView_->DrawFrame ( canvas );

	// terminate the drawing
	ge.EndPicture ();

	// Set SubPage Drawings Pendings counter
	int subpageId = canvas.PresentableId ();
	Presentable* subpage = Root::Instance().FindBranch ( subpageId );
	subpage->PendingDrawingsAdd();
}
#endif

#if 0
bool
Page::FindView(string viewName)
{
	if ((const char*)myView_->GetVerb() == viewName)
		return true;
	else
		return false;
}
#endif

void
Page::Drop ( PmContext& context )
{
   // Skip the first request if it's a drop ( it normally is).
   // If visualize is done directly on a data object, the
   // data request is the first, and should not be skipped.
   if ( context.InRequest().getVerb() == Cached("DROP") )
      context.RewindToAfterDrop();

   myView_->Drop ( context );
}

void
Page::Visit ( Visitor& v )
{
        // Implement the visitor pattern
        // Visit me
	v.Visit (*this);

	// Visit all my children
	Presentable::Visit ( v );
}

bool
Page::InsertDataUnit ( int dataUnitId, long offset, long offset2, 
		       MatchingInfo& dataInfo, int &subPageId )
{
	require ( myView_.get() != 0 );

        // Initialise variables
	SubPage* subpage = 0;            // pointer to subpage

        // Start at the beginning of the list
	MvChildIterator child;
	for ( child = childList_.begin(); child != childList_.end(); ++child )
	{
		subpage = (SubPage*) (*child);

		subpage->InsertDataUnit ( dataUnitId, offset, offset2, dataInfo );
		subPageId = subpage->Id();
			
		subpage->NeedsRedrawing(true);
		RedrawIfWindow ();
		break;
	}

#if 0
        // No subpage has matched or there are no more subpages
        // How to put these pages in the right order ???
	if ( success == false )
	{
		// Retrieve available canvas and the associated request
		MvRequest subpageRequest;
		Canvas* canvas = this->GetNextCanvas (subpageRequest);

		// Create a new subPage
		subpage = new SubPage ( subpageRequest, dataUnitId, offset, offset2, dataInfo );

		// Assign new subpage canvas
		subpage->SetCanvas ( canvas );
		subPageId = subpage->Id();

		// Insert the subpage as a child of the page
		this->Insert ( subpage );

		// Add the new presentable/dataunit relation to icondatabase.
		// subpage->InsertDataUnit normally does this, but the constructor
		// can not do it because the subpage has no parent yet.
		MvIconDataBase& iconDataBase = IconDataBase();
		iconDataBase.PresentableDataUnitRelation (subpage->Id(), dataUnitId);

		// Remember to redraw the subpage if it's visible
		if ( firstVisible_ + visibleCount_ > childList_.size() )
		{
			subpage->SetVisibility( true );
			RedrawIfWindow ();
		}
		else
			subpage->SetVisibility( false );
	}
#endif

	NotifyObservers();

	return true;
}

// The algorithm is the following:
// 1. Drop into Display Window: all icons go to the Plot Level (PL), 
//    with some exceptions:
//    1.0. MText and MLegend (maybe others?) will be connected to the View Level (VL).
//    1.1. VisDef (VD) without DataUnit (DU):
//         1.1.1. single VD:
//                . first, try to replace all existing VDs with similar name
//                . if there is no replacement then
//                     . if there are DUs in the Contents then
//                        . for each DU add VD and remove the old ones
//                            with similar TYPE (Algorithm 1).
//                     . else
//                        . add DU to VL
//                . else return
//         1.1.2. group of VDs:
//                . for each DU in the Contents do Algorithm 1
//    1.2. (VDs) with (DU):
//         . they will be connected together in the PL (Algorithm 1)
//
// 2. Drop into Contents Window: icons stay where there were dropped
//    2.1. (VDs) without (DUs)
//         . replace (VDs) with similar name OR
//         . add (VDs).
//    2.2. (VDs) with (DU)
//         . add (VDs) to (DU).
void
Page::InsertVisDef (MvRequest& vdRequestList, MvIconList& duList)
{
   // Check if visdef was dropped to superpage, in which case it's already added
   if ( myParent_->DroppedHere() )
      return;

   // VD may have dropped with or without DU
   MvListCursor duCursor;
   if ( duList.empty() )
   {
      // Only VD was dropped, no DU
      MvIconDataBase& dataBase = this->IconDataBase();
      MvIconList oldDuList;
      if ( this->DropShouldStay() )
      {
         // Drop came from Contents Window
         if ( this->ContentsDataUnit() )
         {
            // VD dropped into a DU container
            // Retrieve the related DU
            MvIcon theIcon;
            int iconId = this->ContentsDataUnit();
            dataBase.RetrieveIconFromList( DB_DATAUNIT,iconId,theIcon);
            oldDuList.push_back(theIcon);
         }
      }
      else
         // Drop came from Display Window
         // Retrieve all dataunit(s) connected to this Page
         dataBase.RetrieveIcon (PRES_DATAUNIT_REL,Id(),oldDuList);

      // If there are DUs, process VDs for each of them.
      // Otherwise, add VDs to the Page
      if ( !oldDuList.empty() )
      {
         for ( duCursor = oldDuList.begin(); duCursor != oldDuList.end(); ++duCursor)
            this->InsertVisDefToDataUnit(vdRequestList,*duCursor);
      }
      else
         this->InsertVisDefToPresentable(vdRequestList);
   }
   else  // VD and DU dropped together
   {
      // Process VDs for each DU
      for ( duCursor = duList.begin(); duCursor != duList.end(); ++duCursor)
         InsertVisDefToDataUnit(vdRequestList,*duCursor);
   }

   // Redraw window
   RedrawIfWindow();
   NotifyObservers();
}

bool
Page::InsertVisDefToDataUnit (MvRequest& vdRequestList, MvIcon& du )
{
   // Retrieve the Icon Data Base
   MvIconDataBase& dataBase = this->IconDataBase();

   // Remove visdefs with same TYPE
   vdRequestList.rewind();
   while ( vdRequestList )
   {
      MvRequest reqVd = vdRequestList.justOneRequest();
      MvIconList duList;
      if ( CheckValidVisDef( du, reqVd, duList ) )
      {
         MvListCursor duCursor;
         for ( duCursor = duList.begin(); duCursor != duList.end(); ++duCursor)
         {
            MvIcon& theDataUnit = *( duCursor );
            dataBase.RemoveAllVisDefsByDataUnit ( theDataUnit, &reqVd );
         }

      }
      vdRequestList.advance();
   }

   // Insert new VD
   bool found = false;
   vdRequestList.rewind();
   while ( vdRequestList )
   {
      MvRequest reqVd =  vdRequestList.justOneRequest();
      MvIconList duList;
      if ( CheckValidVisDef( du, reqVd, duList ) )
      {
         MvListCursor duCursor;
         for ( duCursor = duList.begin(); duCursor != duList.end(); ++duCursor)
            dataBase.InsertVisDef ( reqVd, Id(), du);

         found  = true;
      }

      vdRequestList.advance();
   }

   if ( found )
   {
      // Remove all drawings related to the DataUnit,
      // but not the DataUnit itself
      removeData_ = false;
      MvIcon dataUnit = du;
      this->RemoveIcon( dataUnit );
      removeData_ = true;
   }

   return found;
}

void
Page::InsertVisDefToPresentable( MvRequest& vdRequestList )
{
   // Retrieve the Icon Data Base
   MvIconDataBase& dataBase = this->IconDataBase();

   // If Visdef came from Contents edition window
   int visdefId = vdRequestList( "_CONTENTS_ICON_EDITED" );
   if (visdefId != 0)
   {
      // Visdef already exists in the data base, replace it
      dataBase.UpdateIcon ( DB_VISDEF, visdefId, vdRequestList );

      return;
   }

   // Insert visdefs into the dataBase. If a visdef with the same NAME
   // and CLASS already exists in the dataBase, replace it.
   bool exclusive = true;
   vdRequestList.rewind();
   while ( vdRequestList )
   {
      MvRequest reqVd = vdRequestList.justOneRequest();
      MvIcon icon(reqVd,true);
      dataBase.InsertIcon( PRES_VISDEF_REL, Id(), icon, -1, exclusive );
      exclusive = false;

      vdRequestList.advance();
   }

   return;
}

#if 0
// Process the dropping of PTEXTS
void
Page::InsertPText (  MvRequest& textRequest )
{
	// Check if ptext was dropped to superpage, in which case it's already added
	Presentable* sp = FindSuperPage();
	if ( sp->DroppedHere() ) return;

	// PText came from Contents window and
	// dropped in a Data Unit window
	if ( dataUnitId_ )
	{
		PlotMod::UserMessage("Drop not allowed");
		return;
	}

	Presentable::InsertPText(textRequest);
}

void
Page::UpdateVisDef(MvRequest &vdreq)
{
	SubPage* subpage = 0;            // pointer to subpage
	MvChildIterator child;

	for ( child = childList_.begin(); child != childList_.end(); ++child )
	{
		subpage = (SubPage*) (*child);
		myView_->UpdateVisDef(subpage,vdreq);
	}
}

void
Page::RemoveVisDef(const char *verb)
{
	SubPage* subpage = 0;            // pointer to subpage
	MvChildIterator child;

	for ( child = childList_.begin(); child != childList_.end(); ++child )
	{
		subpage = (SubPage*) (*child);
		myView_->RemoveVisDef(subpage,verb);
	}
}
#endif

void
Page::RemoveIcon ( MvIcon& icon )
{
	// Remove drawings
        MvRequest req = icon.Request();
	Cached verb = req.getVerb();
	if ( ObjectList::IsVisDef     ( verb ) ||
	     ObjectList::IsDataUnit   ( verb ) ||
	     ObjectList::IsVisDefText ( verb ) )
	{
		MvChildIterator child;
		for ( child = childList_.begin(); child != childList_.end(); ++child )
			(*child)->RemoveIcon ( icon );
		if ( ObjectList::IsVisDefAxis(verb) )
		{
			cout << "Page::RemoveIcon -> NEEDS TO IMPLEMENT FUNCTION ERASEBACKDRAW()" << endl;
		  //U EraseBackDraw();
		}
	}

	// Remove PText annotation
	if ( ObjectList::IsVisDefText ( verb ) )
	{
//U		if ( verb == ANNOTATION )
//U			this->EraseForeDraw ();
	}
	myView_->Reset(req);

	RedrawIfWindow ();
	if ( ObjectList::IsDataUnit ( verb ) )
		NotifyObservers ();
}

void
Page:: RemoveAllData()
{
	RemoveAllDataChildren ();

//	if ( myView_->BackgroundFromData() )
		// Enable new drawing of back and foreground
//		EraseDraw();

	RemoveMyData();

	// Clear Matching Info
	this->InitMatching();
}

#if 0
void
Page::MakeSubPageRequest ( MvRequest& subPageRequest, int position, int nrows, int ncols )
{
	subPageRequest.setVerb("PLOT_SUBPAGE");
  
	// Calculates the size (in percentage of page) of each subpage
	// to be created
	int verSize  = 100 / nrows;
	int horSize  = 100 / ncols;

//	int gap = 2; // two per cent
	int gapX = myRequest_ ("PAGE_X_GAP");
	int gapY = myRequest_ ("PAGE_Y_GAP");

	while ( position >= (nrows * ncols) )
		position -= (nrows * ncols);

	int i = position / ncols;
	int j = position % ncols;

	int subpageTop 		 = gapY + i * verSize;
	subPageRequest("TOP")    = subpageTop;
	subPageRequest("BOTTOM") = subpageTop + verSize - gapY;

	int subpageLeft 	 = gapX + j * horSize;
	subPageRequest("LEFT")   = subpageLeft;
	subPageRequest("RIGHT")  = subpageLeft + horSize - gapX;
}
#endif

void
Page::UpdateDataForZoom(const string& /*zoomInfo*/)
{
 	bool updateWmsForZoom=true;
	if(!updateWmsForZoom)
		return;
		
	vector<Presentable*> presVec;
	FindBranchesByRequest("PRASTERLOOP",presVec);

	for(int i=0; i < (int)presVec.size(); i++)
	{
		Presentable *pres=presVec.at(i);		
		
 		MvIcon  dataUnit;
		pres->DataUnit(dataUnit);
		if(strcmp(dataUnit.Request().getVerb(),"PRASTERLOOP") == 0)
		{
			MvRequest r=dataUnit.Request().getSubrequest("WMS_CLIENT_REQUEST"); 

			r("_CONTEXT")=myView_->ViewRequest();			
	
			r("_CLASS")   = "WMSCLIENT";
			r("_ACTION")  = "update";
			r("_SERVICE") = "WmsClient";
			r("_USELOGWINDOW") = "1";

			int error;
			MvRequest result = MvApplication::waitService("wmsclient",r,error);

			if(result && error == 0)
			{
				cout << "Page::UpdateDataForZoom --> WMS request's bounding box was updated for zoom" << endl;
			  
			  	pres->SetRequest(result);

				MvIconDataBase&  dataBase = pres->IconDataBase();
				dataBase.UpdateIcon(DB_DATAUNIT,dataUnit.Id(),result);
			}
		} 
	}			  
 }

#if 0
void
Page::ZoomRequest ( const Location& zoomCoord )
{
    // Check if Zoom operation can be performed in this Page
    if ( this->CanDoZoom() == false ) return;

    // Retrieve zoom stack structure
    ZoomStacks* stacks = this->GetZoomStacks();
    if ( stacks == 0 ) return;

    // Save new coordinates
    if ( stacks->Size() == 0 )
    {
        // Save initial lat/long coordinates
        PmProjection* projection = this->GetProjection();
        stacks->GeodeticPush ( projection->GeodeticCoord() );
    }
    stacks->GeodeticPush ( zoomCoord );

    // Replace geographical area
    this->ReplaceArea(zoomCoord);

    //Update some dataUnits for the new area
    this->UpdateDataForZoom(zoomCoord);
}
#endif

void
Page::ZoomRequest ( int izoom )
{
     // Retrieve zoom stack structure
    ZoomStacks* stacks = this->GetZoomStacks();
    if ( stacks == 0 ) return;

    string zoomInfo;
   
    //If zoom level is 0 then send the original message to Magics
    if ( izoom == 0 )
    {
        // Set zoom stack index to 0
        stacks->Current(izoom);
        (*myView_).ViewRequest().unsetParam("_ZOOM_DEFINITION");
	zoomInfo = stacks->Get(izoom);
    }
    else
    {  
    	// Get the requested element
    	zoomInfo = stacks->Get(izoom);

    	// Update zoom info
    	(*myView_).ViewRequest()("_ZOOM_DEFINITION") = zoomInfo.c_str();
    }
    
    //Update some dataUnits for the new area
    this->UpdateDataForZoom(zoomInfo);
}

void
Page::ZoomRequest ( const string& zoomInfo )
{
    // Check if Zoom operation can be performed in this Page
    if ( this->CanDoZoom() == false ) return;

    // Retrieve zoom stack structure
    ZoomStacks* stacks = this->GetZoomStacks();
    if ( stacks == 0 ) return;

    // Save new coordinates
    if ( stacks->Size() == 0 )
    {
        // Save initial zoom info
        string cc1("Zoom 0");
        stacks->GeodeticPush ( cc1 );
    }
    stacks->GeodeticPush ( zoomInfo );

    // Update zoom info
    (*myView_).ViewRequest()("_ZOOM_DEFINITION") = zoomInfo.c_str();
    
    this->UpdateDataForZoom(zoomInfo);
}

void
Page::ZoomInfo(int& zoomNumberOfLevels, int& zoomCurrentLevel)
{
    // Initial values
    zoomNumberOfLevels = zoomCurrentLevel = 0;

    // Retrieve stack
    ZoomStacks* stacks = this->GetZoomStacks();
    if ( stacks == 0 ) return;

    // Get values
    zoomNumberOfLevels = stacks->Size();
    zoomCurrentLevel   = stacks->Current();
}

#if 0
void
Page::ReplaceArea ( const Location& zoomCoord, int izoom )
{
	// Replace geographical area in the View
	(*myView_).ReplaceArea ( zoomCoord, izoom );
}
#endif

void
Page::DescribeYourself ( ObjectInfo& description )
{
   description.PutNewLine ("#PageDescription");

   // Ask the view to describe itself
   myView_->DescribeYourself ( description );

   // Remove extra parameters that have not been used in the current
   // implementation of the PLOT_PAGE icon (FAMI, Oct-2015, METV-4.5.7)
   MvRequest myRequest = myRequest_;
   myRequest.unsetParam("ROWS");
   myRequest.unsetParam("COLUMNS");
   myRequest.unsetParam("PAGE_X_GAP");
   myRequest.unsetParam("PAGE_Y_GAP");

   // Convert the page description to the macro syntax
   set<Cached> skipSet;
   skipSet.insert("VIEW");
   description.ConvertRequestToMacro ( myRequest, PUT_LAST_COMMA, MacroName().c_str(), "plot_page", skipSet);

   // Include the view information
   int size = description.MaximumParameterNameLength(myRequest);
   string viewName = myView_->MacroName();
   description.FormatLine ("VIEW",viewName.c_str(),"",size );

   // Close the page description	
   description.PutNewLine (")" );
}

void
Page::CreateSubpages ()
{
	MvRequest sub = myRequest_("SUB_PAGES");

        // Subpage request can be NULL when the Page contains only
        // one Subpage. In this case, creates a default Subpage
	if(!sub)
	{
		MvRequest subpageRequest("PLOT_SUBPAGE");
		subpageRequest = ObjectList::ExpandRequest(subpageRequest,EXPAND_DEFAULTS);

		Presentable* subpage = new SubPage ( subpageRequest );
		ensure ( subpage != 0 );

		// Insert the subpage in the superpage's child list
		this->Insert ( subpage );
		visibleCount_++;
	}
	else
	{
		while(sub)
		{
			MvRequest r =  sub.justOneRequest();
			Presentable* subpage = new SubPage ( r );
			ensure ( subpage != 0 );
			this->Insert ( subpage );
			sub.advance();
			visibleCount_++;
		}
	}
}

#if 0
void
Page::UpdateChildList ()
{
	// Find SubPages with no data
	MvChildList toDeleteSPs;
	MvChildIterator child;
	for ( child = childList_.begin(); child != childList_.end(); ++child )
	{
		if ( ! (*child)->HasData() )
			toDeleteSPs.push_back ( *child );
	}

	// Start deleting superfluous subpages
	for ( child = toDeleteSPs.begin(); child != toDeleteSPs.end(); ++child )
		this->RemovePresentable ( *child );

	GotoFirst();
}
#endif

bool
Page::RemovePresentable ( Presentable* subpage )
{
	// If is the last and doesnt't own a canvas
	if ( ( subpage == childList_.back () ) &&
	     ( (signed int)childList_.size () > visibleCount_ ) &&
	     ! subpage->IsVisible() )
	{
		delete subpage;
		return true;
	}

	MvChildList revertedChildList = childList_;
	revertedChildList.reverse ();

	// Get Last and previous to last in child list
	MvChildIterator j = revertedChildList.begin ();
	Presentable* lastSubPage     = *j;
	Presentable* previousSubPage = *(++j);

	// Save DeviceData and Canvas of the last SubPage that owns a canvas
	DeviceData* savedDevDat = 0;
	Canvas* savedCanvas = 0;
	if ( ( (signed int)childList_.size () == visibleCount_ ) &&
	     ( lastSubPage != subpage ) )
	{
		savedCanvas = &( lastSubPage->GetCanvas () );
		savedDevDat = lastSubPage->ReleaseDeviceData ();
	}

	// Scan child list in reverse order
	while ( lastSubPage != subpage )
	{
		// Disconnect last SubPage canvas from it
                Canvas& lastCanvas = lastSubPage->GetCanvas ();
		lastCanvas.RemovePresentableId ( lastSubPage->Id (), RMNONE );

		// Disconnect and save previous SubPage canvas
		Canvas& previousCanvas = previousSubPage->GetCanvas ();
		previousCanvas.RemovePresentableId ( previousSubPage->Id (), RMNONE );

		// Assign saved canvas to last SubPage
		lastSubPage->SetCanvas ( &previousCanvas );

		lastSubPage->NeedsRedrawing(true);
		previousSubPage->NeedsRedrawing(true);

		// If previous SubPage owns the canvas
		// Save and disconnect device data
		DeviceData* deviceData = 0;
		if ( previousSubPage->GetDeviceData () )
			deviceData = previousSubPage->ReleaseDeviceData ();

		// Assign device data to last SubPage
		if ( deviceData )
			lastSubPage->SetDeviceData ( deviceData );

		// If SubPage to delete owns a canvas
		if ( ( (signed int)childList_.size () == visibleCount_ ) &&
		     ( previousSubPage == subpage ) )
		{
			// Assign saved canvas and Device Data
		        previousSubPage->SetCanvas ( savedCanvas );
			previousSubPage->SetDeviceData ( savedDevDat );

			previousSubPage->NeedsRedrawing(true);
			subpage->NeedsRedrawing(true);
			
			// Make SubPage the last of child list
			MvChildIterator i 
				= find ( childList_.begin(), childList_.end(), subpage );
			ensure ((*i) == subpage);	

			childList_.erase(i);
			childList_.push_back ( subpage );
		}

		// Get next set of SubPages
		lastSubPage = previousSubPage;
		previousSubPage = *(++j);
	}

	if ( ! ( subpage->GetDeviceData () ) )
		delete subpage;

	return true;
}

#if 0
void Page::GotoFirst()
{
	Goto(1);
}

void Page::GotoLast()
{
	Goto(childList_.size());
}

void Page::GotoNext(bool wrap)
{
  int n = firstVisible_ + scrollCount_; //visibleCount_;

  if(wrap) 
    {
      if (  n > childList_.size())
	n = n - childList_.size();
    }
  else 
    {
      if ( n > (childList_.size() - visibleCount_ + 1 ) )
	n = childList_.size() - visibleCount_ + 1;
    }
  Goto(n);
}

void Page::GotoPrevious(bool wrap)
{
  int n = firstVisible_ - scrollCount_; //visibleCount_;

  if(wrap )
    {
      if ( n < 1)
	n = childList_.size() + n;
    }
  else
    {
      if ( n < 1 ) 
	n = 1;
    }

  Goto(n);
}

bool Page::CanGotoNext()
{
	return firstVisible_ + visibleCount_ - 1 < childList_.size();
}

bool Page::CanGotoPrevious()
{
	return firstVisible_ > 1;
}

bool Page::CanScroll()
{
	return visibleCount_  < childList_.size();
}


void Page::Goto(int subpageIndex )
{
	int nrVisible = visibleCount_;
	int subpageNr = subpageIndex;
	MvChildIterator child;
	int i;
	SubPage *subPage = 0, *firstVisSubPage = 0;
	vector<SubPage*> managePages, unmanagePages;

	if ( subpageNr < 1 || subpageNr > childList_.size() ||
	     subpageNr == firstVisible_ )
		return ; // Already visible or invalid 

	int j = 0, position = 1;
	// Now find manage pages and switch canvases with other ones.
	for (child = childList_.begin(),i = 1;
	     child != childList_.end();child++,i++)
	{
	  subPage = (SubPage*) *child;
	  
	  if ( i < subpageNr )
	    continue;

	  if ( i == subpageNr )
	    firstVisSubPage = subPage;

	  Canvas &current = subPage->GetCanvas();
	  current.MovePresentable(subPage,position);
	  managePages.push_back(subPage);

	  j++;
	  position++;

	  if ( i >= (subpageNr + nrVisible - 1) )
	    break;
	}

	// Check if we've got enough pages.
	if ( j < nrVisible )
	  {
	    for (child = childList_.begin(),i = 1;
	     child != childList_.end();child++,i++)
	      {
		subPage = (SubPage*) *child;
		Canvas &current = subPage->GetCanvas();
		current.MovePresentable(subPage,position);
		managePages.push_back(subPage);
		
		j++;
		position++;
		
		if ( j == nrVisible )
		  break;
	      }
	  }

	// Can be required from Visibility method
	firstVisible_ = subpageNr;
	myRequest_ ("FIRST_VISIBLE") = subpageNr;

	// Manage/unmanage pages and update firstVisible_;
	for (i = 0; i < managePages.size(); i++ )
	{
		if ( managePages[i]->NeedsRedrawing() )
		{
			AddTask(*managePages[i], &SubPage::Draw);
			managePages[i]->HasDrawTask ( true );
			managePages[i]->NeedsRedrawing ( false );
		}

		managePages[i]->SetVisibility(true);
	}

	if ( firstVisSubPage )
		firstVisSubPage->NotifyObservers ();

	TaskBase::Flush();  
}

bool Page::LoadNextFrame()
{
	MvChildIterator child;
	bool loading = false;
	SubPage *subPage;

	for ( child = childList_.begin(); !loading && child != childList_.end();
	      child++)
	{
		subPage = (SubPage*) *child;
		if ( subPage->NeedsRedrawing() )
		{
			AddTask(*subPage, &SubPage::Draw);
			subPage->HasDrawTask ( true );
			subPage->NeedsRedrawing ( false );
			loading = true;
		}

		if (subPage->PendingDrawings())
			loading = true;

		subPage->VisitInBackground(true);
	}

	TaskBase::Flush();

	return loading;
}

void
Page::NeedsRedrawing(bool yesno)
{
	for (MvChildIterator ii = childList_.begin(); ii != childList_.end(); ii++)
	{
		if ( yesno )
			(*ii)->NeedsRedrawing (yesno);
		else if ( (*ii)->IsVisible () )
			//Set false only if not visible
			(*ii)->NeedsRedrawing (yesno);
	}
}

bool
Page::NeedsRedrawing()
{
	for (MvChildIterator ii = childList_.begin(); ii != childList_.end(); ii++)
		if ( (*ii)->NeedsRedrawing() )
			return true;

	return false;
}
#endif

void
Page::RemovePageFromDataBase ()
{
	// Verify if my Parent is still alive
	if ( myParent_ == 0 ) return;

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

	// Remove Page from DataBase
	dataBase.RemovePresentable ( presentableId_ );

	// Remove children from DataBase
	MvChildIterator child;
	for ( child = childList_.begin(); child != childList_.end(); ++child )
		dataBase.RemovePresentable ( (*child)->Id() );
}

void
Page::SetProjection ( const MvRequest& viewRequest )
{
	// Initialize projection based on a request
	auto_ptr <PmProjection> proj ( PmProjectionFactory::Make ( viewRequest ) );

	projection_ = proj;
}

#if 0
// Utility function, to get rid of duplicated code.
void Page::RedrawIfWindow(int visdefId)
{
	if (IsAWindow() )
	{
		if ( myView_->EraseAll() )
			EraseDraw (visdefId);

		if ( ! HasDrawTask () )
		{
			AddTask ( *this, &Page::DrawProlog );
			AddTask ( *this, &Page::Draw );
			HasDrawTask ( true );
		}
	}
}
#endif

int
Page::FirstPrintable ()
{
	if ( IsParameterSet ( myRequest_, "FIRST_VISIBLE" ) )
		return myRequest_ ( "FIRST_VISIBLE" );
	else
		return 1;
}

int
Page::PaperPageNumber ( int subPageId )
{
	int nrVisible = visibleCount_;

	MvChildIterator child;
	int i;

	for ( child = childList_.begin(), i = 1; child != childList_.end();
	      ++child, i++ )
	{
		if ( (*child)->Id() == subPageId )
			break;
	}

	int paperPageNumber;
	if ( myParent_->PrintAll () )
	{
		paperPageNumber = ( (i - 1) / nrVisible ) + myParent_->PageIndex();
	}
	else
	{
		int firstPrintable = this->FirstPrintable ();
		if ( ( i >= firstPrintable ) &&
		     ( i < firstPrintable + nrVisible ) ) 
			paperPageNumber = myParent_->PageIndex();
		else
			paperPageNumber = -1;
	}

	return paperPageNumber;
}

#if 0
void
Page::EraseBackDraw ()
{
#if 0 //D
	CanvasList canvasList = this->GetCanvases ();
	CanvasList::iterator i;
	for ( i = canvasList.begin(); i != canvasList.end(); ++i )
		(*i)->ClearShared("BACKGROUND");
#endif
}

void
Page::EraseForeDraw ()
{
#if 0 //D
	CanvasList canvasList = this->GetCanvases ();
	CanvasList::iterator i;
	for ( i = canvasList.begin(); i != canvasList.end(); ++i )
		(*i)->ClearShared ("FOREGROUND");
#endif
}

CanvasList
Page::GetCanvases ()
{
	CanvasList canvasList;
	MvChildIterator child = childList_.begin();
	for ( child = childList_.begin(); child != childList_.end(); ++child )
	{
		Canvas& canvas = (*child)->GetCanvas ();
		canvasList.push_back ( &canvas );
	}

	return canvasList;
}
#endif

Canvas*
Page::GetNextCanvas( MvRequest& subpageReq )
{
	Canvas* canvas = 0;
	MvChildIterator child;
	for ( child = childList_.begin(); child != childList_.end(); ++child )
	{
		if ( ! (*child)->HasData () )
			// Found available canvas on existing child
			break;
	}

	if ( child == childList_.end() )
	{
		// Look for the least used canvas
		int index = childList_.size () % visibleCount_;

		child = childList_.begin();
		for ( int i = 0; i < index; i++ )
			child++;
	}
	canvas     = & ( (*child)->GetCanvas () );
	subpageReq =   (*child)->Request();

	return canvas;
}
 
#if 0
void
Page::InputCoordinates ( const Location& geodeticCoord, int presentableId )
{
	// Check if GribTool is available, start it if not, start it if
	// it's linked to another presentable
	GribToolService::Instance().SetInputPresentableId ( presentableId );

	// Send coordinates to GribTool
	GribToolService::Instance().SetLocation ( geodeticCoord );
}

void
Page::ExecuteToolPlusPointScript ( const Location& geodeticCoord )
{
	// Check if ToolPlus is available, start it if not, start it
	// if it's linked to another presentable
	Presentable* sp = FindSuperPage();
	ToolPlusService::Instance().SetInputPresentableIdPoint ( sp );

	// Send coordinates to EpsTool
	ToolPlusService::Instance().SetLocationPoint ( geodeticCoord );
}

void
Page::ExecuteToolPlusAreaScript ( const Location& geodeticCoord )
{
	// Check if ToolPlus is available, start it if not, start it
	// if it's linked to another presentable
	Presentable* sp = FindSuperPage();
	ToolPlusService::Instance().SetInputPresentableIdArea ( sp );

	// Send coordinates to EpsTool
	ToolPlusService::Instance().SetLocationArea ( geodeticCoord );
}

MvRequest& 
Page::Request () 
{ 
	if ( myView_.get () )
	{
		MvRequest viewRequest = (*myView_).ViewRequest ();
		myRequest_.setValue ( "VIEW", viewRequest );
	}

	return myRequest_; 
}

list<bool>
Page::ReadyChilds ()
{
	list<bool> readyChilds;
	for ( MvChildIterator ii = childList_.begin (); ii != childList_.end ();
	      ii++ )
		readyChilds.push_back ( ! ( (*ii)->PendingDrawings () || (*ii)->NeedsRedrawing () ) );

	return readyChilds;
}

void
Page::PrintInfo ()
{
	SubPage* subpage = 0;            // pointer to subpage

	cout << "Page " << presentableId_ << endl;
	myRequest_.print();

        // Start at the beginning of the list
	MvChildIterator child;
	for ( child = childList_.begin(); child != childList_.end(); ++child )
	{
		subpage = (SubPage*) (*child);
		cout << "SubPage " << subpage->Id() << endl;
		subpage->Request().print();
		Canvas& canvas = (*child)->GetCanvas ();
		cout << "Canvas width/height: " << canvas.GetWidth() << " " << canvas.GetHeight() << endl;
	}
}

void
Page::StartMapEditor()
{
	// Call a Request which is sent directly to PlotMod

	MvRequest mapRequest ("EDIT_MAP");

	mapRequest ( "CUSTOM_WIDTH"  ) = 14.8;
	mapRequest ( "CUSTOM_HEIGHT" ) = 10.5;
	mapRequest ( "INPUT_TYPE")     = myView_->InputMode();
	mapRequest ( "PATH"   )        = ObjectInfo::ObjectPath ( myRequest_ );
	mapRequest ( "_PAGE_ID")       = presentableId_;

	// Add the map information to the request
	mapRequest = mapRequest + myView_->ViewRequest();

	MvApplication::callService ( "uPlot", mapRequest, 0 );	
}	

void
Page::SaveView()
{
        // Confirm if the user really wants to save the geography
	MvRequest genappRequest = myView_->ViewRequest();
	Cached name = ObjectInfo::ObjectName ( genappRequest );
	Cached title("uPlot: Save Geography");
	Cached message("This will save the current geography to the view icon: ");
	message = message + name;
        QuestionDialogManager::Instance().post( (const char*)title, (const char*)message, &genappRequest, okSaveViewCallback, cancelSaveViewCallback);
}

void 
Page::okSaveViewCallback (void *cd)
{
	// Save request
	MvRequest *req = (MvRequest*)cd;
	Cached name = ObjectInfo::ObjectName ( *req );
	Cached path = ObjectInfo::ObjectPath ( *req );
	Cached clas = ObjectInfo::IconClass  ( *req );
	Cached fullPath = path + Cached("/") + name;
	req->save ( (const char*) fullPath );

	// Notify everyone
	MvApplication::notifyIconModified(name,clas);
}

void 
Page::cancelSaveViewCallback (void *)
{
	// empty
}
#endif

bool
Page::CheckValidVisDef ( MvIcon& dataUnit, MvRequest& vdRequest, MvIconList& dataUnitList )
{
   // Check only if a visdef is being wrongly applied to a 1D or 2D fields
   // Get the dimension flag
   int iflag = dataUnit.Request()("_NDIM_FLAG");

   // Check visdef on the page level
   if ( ObjectList::CheckValidVisDef ( dataUnit.IconClass(),vdRequest.getVerb(),iflag ) )
   {
      dataUnitList.push_back(dataUnit);
      return true;
   }

   // Visdef is not valid here, check its children
   bool found = false;
   MvChildIterator child;
   for (child = childList_.begin(); child != childList_.end(); ++child)
   {
      SubPage* subpage = (SubPage*)(*child);
      if ( subpage->CheckValidVisDef ( dataUnit,vdRequest,dataUnitList,iflag ) )
            found = true;
   }

   return found;
}

bool
Page::CanDoZoom ()
{
   return myView_->CanDoZoom();
}

#if 0
bool
Page::CanChangeGeography ()
{
	// Check if my View allows to change the geography
	if ( myView_->CanChangeGeography() == false ) return false;

	// This test is suitable for XSectView, AverageView, ...
	// They do not have a Projection, but they can change coordinates
	if ( this->HasProjection() == false ) return true;

	// Check if the current Projection can change coordinates
	return ( this->CanDoZoom() );
}

void
Page::ChangeFontType ( bool flag )
{
	myView_->ChangeFontType ( flag );
	EraseDraw ();

	if ( ! HasDrawTask () )
	{
		AddTask ( *this, &Page::Draw );
		HasDrawTask ( true );
		NeedsRedrawing ( true );
	}

	TaskBase::Flush();
}
#endif

void Page::RegisterDrawTrailer()
{
	// Call plotting procedure
	myParent_->DrawTrailer();
	return;
}

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

	this->Drop(context);
}

void Page::UpdateView( const MvRequest& view1)
{
   // Expand request
   MvRequest view = ObjectList::ExpandRequest(view1,EXPAND_DEFAULTS);

   // Copy hidden parameters
   view.mars_merge(view1);

   // Create an icon saving the new ID value
   MvIcon iconView(view,true);
   MvRequest reqView = iconView.Request();

   // Update internal Request
   MvRequest presReq = Request();
   presReq("VIEW") = reqView;
   SetRequest(presReq);

   // Update View resquest at myview structure
   myView_->ViewRequest(reqView);

   // Update dataBase
   MvIconDataBase& dataBase = this->IconDataBase();
   dataBase.InsertIcon( PRES_VIEW_REL, presentableId_, iconView, -1, true );
}

void Page::contentsRequest( MvRequest& req)
{
   myView_->contentsRequest( req );
}
