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

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

#include "SubPage.h"
#include "Canvas.h"
#include "PlotModView.h"
#include "GraphicsEngine.h"
#include "DataObject.h"
#include "ObjectList.h"

SubPage::SubPage ( MvRequest& subpageRequest ):
	Presentable ( subpageRequest ),
	subpageCanvas_ (0),
	visitInBackground_(false),
	hasTitleDraw_(false)
{
	// Empty
}

SubPage::SubPage ( MvRequest&          subpageRequest,
		   int                 dataunitId,
		   long                offset,
		   long                offset2,
		   const MatchingInfo& dataInfo ):
	Presentable        ( subpageRequest ),
	subpageCanvas_ 	   (0),
	visitInBackground_ ( false ),
	hasTitleDraw_      ( false )
{
	// Copy the matching information
	matchingInfo_  = dataInfo;

	// Create a new DataObject
	Presentable* dataObj = new DataObject ( dataunitId, offset, offset2, dataInfo.Request() );
	ensure (dataObj != 0);
	
 	// Insert the data object as a child of the subpage
	this->Insert(dataObj);

	// Indicate that the subpage has found a match
	this->MatchFound();
}

SubPage::SubPage(const SubPage& old) :
		 Presentable(old),
		 subpageCanvas_     (0),
  		 visitInBackground_ ( false ),
		 hasTitleDraw_      ( false )
{
}

SubPage::~SubPage()
{
	if ( subpageCanvas_ )
	{
		Canvas& canvas = this->GetCanvas();
		canvas.RemovePresentableId (this->Id(), RMALL);
	}
}

void 
SubPage::Visit ( Visitor& v )
{ 
	// Implements the visitor pattern
	// Visit me
	v.Visit ( *this );
}

void
SubPage::InsertDataUnit ( int dataunitId, long offset, long offset2, const MatchingInfo& dataInfo )
{
	// If data unit already exists -> append it.
	DataObject* dob = 0;
	MvChildIterator child;
	for ( child = childList_.begin(); child != childList_.end(); ++child )
	{
		dob = (DataObject*) (*child);
		if ( dob->AppendDataUnit( dataunitId,offset,offset2,dataInfo.Request() ))
			return;
	}

	// New Data Unit
	// Create a data object which will store ONE field
	Presentable* dataObj = new DataObject ( dataunitId, offset, offset2, dataInfo.Request() );
	ensure ( dataObj != 0 );

	// Put the data object into  the subpage
	this->Insert ( dataObj );

	// Insert the data in the the subpage IconDataBase
	MvIconDataBase&   iconDataBase = this->IconDataBase();
   iconDataBase.InsertIconRelation (PRES_DATAUNIT_REL,presentableId_, dataunitId);

	// Indicate that the subpage has found a match
	this->MatchFound();

	// Set the matching information
	this->SetMatchingInfo ( dataInfo );
}

void
SubPage::RemoveIcon ( MvIcon& icon )
{
	DataObject* dataObj = 0;
	MvChildIterator child;
	Canvas& canvas = this->GetCanvas ();

	Cached verb = icon.Request().getVerb();
	if ( ObjectList::IsVisDef ( verb ) )
	{
		for ( child = childList_.begin(); child != childList_.end(); ++child )
		{
			dataObj = (DataObject*) (*child);
			dataObj->EraseDraw ( icon.Id() );
		}
		// What else??
	}
	else if ( ObjectList::IsDataUnit ( verb ) )
	{
		MvIconDataBase& iconDataBase = this->IconDataBase();
		child = childList_.begin();
		while (  child != childList_.end() )
		{
			dataObj = (DataObject*) (*child);

			MvIcon dataUnit;
			MvChildIterator foundChild;
			//iconDataBase.RetrieveDataUnit( dataObj->DataUnitId(), dataUnit);
         iconDataBase.RetrieveIconFromList( DB_DATAUNIT, dataObj->DataUnitId(), dataUnit);
			if ( dataUnit.Id() == icon.Id() ||
			     dataUnit.ParentId() == icon.Id())
			{
			        foundChild = child;
				child++;
				dataObj->EraseDraw();
				if (myParent_->RemoveData())
					iconDataBase.RemoveDataUnitFromPresRel ( dataUnit,this->Id() );
//D				canvas.RemoveForAll ( this->Id (),"999TITLETEXT" );
				if ( myParent_->RemoveData() )
				  childList_.erase ( foundChild );
			}
			else
			  child++;

		}
		// What else??
	}
	else if ( ObjectList::IsVisDefText ( verb ) ) 
	{
//		MvRequest textRequest = icon.Request();
//		if ( textRequest ( "TEXT_TYPE" ) == Cached ( "TITLE" ) )
		if ( verb != ANNOTATION )
		  canvas.RemoveForAll ( this->Id (),"999TITLETEXT" );
	}
}

