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

#include "Root.h"

#include <Assertions.hpp>
#include "MagPlusService.h"
#include "ObjectList.h"
#include "PlotMod.h"
#include "SuperPage.h"
#include "Task.h"

Root* Root::rootInstance_;

Root::Root()
{
    install_exit_proc(Root::ServerDead, nullptr);
}

void Root::ServerDead(int, void*)
{
    delete rootInstance_;
}

Root& Root::Instance()
{
    return *rootInstance_;
}

bool Root::DefaultVisDefList(const char* className, MvIconList& visdefList, int ndim_flag, int type)
{
    // Pre-condition
    require(className != nullptr);
    visdefList.clear();  // visdefList should be empty

    // Get the default VisDef given its input class name
    if (type == GETBYVISDEF) {
        // Create default visdef
        MvRequest visdefRequest = ObjectList::UserDefaultRequest(className);

        // Insert the icon visdef into the list
        MvIcon newVisDef(visdefRequest, true);
        visdefList.push_back(newVisDef);

        return true;
    }

    // Get the default VisDef(s) associated to the dataUnit
    // First find out the name of all default VisDef(s) associated to the dataUnit
    std::string str = (const char*)ObjectList::DefaultVisDefClass(className);

    // Add all valid default visdefs to the list
    size_t ip1 = 0, ip2 = 0, ip = 0;
    ip1 = 0;
    do {
        // Get visdef name
        ip = str.find('/', ip1);
        ip2 = (ip != std::string::npos) ? (ip - ip1) : ip;
        std::string str1 = str.substr(ip1, ip2);
        ip1 = ip2 + 1;

        // Accept all valid visdefs
        if (!ObjectList::CheckValidVisDef(className, str1.c_str(), ndim_flag))
            continue;

        // Create default visdef
        MvRequest visdefRequest = ObjectList::UserDefaultRequest(str1.c_str());

        // Now we have all the information to create the VisDef
        MvIcon newVisDef(visdefRequest, true);

        // Insert the visdef into the list
        visdefList.push_back(newVisDef);

    } while (ip != std::string::npos);

    // Post-condition
    ensure(visdefList.size() > 0);

    return true;
}

bool Root::RetrieveTextTitle(MvIconList& textList)
{
    // Create a default request
    MvRequest textRequest = ObjectList::CreateDefaultRequest("MTEXT");

    // Insert the text into the list
    MvIcon newText(textRequest, true);
    textList.push_back(newText);

    return true;
}

bool Root::RetrieveLegend(MvIcon& legIcon)
{
    // Create a default request
    MvRequest legRequest = ObjectList::CreateDefaultRequest("MLEGEND");

    // Copy the default request to the icon.
    // Make sure that the icon has an ID; otherwise, create a new one.
    if (legIcon.Id())
        legIcon.SaveRequest(legRequest);
    else {
        MvIcon auxIcon(legRequest, true);
        legIcon = auxIcon;
    }

    return true;
}

Presentable* Root::FindSuperPage(int index)
{
    Presentable* sp = nullptr;
    int nr = NrOfChildren();

    // Indicate to build a new superpage
    if (HasReceivedNewpage() || nr == 0 || index > (nr - 1)) {
        HasReceivedNewpage(false);
        return sp;
    }

    // Negative index indicates to get the last superpage
    auto ii = childList_.begin();
    int idx = (index < 0) ? nr - 1 : index;
    for (int i = 0; i < idx; ++i, ++ii)
        ;

    sp = (*ii);

    return sp;
}

void Root::Clean()
{
    // Delete all children
    auto child = childList_.begin();
    while (child != childList_.end()) {
        // Before deletion, make the parent point to NULL
        // This is necessary because otherwise the top
        // statement would generate a recursive call
        (*child)->Parent(nullptr);  // Don't get called back

        // Create a temporary pointer to store the child's address
        Presentable* pt = (*child);

        // Remove the child from the list
        childList_.erase(child++);

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

void Root::AddDrawTask()
{
    if (!this->HasDrawTask()) {
        AddTask(*(Presentable*)this, &Presentable::DrawProlog);
        AddTask(*(Presentable*)this, &Presentable::Draw);
        AddTask(*(Presentable*)this, &Presentable::DrawTrailer);
        this->HasDrawTask(true);
    }
}

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

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

    return false;
}

void Root::DrawTask()
{
    // Check if there is something to be drawn
    if (this->CheckDrawTask()) {
        this->AddDrawTask();
        TaskBase::Flush();
    }
}

void Root::DrawTrailer()
{
    // Nothing to be drawn
    if (!this->HasDrawTask())
        return;

    // Flag to indicate if the visualization tree needs to be rebuilt
    // Interactive mode:
    //   a) first drawing: tag is Clear, requests status have not been
    //      modified by the uPlot Contents Manager
    //   b) not first drawing: tag is Refresh, requests status may have
    //      modified by the Uplot Contents Manager
    // Batch mode:
    //   a) always treat as a first drawing
    bool modeClear;
    if (PlotMod::Instance().IsInteractive()) {
        if (this->Refresh())  // not first drawing
            modeClear = false;
        else  // first drawing
        {
            modeClear = true;     // current interaction status
            this->Refresh(true);  // next interaction status
        }
    }
    else {
        modeClear = true;      // current interaction status
        this->Refresh(false);  // next interaction status
    }

    // Get children requests
    MvRequest iconReq;
    MvChildIterator child;
    for (child = childList_.begin(); child != childList_.end(); ++child)
        (*child)->DrawTrailerWithReq(iconReq);

    // If this is a new visualization tree then there is no need to sort
    // the requests (the requests status have not been modified by the
    // uPlot Contents manager).
    if (!modeClear) {
        MvRequest inReq = iconReq;
        auto* sp = (SuperPage*)this->FindSuperPage();
        sp->SortRequest(inReq, iconReq);
    }

    // Build full request
    // First request indicates if the visualization tree needs to be rebuilt
    MvRequest fullReq;
    if (modeClear)
        fullReq.setVerb("CLEAR");
    else
        fullReq.setVerb("REFRESH");

    // Second request set contains the output devices
    fullReq += PlotMod::Instance().OutputFormat()("OUTPUT_DEVICES");

    // Add the remaining requests
    fullReq += iconReq;

    // Call MAGICS to process the request
    MagPlusService::Instance().SetRequest(fullReq);

    // Process weather room
#ifdef METVIEW_WEATHER_ROOM
    SuperPage* sp = (SuperPage*)this->FindSuperPage();
    sp->plotWeatherRoom(true);
#endif

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