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

#ifndef NOMETVIEW_QT
#include "uPlotBase.h"
#endif

#include "SuperPage.h"

#include <Assertions.hpp>
#include "MacroVisitor.h"
#include "MvApplication.h"
#include "MvLog.h"
#include "MvScanFileType.h"
#include "ObjectList.h"
#include "Page.h"
#include "PlotMod.h"
#include "PmContext.h"

int SuperPage::nrSuperPages_ = 0;

#ifndef NOMETVIEW_QT
uPlotBase* SuperPage::plotApplication_ = nullptr;
#endif

SuperPage::SuperPage(const MvRequest& superpageRequest) :
    Presentable(superpageRequest),
    iconDataBase_(new MvIconDataBase),
    preferences_(*this),
    pageIndex_(1),
    superPageIndex_(1),
    printIndex_(0)

{
    // bool calledFromMacro = ObjectInfo::CalledFromMacro(superpageRequest);
    // Retrieve the subrequest describing the Pages
    MvRequest pagesRequest = superpageRequest.getSubrequest(PAGES);

    // Check if the request is a PAGE request
    // If it does not exist, create one PAGE request
    Cached pageVerb = pagesRequest.getVerb();
    if (pageVerb != PLOTPAGE)
        pagesRequest = ObjectList::CreateDefaultRequest("PLOT_PAGE");

    // Rewind the request
    pagesRequest.rewind();

    // Loop through the request, create new Pages
    // and insert them as children of the superpage
    while (pagesRequest) {
        // Extract the page request
        MvRequest newpageRequest = pagesRequest.justOneRequest();

        // Create a new page
        // Presentable* page = new Page ( newpageRequest );
        Page* page = new Page(newpageRequest);
        ensure(page != nullptr);

        // Insert the page in the superpage's child list
        this->InsertOnePageInner(page);

        // Goto the next page request
        pagesRequest.advance();
    }

    // Store my size
    double width = superpageRequest("CUSTOM_WIDTH");
    double height = superpageRequest("CUSTOM_HEIGHT");
    if (width <= 0 || height <= 0) {
        // default A4 landscape
        width = 29.7;
        height = 21.0;
    }
    size_ = PaperSize(width, height);

    // Create and store the device information
    this->SetDeviceInfo(superpageRequest);

#ifndef NOMETVIEW_QT
    // Save information in uPlot
    if (PlotMod::Instance().IsWindow() && plotApplication_)
        plotApplication_->setPresentable(*this);
#endif

    nrSuperPages_++;
    // The purpose of this is to keep PlotMod alive when called in batch. Macro will die once it gets
    // a reply request from PlotMod, and take other modules down before plotting is finished.
    if (nrSuperPages_ == 1 && !PlotMod::Instance().IsInteractive())
        MvApplication::setKeepAlive(true);
}


SuperPage::SuperPage(const SuperPage& old) :
    Presentable(old),
    //  size_(old.size_),
    //  macroFileName_(old.macroFileName_),
    preferences_(old.preferences_),
    //  macroType_(old.macroType_),
    //  zoomPageId_(0),
    //  printPreview_(old.printPreview_),
    //  contentsActive_(old.contentsActive_),
    pageIndex_(1),
    superPageIndex_(1),
    //  paperPageIndex_(1),
    //  backDrawEnabled_(old.backDrawEnabled_),
    printIndex_(0)
{
    // Make a new icon db from the old one
    iconDataBase_ = std::unique_ptr<MvIconDataBase>(new MvIconDataBase(old.IconDataBase()));

    // Set new owner for preferences
    preferences_.Owner(*this);

    // Member myDevice_ is intentionally not copied
    nrSuperPages_++;

    // Should never kick in, nrSuperPages_ should be at least 2 here.
    if (nrSuperPages_ == 1 && !PlotMod::Instance().IsInteractive())
        MvApplication::setKeepAlive(true);
}

SuperPage::~SuperPage()
{
    nrSuperPages_--;

    // This code was disabled for METV-2744
#if 0
    // If called from macro, we don't need to keep PlotMod artificially alive any more.
    if (nrSuperPages_ == 0 && !PlotMod::Instance().IsInteractive())
          MvApplication::setKeepAlive(false);
#endif
}

// -- METHOD  : Create Reply
//
// -- PURPOSE : Indicate to MetviewUI and to Macro the ids
//              of the superpage and pages
//
// -- INPUT   : A reply request
//
// -- OUTPUT  : An updated reply request (which is included
//              in the Context)
//
// -- OBS     : Please see the PmContext class ( especially
//              the destructor )