bool
SubPage::GetDrawPrior ( DrawPriorMap* drawPriorMap )
{
	if ( childList_.empty() )
		return false;

	DrawPriorMap::iterator j;

	DataObject* dataObj = 0;
	MvChildIterator child;
	for ( child = childList_.begin(); child != childList_.end(); ++child )
	{
		dataObj = (DataObject*) (*child);
		DrawPriorMap tmpDrawPriorMap = dataObj->DrawPriority();
		for ( j = tmpDrawPriorMap.begin(); j != tmpDrawPriorMap.end(); ++j )
			drawPriorMap->insert ( *j );
	}
	return true;
}

bool SubPage::GetDataObjects(list<DataObject*> &dolist)
{
	if ( childList_.empty() )
		return false;

	MvChildIterator child;
	for ( child = childList_.begin(); child != childList_.end(); ++child )
		dolist.push_back( (DataObject*) (*child) );

	return true;
}

#if 0
void
SubPage::EraseDraw ( int visdefId )
{
  if ( visdefId == 0 ) 
    return;

  MvIconDataBase&   iconDataBase = IconDataBase();

  // Call dataObject::EraseDraw for all dataobjects having 
  // visdefs for this id.

  DataObject* dataObj = 0;
  MvChildIterator child;
  MvIcon notused;

  for ( child = childList_.begin(); child != childList_.end(); ++child )
    {
      dataObj = (DataObject*) (*child);
      iconDataBase.DataUnitVisDefRelationRewind();
      bool hasAnyVisdefs = iconDataBase.NextVisDefByDataUnitId(dataObj->DataUnitId(),
							       notused);

      if ( iconDataBase.DataUnitHasVisDef(dataObj->DataUnitId(),visdefId) ||
	   ! hasAnyVisdefs )
	dataObj->EraseDraw ( visdefId );
    }
}
#endif

void
SubPage::Draw ()
{
   if ( ! ( this->IsVisible() || VisitInBackground() ) && this->IsAWindow () )
   {
      HasDrawTask ( false );
      return;
   }

   hasTitleDraw_ = false;
   HasDrawTask ( true );
   NeedsRedrawing ( false ); // For Animation purpose

   PlotModView& view = this->GetView ();

   // FAMI 29/09/2010 Review Paper page number algorithm
   // int paperPageNumber = myParent_->PaperPageNumber ( this->Id() );
#if 0  ///SYLVIE
		if ( paperPageNumber == -1 )
		{
			HasDrawTask ( false );
			return;
		}
		else
		{
			if ( paperPageNumber > myParent_->PaperPageIndex () )
			{
				myParent_->PaperPageIndex ( paperPageNumber );
				this->DrawPageHeader (); // Use incremented PaperPageIndex
			}
		}
#endif  //SYLVIE

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

   ge.StartPicture ( "DATA" );

   view.DrawNewPage ();

   view.DrawBackground ();

   // Data drawing
   view.Draw ( this );

   view.DrawForeground ( );

   // Draw text, legend and import icons
   this->DrawText();
   this->DrawLegend();
   this->DrawImport();

   // Terminate the drawing
   ge.EndPicture ();

   this->PendingDrawingsAdd();

   HasDrawTask ( false );
   VisitInBackground(false);
}

