/***************************** 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,0 );
}

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 != 0);
   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
   string str = (const char*)ObjectList::DefaultVisDefClass ( className );

   // Add all valid default visdefs to the list
   size_t ip1,ip2,ip;
   ip1 = 0;
   do
   {
      // Get visdef name
      ip = str.find('/',ip1);
      ip2 = ( ip != string::npos ) ? (ip - ip1) : ip;
      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,1 );

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

   } while (ip != 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 = 0;
   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
   MvChildIterator 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
     MvChildIterator child = childList_.begin();
     while ( child != childList_.end() )
     {
          // Before deletion, make the parent point to NULL
          // This is necessary because otherwise the top
          // statement would generate a recursive call
          (*child)->Parent(0); //Don't get called back

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

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

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

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)->DrawTrailer(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;
      SuperPage* 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 );
}