void SuperPage::CreateReply(MvRequest& replyRequest)
{
    // First, for all its children, include the drop id
    MvChildIterator child = childList_.begin();

    int k = 0;
    while (child != childList_.end()) {
        MvRequest pageReply("DROP");
        pageReply.setValue("DROP_ID", (*child)->Id());

        // If the superpage  has been called from a Macro,
        //   then it needs to ask Macro to notify when finished.
        if (PlotMod::Instance().CalledFromMacro() && k++ == 0)
            pageReply("_NOTIFY") = "NEWPAGE";

        replyRequest += pageReply;
        ++child;
    }

    // This facility was removed due to backward compatibilities.
    // The "count(superpage)" command  should return only the number
    // of pages and not the number of pages plus one (the superpage id).
    // Return drop id for superpage at the end.
    //  MvRequest myReply ( "DROP" );
    //  myReply.setValue  ( "DROP_ID", Id() );
    //  replyRequest = replyRequest + myReply;
}

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

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

void SuperPage::Drop(PmContext& context)
{
    MvLog().dbg() << "DROP";
    // Not dropped here, send to children
    if (!this->DroppedHere()) {
        std::for_each(childList_.begin(), childList_.end(), DropFunctor(context));

        return;
    }

    // Icons dropped in the Super Page.
    // Icons not handled here will be send to
    // the children (e.g. dataunit)
    bool vdReplace = true;
    MvRequest dropRequest = context.InRequest();
    MvRequest remainingReq;
    while (dropRequest) {
        // Process Visdefs
        MvRequest request = dropRequest.justOneRequest();
        MvLog().dbg() << " ->req=";
        request.print();

        Cached verb = request.getVerb();
        if (ObjectList::IsVisDef(verb) == true) {
            this->InsertVisDef(request, vdReplace);
            vdReplace = false;
        }
        else if (ObjectList::IsVisDefCoastlines(verb)) {
            ProcessCoastlines(request, PlotMod::Instance().CalledFromMacro());
        }
        else if (ObjectList::IsWeatherSymbol(verb)) {
            PlotMod::Instance().addWeatherSymbol(request);
            // Process other icons
        }
        else {
            if (!this->InsertCommonIcons(request))
                remainingReq = remainingReq + request;
        }

        dropRequest.advance();
    }

    // Send remaining requests to children
    if (remainingReq) {
        // Update context
        context.Update(remainingReq);

        // Send requests to Page
        std::for_each(childList_.begin(), childList_.end(), DropFunctor(context));
    }
    else
        context.AdvanceToEnd();
}

#if 0
// -- METHOD  : Close
//
// -- PURPOSE : Save and delete a superpage
//
// -- OBS     : This is achieved by sending a request to PlotMod
//              indicating the presentable to be deleted

void
SuperPage::Close()
{
	// Delete all structures and exit
	//command exit(0) should do the job
        Presentable* treeNode = Root::Instance().FindBranch (Id());
          if (treeNode == 0)
          {
                   // Error - Cannot associate page with DropId
                   return;
          }
 
          // delete the tree node and its children
          delete treeNode;
//	  exit(0);

MvRequest closeRequest ("CLOSE");
closeRequest ("DROP_ID") = Id();
MvApplication::callService ( "uPlotBatch", closeRequest, 0 );
}

// -- METHOD  : Save in File
//
// -- PURPOSE : Save the contents of the superpage

void
SuperPage::SaveInFile()
{
	// Don't save if called from a macro

	if ( ObjectInfo::CalledFromMacro ( myRequest_ ) == false )
	{
	}
		// My pages may have changed their views

// SuperPage shouldn't save the views associated to the pages, 
// unless the users ask explicitly for that. A tool for that 
// should be built.
// It will make sense only when the view associated to the page 
// can be replaced.
//		this->SaveChildren ();

}
#endif


// -- METHOD :  Name
//
// -- PURPOSE:  Inform the superpage's name
//
// -- INPUT  :  (none)
//
// -- OUTPUT :  The name of the superpage
std::string
SuperPage::Name()
{
    return (const char*)ObjectInfo::ObjectName(myRequest_, "SuperPage", Id());
}

// -- METHOD :   Draw
//
// -- PURPOSE:   Draws a superpage
//
// -- INPUT  :   (none)
//
// -- OUTPUT : 	 Drawing on window or paper
//
void SuperPage::Draw()
{
    HasDrawTask(true);

    printIndex_ = 0;

    // Draw all my children
    DrawChildren();

    this->HasDrawTask(false);
}