#if 0
void
SubPage::DrawData ()
{
	if ( ! ( this->IsVisible() || VisitInBackground() ) && 
	     this->IsAWindow () )
	{
		HasDrawTask ( false );
		return;
	}

	hasTitleDraw_ = false;
	HasDrawTask ( true );
	NeedsRedrawing ( false ); // For Animation purpose

	PlotModView& view = this->GetView ();
	Canvas &canvas = this->GetCanvas();
//D	if ( ! this->IsAWindow () )
	{
		int paperPageNumber = myParent_->PaperPageNumber ( this->Id() );
		if ( paperPageNumber == -1 )
		{
			HasDrawTask ( false );
			return;
		}
		else
		{
			if ( paperPageNumber > myParent_->PaperPageIndex () )
			{
				myParent_->PaperPageIndex ( paperPageNumber );
				this->DrawPageHeader (); // Use incremented PaperPageIndex
			}
		}
		// Retrieve the graphics engine
		GraphicsEngine& ge = this->GetGraphicsEngine();

		ge.StartPicture ( canvas, "DATA", paperPageNumber );

		view.DrawNewPage ( canvas );

		view.DrawBackground ( canvas );
	}
//D	else
//D	  myParent_->DrawBackground ( canvas ); 

	view.Draw ( this );

//D	if ( ! this->IsAWindow () )
	{
		view.DrawForeground ( canvas );

		// Ask my parent to draw positional text 
		myParent_->DrawText ( canvas );

		// Draw title text 
		this->DrawText ( canvas );

		// Ask my parent's view to draw the frame 
//D		view.DrawFrame ( canvas );
	
		// Retrieve the graphics engine
		GraphicsEngine& ge = this->GetGraphicsEngine();

		// terminate the drawing
		ge.EndPicture ();

		this->PendingDrawingsAdd();
	}
//D	else
//D	  myParent_->DrawForeground ( canvas );

	if ( ! PendingDrawings() )
	  this->DrawText ( this->GetCanvas() );

	HasDrawTask ( false );
	VisitInBackground(false);
}

void
SubPage::DrawForeground ( Canvas &, bool )
{
//D	if ( this->IsAWindow () )
//D		return;
	// Used only on PS file
	{
		Canvas& canvas = this->GetCanvas ();

		int paperPageNumber = myParent_->PaperPageNumber ( this->Id() );

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

		ge.StartPicture ( canvas, "DATAFOREGROUND", paperPageNumber );

		myParent_->DrawNewPage ( canvas );

		PlotModView& view = this->GetView ();
		view.DrawForeground ( canvas );

		// Ask my parent to draw positional and/or user defined title text 
		myParent_->DrawText ( canvas );

		// Ask my parent's view to draw the frame 
//D		myParent_->DrawFrame ( canvas );
	
		// terminate the drawing
		ge.EndPicture ();

		this->PendingDrawingsAdd();
	}
}
#endif

// Draw SubPage Text
void
SubPage::DrawText ( )
{
   // Retrieve the texts associated to this page
   MvIconList textList;
   RetrieveTextList ( textList );

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

   // Draw all texts
   MvListCursor vdCursor;
   for ( vdCursor = textList.begin(); vdCursor != textList.end(); ++vdCursor)
   {
      MvIcon oneIcon = *( vdCursor );
      MvRequest textRequest = oneIcon.Request();

      // Draw the layer
      DrawLayerInfo( (int)textRequest("_ID") );

      // Draw the title
      ge.Draw ( textRequest, true );
   }

   return;
}

