/***************************** 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)) {        
        // If AREA is not given, Magics will use the default value
        if ((const char*)viewRequest("AREA")) {
            Location area;
            auto_ptr<PmProjection> proj(PmProjectionFactory::Make(viewRequest));

            const char *proj_name = viewRequest("MAP_PROJECTION");

            if (proj_name && strcmp(proj_name,"CYLINDRICAL") == 0) {
                auto_ptr<PmProjection> proj(PmProjectionFactory::Make(viewRequest));
                if (!proj.get()->CheckGeodeticCoordinates(area)) {
                    PlotMod::Instance().MetviewError("Some/All area coordinates are invalid! Replaced by default values");
                }
            } else {

                // Initialize the bounding box in geodetic coordinates
                double lat1  = viewRequest("AREA", 0);
                double long1 = viewRequest("AREA", 1);
                double lat2  = viewRequest("AREA", 2);
                double long2 = viewRequest("AREA", 3);

                area = Location(lat2, long1, lat1, long2);
            }

            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)) {
            // It needs to implement this function
            // 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) {
                // WMS request's bounding box was updated for zoom
                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);
}