void SuperPage::DrawProlog()
{
    // Call all my children
    MvChildIterator child;
    for (child = childList_.begin(); child != childList_.end(); ++child)
        (*child)->DrawProlog();
}

void SuperPage::DrawTrailerWithReq(MvRequest& fullReq)
{
    // Nothing to be drawn
    if (!this->HasDrawTask())
        return;

    // This was needed to send the driver info to Magics for each newpage.
    // Now, Magics only receive the driver info once (FAMI20150514)
    // Retrieve from the request the Output Device description
    //   fullReq = fullReq + myRequest_("_OUTPUT_DEVICES");

    // Get all requests
    GetAllRequests(fullReq);

    // Reset the draw task
    this->HasDrawTask(false);
}

void SuperPage::GetAllRequests(MvRequest& fullReq, bool checkIconStatus)
{
    // Add the superpage request
    MvIcon temp = MvIcon(true);
    MvRequest sp("SUPERPAGE");
    sp("SUPER_PAGE_X_LENGTH") = size_.GetWidth();
    sp("SUPER_PAGE_Y_LENGTH") = size_.GetHeight();
    sp("_ID") = temp.Id();
    sp("_PRESENTABLE_ID") = this->Id();
    fullReq += sp;

    // Get all page requests
    MvChildIterator child;
    for (child = childList_.begin(); child != childList_.end(); ++child)
        (*child)->DrawTrailerWithReq(fullReq);

    // Take into consideration the icon status
    if (checkIconStatus) {
        MvRequest inReq = fullReq;
        SortRequest(inReq, fullReq);
    }
}

void SuperPage::SortRequest(MvRequest& in, MvRequest& out, bool save_layer)
{
    // Hold pairs <order,index>, where:
    // order : parameter _STACKING_ORDER
    // index : position of an individual request in the full request
    std::map<float, int> mymap;
    std::map<float, int>::iterator it;

    // Build pairs
    MvIconDataBase& dataBase = this->IconDataBase();
    bool sorted = true;
    int index = 0;
    float order = -1.;
    float delta = 0.001;
    const std::string slayer("LAYER");
    in.rewind();
    while (in) {
        MvRequest req = in.justOneRequest();

        // Handle SuperPage and Page requests.
        // They have both parameters _PRESENTABLE_ID and _ID.
        // The _ID should be unique for each icon (MvIcon class generates this value),
        // but in this particular case (Superpage and Page), it contains the value of
        // the Presentable Id. At this moment (20170127), it is not possible to update
        // this code, because Magics is using this information (_ID).
        // Remove this code when Magics starting using _PRESENTABLE_ID and _ID properly.
        if ((const char*)req("_PRESENTABLE_ID")) {
            // Create a new pair
            order = order + delta;
            mymap[order] = index;

            // Next interation
            index++;
            in.advance();
            continue;
        }

        // Skip Layer request
        if (req.getVerb() == slayer) {
            index++;
            in.advance();
            continue;
        }

#if 0
        // Skip icon if its visibility is off
        if (!dataBase.IconVisibility(req("_ID"))) {
            // Icon not to be drawn, skip it
            index++;
            in.advance();
            sorted = false;

            // If icon is a dataunit then it is followed by a visdef,
            // which needs to be skipped too.
            if (in && std::string(in.getVerb()) == "VISDEFS") {
                index++;
                in.advance();
            }
            continue;
        }
#endif

        // Get stacking order value.
        // If it is not available, e.g. value=-1, then apply a computed value,
        // which should allow this request to retain its position in the stack.
        int stack = dataBase.IconStackingOrder(req("_ID"));
        order = (stack != -1) ? (float)stack : order + delta;

        // Create a new pair
        mymap[order] = index;

        // Next interation
        index++;
        in.advance();

        // If the next icon is a visdef then it does not have the visibility
        // and stacking order tags. So, it needs to be added to the list now.
        if (in && std::string(in.getVerb()) == "VISDEFS") {
            order += delta;
            mymap[order] = index++;

            in.advance();
        }
    }

    // First check: if there is a need to sort the input request
    if (sorted && !mymap.empty()) {
        // It means that all icons have visibility tag = true,
        // but still needs to check if the stacking order has changed.
        int index1 = 0;
        it = mymap.begin();
        index1 = it->second;
        it++;
        while (it != mymap.end()) {
            int index2 = it->second;
            if (index2 < index1) {
                sorted = false;
                break;
            }
            index1 = index2;
            it++;
        }
    }

    // Second check: if there is a need to sort the input request
    if (sorted) {
        // It is already sorted
        // Output request set may contain Layer requests
        in.rewind();
        if (save_layer) {
            out = in;
            return;
        }

        // Output request set should not contain Layer requests
        while (in) {
            if (in.getVerb() != slayer)
                out += in.justOneRequest();

            in.advance();
        }
        return;
    }

    // Build an output request sorted by the plotting order
    out.clean();
    int i = 0;
    for (it = mymap.begin(); it != mymap.end(); ++it) {
        // Position the list of requests to the target one
        in.rewind();
        for (i = 0; i < it->second; i++)
            in.advance();

        // Check if the correspondent Layer request needs to be added
        if (save_layer) {
            MvIcon iconLayer;
            if (dataBase.RetrieveIcon(ICON_LAYER_REL, in("_ID"), iconLayer))
                out = out + iconLayer.Request();
        }

        // Add request to the output list
        out = out + in.justOneRequest();
    }
}