// Draw SubPage Legend
void
SubPage::DrawLegend ( )
{
   // Retrieve the legend associated to this page
   // It there is no legend, get a default one
   MvIcon legIcon;
   RetrieveLegend ( legIcon );

   // Draw the layer info
   DrawLayerInfo( legIcon.Id() );

   // Draw legend
   MvRequest legRequest = legIcon.Request();
   GraphicsEngine& ge = this->GetGraphicsEngine();
   ge.Draw ( legRequest, true );
}

// Draw SubPage Import icons
void
SubPage::DrawImport ( )
{
   // Retrieve import icons associated to this page
   MvIconList importList;
   if ( RetrieveImportList(importList) == false )
      return;   // nothing to be plotted

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

   // Draw all icons
   MvListCursor vdCursor;
   for ( vdCursor = importList.begin(); vdCursor != importList.end(); ++vdCursor )
   {
      MvIcon oneIcon = *( vdCursor );
      MvRequest req = oneIcon.Request();

      // Draw the layer
      DrawLayerInfo( (int)req("_ID") );

      // Draw the icon
      ge.Draw ( req, true );
   }

   return;
}

void
SubPage::DrawProlog ()
{
	// Retrieve the graphics engine
	GraphicsEngine& ge = this->GetGraphicsEngine();
	ge.DrawProlog ( );

	this->PendingDrawingsAdd();
}

void
SubPage::DrawPageHeader ()
{
	// Retrieve the graphics engine
	GraphicsEngine& ge = this->GetGraphicsEngine();
	ge.DrawPageHeader ( this->GetCanvas() );

	this->PendingDrawingsAdd();
}

void
SubPage::DrawTrailer () //CHECK IF THIS IS STILL NEEDED
{
	// Retrieve the graphics engine
//	GraphicsEngine& ge = this->GetGraphicsEngine();
//?	ge.DrawTrailer ( this->GetCanvas() );

	this->PendingDrawingsAdd();
}

void
SubPage::DrawTrailer ( MvRequest& fullReq )
{
     // Retrieve the graphics engine
     GraphicsEngine& ge = this->GetGraphicsEngine();
     ge.DrawTrailer ( fullReq );

     this->PendingDrawingsAdd();
}

void
SubPage::SetCanvas ( Canvas * canvas )
{
 	subpageCanvas_ = canvas;
	subpageCanvas_->PresentableId ( this->Id () );
}

Canvas&
SubPage::GetCanvas () const
{
	ensure ( subpageCanvas_ != 0 );
	
	// Select this presentable sector in the canvas
//D	subpageCanvas_->PresentableId ( presentableId_ );

	return *subpageCanvas_;
}

bool
SubPage::CheckCanvas ( )
{
 	return (subpageCanvas_ != 0 ? true : false);
}

#if 0
void  
SubPage::Undo()
{
//	if ( subpageCanvas_.get() )
//		subpageCanvas_->UndoSelection();
}
void 
SubPage::Redo()
{
//	if ( subpageCanvas_.get() )
//		subpageCanvas_->RedoSelection();
}
 
void
SubPage::Reset()
{
//	if ( subpageCanvas_.get() )
//		subpageCanvas_->ResetSelection();
}
#endif

void
SubPage::PendingDrawingsAdd ()
{
	pendingDrawings_++;
	myParent_->PendingDrawingsAdd ();
}

void
SubPage::PendingDrawingsRemove ()
{
	pendingDrawings_--;
	myParent_->PendingDrawingsRemove ();
}

void
SubPage::SetVisibility ( bool visibility )
{
	Presentable::SetVisibility ( visibility );
	
	if ( isVisible_ )
	{
		Canvas& canvas = this->GetCanvas();
		canvas.ShowPresentableId ( this->Id () );
	}
}

bool
SubPage::IsVisible ()
{
//	Canvas& canvas = this->GetCanvas ();
	
//U	return ( canvas.ShowPresentableId () == this->Id () );
	return true; //U revise this function later
}