#if 0
void
SuperPage::HasReceivedNewpage( bool isLast )
{
        if ( NeedsRedrawing ( ) ) this->Draw ();

//D	if ( this->IsAWindow () ) return;

	if ( isLast )
	{
//D		MvChildIterator child = childList_.begin();
//D		Page* page = (Page*) (*child);			  
//D		page->DrawTrailer ();
		this->DrawTrailer();
		hasNewpage_ = true;
	}
	else
		superPageIndex_ = paperPageIndex_ + 1;
}
#endif

// -- METHOD :  SetDeviceInfo
//
// -- PURPOSE:   Create and initialise the superpage's device
//               (called by the constructor)
// -- INPUT  :   The request which created the superpage
//
// -- OUTPUT :   (none)
//
// -- OBS    :   The device class will implement the "Visitor"
//               pattern, and this method will enable it
//               to "visit" the superpage and its children

void SuperPage::SetDeviceInfo(const MvRequest& inRequest)
{
    // The input request contains the necessary information
    // for creating a device driver

    // The Create Request method will generate an
    // appropriate device Request
    MvRequest deviceRequest = Device::CreateRequest(inRequest);

    myDevice_ = std::unique_ptr<Device>(DeviceFactory::Make(deviceRequest));

    // The device is a visitor - this command will trigger
    // its visit (i.e., its traversal through the page hierarchy)
    myDevice_->Initialise(*this);
}

void SuperPage::SetDeviceInfo()
{
    //	ensure ( myDevice_ == 0);

    // The device is a visitor - this command will trigger
    // its visit (i.e., its traversal through the page hierarchy)
    myDevice_->Initialise(*this);
}

// --- METHOD : GenerateMacro
// --- PURPOSE: Generate a macro file with the contents of the superpage
//
// --- INPUT  : Name of the macro file the be written
//              Type of the macro (can be:
//                  SCREEN_MACRO - Reproduce the output of the screen
//       	    ZOOM_AREA    - Zoom into a single page
//
// --- OUTPUT : Macro program written (by the class MacroDevice)

void SuperPage::GenerateMacro()
{
    // Restart the page index count (used later)
    pageIndex_ = 1;

    // Update the macro type for later use
    //	macroType_ = type;

    // Update the macrofile name, if screenmacro, put file in the superpage directory,
    // otherwise put in temporary directory.
    macroFileName_ = MacroVisitor::CreateFileName(myRequest_);

    // Generate plotting requests without output formats
    MvRequest plotReq;
    this->DrawProlog();             // clean the main Request
    this->Draw();                   // update drawing structure with the latest Contents info
    GetAllRequests(plotReq, true);  // collect all plotting requests

    // Create a new visitor
    std::unique_ptr<Visitor> vis(new MacroVisitor(macroFileName_, plotReq));

    // Visit me
    vis->Visit(*this);

    // Visit all my children
    Presentable::Visit(*vis);

    // Post a message on the screen
    MvLog().popup().info() << "Macro generated in file " << macroFileName_;
}