#if 0
void
SubPage::DescribeData ( MvRequest& request )
{
	for ( MvChildIterator child = childList_.begin();
		              child != childList_.end(); ++child )
	{
		request = request + ( (DataObject*)(*child) )->DataRequest ();
	}
}			

Cached
SubPage::DescribeMatchingInfo ()
{
	Cached title;

	const MatchingInfo mf = this->GetMatchingInfo ();
	MvRequest req = mf.Request ();

	if ( IsParameterSet ( req, "DATA_TYPE" ) )
	{
		title = req ( "DATA_TYPE" );
		if ( IsParameterSet ( req, "DATE" ) )
		{
			title = title + Cached ( " " );
			title = title + ( int ) req ( "DATE" );
		}
  		if ( IsParameterSet ( req, "TIME" ) )
		{
			title = title + Cached ( " " );
			title = title + ( int ) req ( "TIME" );
		}
		if ( IsParameterSet ( req, "LEVEL" ) )
		{
			title = title + Cached ( " " );
			title = title + ( int ) req ( "LEVEL" );
		}
		if ( IsParameterSet ( req, "PARAM" ) )
		{
			title = title + Cached ( " " );
			title = title + (const char*)req( "PARAM" ); //-- may be 'ppp.ttt'
		}
	}
	return title;
}
#endif

long SubPage::PictureId(const char *typeName,bool generate )
{
	long id = 0;
	if ( generate )
	{
		struct timeval t;
		gettimeofday(&t,NULL);
		id = t.tv_sec + t.tv_usec;
		pictureIdMap_[typeName] = id;
	}
	else
	{
		PictureIdMap::iterator ii;
		if ( ( ii = pictureIdMap_.find(typeName)) != pictureIdMap_.end() )
			id =  (*ii).second;
	}

	return id;
}

#if 0
// Reset all if true, reset only current canvas if false.
void SubPage::NeedsRedrawing(bool yesno)
{
  if ( ! subpageCanvas_ )
    return;

  if ( yesno )
    {
      for ( RedrawMap::iterator ii = redrawMap_.begin(); ii != redrawMap_.end(); ii++)
	(*ii).second = true;
    }
  
  // Set value, or Insert it if it's not there.
  redrawMap_[GetCanvas().PresentableString(Id())] = yesno;
}

bool SubPage::NeedsRedrawing()
{
  if ( ! subpageCanvas_ )
    return false;

  RedrawMap::iterator ii = redrawMap_.find(GetCanvas().PresentableString(Id() ) );
  if ( ii != redrawMap_.end() )
    return (*ii).second;

  else
    return true;
}
#endif

bool
SubPage::CheckValidVisDef ( MvIcon& dataUnit, MvRequest& vdRequest, MvIconList& dataUnitList, int ndimFlag )
{
   if ( childList_.size() == 0 )
      return true;

   MvIconDataBase& dataBase = IconDataBase();
   bool found = false;
   for ( MvChildIterator child = childList_.begin(); child != childList_.end(); ++child)
   {
      // Get dataUnit object
      DataObject* dataObj = (DataObject*)(*child);

      // Get corresponding dataunit icon from the DataBase
      MvIcon currentdu;
      dataBase.RetrieveIconFromList(DB_DATAUNIT,dataObj->DataUnitId(),currentdu);

      // Find the appropriate dataUnit and check if it (or its parent) matches.
      if ( currentdu.Id()       == dataUnit.Id() ||
           currentdu.ParentId() == dataUnit.Id() )
      {
         if ( ObjectList::CheckValidVisDef( dataObj->Request().getVerb(), vdRequest.getVerb(), ndimFlag ) )
         {
             // Found a dataUnit associated with the visDef. Add it to the list.
             dataUnitList.push_back (currentdu);
             found = true;
         }
      }
   }

   return found;
}