// -- METHOD :  Visit
//
// -- PURPOSE:  Implement the visitor pattern
//
// -- INPUT  :  visitor (XDevice or PSDevice or MacroVisitor)
//
// -- OUTPUT :  (none)
void SuperPage::Visit(Visitor& v)
{
    // Visit me
    v.Visit(*this);

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

// -- METHOD :  GetMySize
//
// -- PURPOSE:  Return the presentable's size
//
// -- INPUT  :  (none)
//
// -- OUTPUT :  The size of the superpage window
//              (which is always in paper coordinates)
PaperSize
SuperPage::GetMySize()
{
    return size_;
}


// --  METHOD  : MacroPlotIndexName
//
// --  PURPOSE : Provide a sequential index to all pages
//                associated to the superpage
// --  INPUT   : none
//
// --  OUTPUT  : An index into the name returned by plot_superpage.
//
//               Has the side effect of updating member pageIndex_.
//
std::string
SuperPage::MacroPlotIndexName(bool useIndex)
{
    int index = 0;
    if (useIndex)
        index = pageIndex_++;
    else
        index = childList_.size() + 1;

    std::ostringstream ostr;
    ostr << "dw[" << index << "]";
    return ostr.str();
}

// -- METHOD :  Describe Device
//
// -- PURPOSE:  Generate the device description in macro syntax
//
// -- INPUT  :  a description object
//
// -- OUTPUT :  an updated description object
//
void SuperPage::DescribeDevice(ObjectInfo& myDescription)
{
    myDescription.PutNewLine("");
    myDescription.PutNewLine("# Device description");

    // Generate description for file
    MvRequest reqDev("PDFOUTPUT");
    std::string filename = (const char*)ObjectInfo::ObjectPath(myRequest_);
    filename += "/pdf";
    reqDev("OUTPUT_NAME") = filename.c_str();
    myDescription.ConvertRequestToMacro(reqDev, PUT_END, "File", "pdf_output");

    // Check running mode
    myDescription.PutNewLine("");
    myDescription.PutNewLine("# Checks running mode");
    myDescription.PutNewLine("mode = runmode()");
    myDescription.PutNewLine("if mode = 'batch' or mode = 'execute' then");
    myDescription.PutNewLine("   setoutput(File)");
    myDescription.PutNewLine("end if");
}

// -- METHOD :  Describe Yourself
//
// -- PURPOSE:  Generate the superpage description in macro syntax
//
// -- INPUT  :  a description object
//
// -- OUTPUT :  an updated description object
//
void SuperPage::DescribeYourself(ObjectInfo& myDescription)
{
    myDescription.PutNewLine("");
    myDescription.PutNewLine("# SuperPage description");

    std::set<Cached> skipSet;
    skipSet.insert("PAGES");
    myDescription.ConvertRequestToMacro(myRequest_, PUT_LAST_COMMA,
                                        MacroName().c_str(), "plot_superpage",
                                        skipSet);

    // Print the page list
    int size = myDescription.MaximumParameterNameLength(myRequest_);
    std::string pageList = ListMyPages();
    myDescription.FormatLine("PAGES", pageList.c_str(), "", size);

    // Close superpage request
    myDescription.PutNewLine(")");
    myDescription.PutNewLine("# plot_superpage returns a list of drop identifiers.");
    myDescription.PutNewLine("# Index 1 is for first page, and so on.\n");
    std::string ret = "return " + MacroName();
    myDescription.PutNewLine(ret.c_str());
}

// --  METHOD  : ListMyPages
//
// --  PURPOSE : Provide a listing all all pages
//                associated to the superpage
// --  INPUT   : none
//
// --  OUTPUT  : list of page name ( enclosed in [..] )
//
std::string
SuperPage::ListMyPages()
{
    // Put an enclosing brace
    std::string pageNames = "[ ";

    auto child = childList_.begin();
    int npages = 1;
    while (child != childList_.end()) {
        // Put a comma separator
        if (npages > 1)
            pageNames += ", ";

        // Get the name of the page
        pageNames += (*child)->MacroName();

        ++child;
        ++npages;
    }

    // Put a termination brace
    pageNames += " ]";

    return pageNames;
}

void SuperPage::PrintAllV1(bool printAll)
{
    if (printAll)
        myRequest_("_PRINT_OPTION") = "ALL";
    else
        myRequest_("_PRINT_OPTION") = "VISIBLE";
}

bool SuperPage::PrintAll()
{
    return (myRequest_("_PRINT_OPTION") == Cached("ALL"));
}

void SuperPage::InsertVisDef(MvRequest& visdefRequest, bool vdReplace)
{
    // Retrieve the Icon Data Base
    MvIconDataBase& dataBase = this->IconDataBase();

    // Test if Visdef came from Contents edition window
    int visdefId = visdefRequest("_CONTENTS_ICON_EDITED");
    if (visdefId != 0) {
        // Visdef already exists in the data base, replace it
        dataBase.UpdateIcon(DB_VISDEF, visdefId, visdefRequest);
    }
    else {
        // Insert visdef in the data base
        MvIcon visDef(visdefRequest, true);
        dataBase.InsertIcon(PRES_VISDEF_REL, Id(), visDef, -1, vdReplace);
    }

    // Draw me later on
    RedrawIfWindow();
    NotifyObservers();
}

void SuperPage::RemoveIcon(MvIcon& icon)
{
    Cached verb = icon.Request().getVerb();
    if (ObjectList::IsVisDef(verb) ||
        ObjectList::IsVisDefText(verb)) {
        MvChildIterator child;
        for (child = childList_.begin(); child != childList_.end(); ++child)
            (*child)->RemoveIcon(icon);
    }
}

// -- METHOD  :  RetrieveTextTitle
//
// -- PURPOSE : This method will retrieve the Text Title list. If there is no
//              title it will go up to the Root treeNode (the top of the tree)
//              to retrieve the user's default Text Title.
bool SuperPage::RetrieveTextTitle(MvIconList& textList)
{
    bool usingDefault = false;

    // Find Texts associated to this presentable
    MvIconList tList;
    int nIcons = iconDataBase_->RetrieveIcon(PRES_TEXT_REL, presentableId_, tList);

    // Find a Title
    if (nIcons) {
        // Check if type is Title
        MvListCursor lCursor;
        for (lCursor = tList.begin(); lCursor != tList.end(); ++lCursor) {
            if (ObjectList::IsVisDefTextTitle((*lCursor).Request())) {
                textList.push_back((*lCursor));
                return false;
            }
        }
    }

    // Title not found. Search up to the tree.
    usingDefault = myParent_->RetrieveTextTitle(textList);
    ensure(textList.size() > 0);

    // It is a Text default and I am the SuperPage
    // Save Texts default in the DataBase
    if (usingDefault) {
        MvListCursor vdCursor;
        for (vdCursor = textList.begin(); vdCursor != textList.end(); ++vdCursor) {
            MvIcon& text = *(vdCursor);
            iconDataBase_->InsertIcon(PRES_TEXT_REL, presentableId_, text);
        }
    }

    return usingDefault;
}

void SuperPage::RetrieveTextAnnotation(MvIconList& textList)
{
    // Find Texts associated to this presentable
    MvIconList tList;
    int nIcons = iconDataBase_->RetrieveIcon(PRES_TEXT_REL, presentableId_, tList);

    // Find Text Annotations
    if (nIcons) {
        // Check if type is Annotation
        MvListCursor lCursor;
        for (lCursor = tList.begin(); lCursor != tList.end(); ++lCursor) {
            if (ObjectList::IsVisDefTextAnnotation((*lCursor).Request()))
                textList.push_back((*lCursor));
        }
    }

    // Return even if no Annotation was found. There is no need to search up to
    // the tree (i.e. the Root) because Text Annotation is not mandatory to have.
    return;
}

// -- METHOD  :  RetrieveLegend
//
// -- PURPOSE : This method will retrieve the Legend. If there is no
//              Legend it will go up to the Root treeNode (the top of
//              the tree) to retrieve the user's default Legend.
bool SuperPage::RetrieveLegend(MvIcon& legIcon)
{
    bool usingDefault = false;

    // Try to find a visdef associated to the presentable
    if (!iconDataBase_->RetrieveIcon(PRES_LEGEND_REL, presentableId_, legIcon))
        usingDefault = myParent_->RetrieveLegend(legIcon);

    // It is a Legend default and I am the SuperPage
    // Save Legend default in the DataBase
    if (usingDefault)
        iconDataBase_->InsertIcon(PRES_LEGEND_REL, presentableId_, legIcon);

    return usingDefault;
}

int SuperPage::IncrementPrintIndex()
{
    printIndex_++;
    return printIndex_;
}

void SuperPage::ZoomRequestByDef(int treeNodeId, const std::string& zoomInfo)
{
    // Find which child to send the information
    MvChildConstIterator child;
    for (child = childList_.begin(); child != childList_.end(); ++child) {
        if (treeNodeId == (*child)->Id()) {
            // Update page info
            Page* page = (Page*)(*child);
            page->ZoomRequest(zoomInfo);

            // Rebuild page request
            page->HasDrawTask(true);
            page->DrawTask();
            return;
        }
    }
    MvLog().popup().warn() << "Page to perform the zoom not found";
}

void SuperPage::ZoomRequestByLevel(int treeNodeId, int izoom)
{
    // Find which child to send the information
    MvChildConstIterator child;
    for (child = childList_.begin(); child != childList_.end(); ++child) {
        if (treeNodeId == (*child)->Id()) {
            // Update page geographical area
            Page* page = (Page*)(*child);
            page->ZoomRequest(izoom);

            // Rebuild page request
            page->HasDrawTask(true);
            page->DrawTask();
            return;
        }
    }

    MvLog().popup().warn() << "Page to perform the zoom not found";
}

bool SuperPage::UpdateLayerTransparency(int id, int value)
{
    // Retrieve the Icon Data Base
    MvIconDataBase& dataBase = this->IconDataBase();

    // Update transparency value in the data base
    return dataBase.LayerTransparency(id, value);
}

bool SuperPage::UpdateLayerVisibility(int id, bool onoff)
{
    // Retrieve the Icon Data Base
    MvIconDataBase& dataBase = this->IconDataBase();

    // Update visibility value in the Data Base
    return dataBase.LayerVisibility(id, onoff);
}

bool SuperPage::UpdateLayerStackingOrder(int id, int value)
{
    // Retrieve the Icon Data Base
    MvIconDataBase& dataBase = this->IconDataBase();

    // Update plotting order value in the Data Base
    return dataBase.LayerStackingOrder(id, value);
}

bool SuperPage::ExportPlot(MvRequest* req, bool sync)
{
    MvRequest inReq = *req;

    // Create printer request
    MvRequest printerReq("PRINTER_MANAGER");
    printerReq("DESTINATION") = MVFILE;
    printerReq("OUTPUT_DEVICES") = inReq;

    // Redraw in order to get the latest icon status (transparency,
    // order and visibility). These information are saved in the
    // MvDataBase but are not updated in the main Request directly
    // (i.e. MagicsGraphicsEngine::fullRequest_)
    this->DrawProlog();  // clean the main Request
    this->Draw();

    // Retrieve current plot request without output formats
    MvRequest plotReq;
    GetAllRequests(plotReq, true);

    // Create the full request: output formats + plot requests
    MvRequest newReq = printerReq + plotReq;

    // Call uPlotBatch (syncronous or asyncronous) to do the job
    if (sync) {
        int err = 0;
        MvApplication::waitService("uPlotBatch", newReq, err);
        if (err)
            return false;
    }
    else
        MvApplication::callService("uPlotBatch", newReq, nullptr);

    return true;
}

// typedef std::map<int, std::string> ecVisDefMap;

MvRequest
SuperPage::ExportToEcCharts()
{
    // There are a few implementation's options
    // 1. Create a Visitor similar to Macro Visitor (see GenerateMacro function)
    // 2. Because we are assuming that there will be only one
    //    PAGE then the Visitor functionality can be simplified
    // 3. Function DescribeDrops(ObjectInfo,ecVisDefMap) can be
    //    replaced by DescribeDrops(ObjectInfo,MvRequest)
    // 4. Get all the requests and select only the relevant ones
    //    with a special treatment for dataunits.

#if 1
    // Option 4

    // Generate plotting requests without output formats
    MvRequest plotReq;
    this->DrawProlog();             // clean the main Request
    this->Draw();                   // update drawing structure with the latest Contents info
    GetAllRequests(plotReq, true);  // collect all plotting requests

    // Filter only dataunits and visdefs. If a dataunit is not a
    // data file then it needs to be decoded (e.g. Mars Retrieval,
    // Simple Formula, ...)
    MvRequest ecReq;
    while (plotReq) {
        // Process Visdefs
        MvRequest request = plotReq.justOneRequest();
        const char* verb = request.getVerb();
        if (ObjectList::IsVisDef(verb) ||
            ObjectList::IsVisDefText(verb) ||
            ObjectList::IsVisDefLegend(verb) ||
            ObjectList::IsVisDefImport(verb) ||
            ObjectList::IsVisDefCoastlines(verb))
            ecReq = ecReq + request;

        // Process Dataunits. At the moment, only decode data from
        // MARS or from disk. Implement later data coming from a
        // Formula, Simple Formula, Grib Filter, ...
        else if (ObjectList::IsDataUnit(verb) ||
                 strcmp(verb, "PGRIB") == 0) {
            ecReq = ecReq + request;
            std::string iclass = (const char*)ObjectInfo::IconClass(request);
            std::string fileName = ObjectInfo::FileName(request);
            if (!IsBinaryOrMissingFile(fileName.c_str())) {
                MvRequest fileReq;
                fileReq.read(fileName.c_str(), true);

                // Just in case further expansion is needed
                if (!(const char*)fileReq("_PATH") && (const char*)request("_PATH"))
                    fileReq("_PATH") = request("_PATH");
                if (!(const char*)fileReq("_NAME") && (const char*)request("_NAME"))
                    fileReq("_NAME") = request("_NAME");

                ecReq = ecReq + fileReq;
            }
        }
        else if (strcmp(verb, "VISDEFS") == 0) {
            ecReq = ecReq + request("ACTIONS");
        }

        plotReq.advance();
    }
#endif

    return ecReq;
}

void SuperPage::PrintFile(MvRequest& req)
{
    // Create output format request
    MvRequest driverReq("PDFOUTPUT");
    std::string fileName = tempnam(getenv("METVIEW_TMPDIR"), "plot");
    fileName += ".pdf";
    driverReq("OUTPUT_FULLNAME") = fileName.c_str();
    if ((const char*)req("_OUTPUT_FRAME_LIST")) {
        for (int i = 0; i < req.countValues("_OUTPUT_FRAME_LIST"); i++)
            driverReq.addValue("OUTPUT_FRAME_LIST", (int)req("_OUTPUT_FRAME_LIST", i));
        req.unsetParam(("_OUTPUT_FRAME_LIST"));
    }

    // To avoid the printer to clip the bottom and left sides of the plot
    driverReq("OUTPUT_PDF_SCALE") = 0.96;

    // Create printer request
    MvRequest printerReq = req;
    printerReq("OUTPUT_DEVICES") = driverReq;

    // Redraw in order to get the latest icon status (transparency,
    // order and visibility). These information are saved in the
    // MvDataBase but are not updated in the main Request directly
    // (i.e. MagicsGraphicsEngine::fullRequest_)
    this->DrawProlog();  // clean the main Request
    this->Draw();

    // Retrieve current plot requests
    MvRequest fullReq;
    GetAllRequests(fullReq, true);

    // Create the full request: printer request + plot requests
    MvRequest newReq = printerReq + fullReq;

    // Call uPlotBatch to do the job
    MvApplication::callService("uPlotBatch", newReq, nullptr);

    return;
}

Page* SuperPage::InsertOnePage(MvRequest& superpageRequest)
{
    // Retrieve the subrequest describing the Page
    MvRequest pagesRequest = superpageRequest.getSubrequest(PAGES);

    // Check if the request is a PAGE request
    // If it does not exist, create one PAGE request
    const char* pageVerb = pagesRequest.getVerb();
    if (strcmp(pageVerb, PLOTPAGE) != 0)
        pagesRequest = ObjectList::CreateDefaultRequest("PLOT_PAGE");

    // Create a new Page
    // and insert it as a child of the superpage
    // Extract the page request
    MvRequest newpageRequest = pagesRequest.justOneRequest();

    // Create a new page
    Page* page = new Page(newpageRequest);
    ensure(page != nullptr);

    // Insert the page in the superpage's child list
    this->InsertOnePageInner(page);

    // Create and store the device information
    this->SetDeviceInfo();

    return page;
}

void SuperPage::InsertOnePageInner(Page* page)
{
    // Add page to the childlist
    this->Insert(page);

    // Update DataBase
    MvIcon iconView(page->GetView().ViewRequest(), true);
    iconDataBase_->InsertIcon(PRES_VIEW_REL, page->Id(), iconView, -1, true);
}

void SuperPage::contentsRequest(MvRequest& req)
{
    // Find which child to send the information
    int treeNodeId = req("DROP_ID");
    MvChildConstIterator child;
    for (child = childList_.begin(); child != childList_.end(); ++child) {
        if (treeNodeId == (*child)->Id()) {
            // Update page geographical area
            Page* page = (Page*)(*child);
            page->contentsRequest(req);

            // Rebuild page request
            page->HasDrawTask(true);
            page->DrawTask();
            return;
        }
    }

    MvLog().popup().warn() << "Page to perform the zoom not found";
}

#ifndef NOMETVIEW_QT
#ifdef METVIEW_WEATHER_ROOM
void SuperPage::plotWeatherRoom(bool sync)
{
    plotApplication_->plotWeatherRoom(sync);
}
#endif
#endif
