/***************************** 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_(nullptr),
    visitInBackground_(false),
    hasTitleDraw_(false)
{
    // Empty
}

SubPage::SubPage(MvRequest& subpageRequest,
                 int dataunitId,
                 long offset,
                 long offset2,
                 const MatchingInfo& dataInfo) :
    Presentable(subpageRequest),
    subpageCanvas_(nullptr),
    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 != nullptr);

    // 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_(nullptr),
    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 = nullptr;
    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 != nullptr);

    // 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 = nullptr;
    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);
        //        }
        // 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 = nullptr;
    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(std::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);
    SetVisitInBackground(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"), "TEXT");

        // 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(), "LEGEND");

    // 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::DrawTrailerWithReq(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_ != nullptr);

    // Select this presentable sector in the canvas
    // D	subpageCanvas_->PresentableId ( presentableId_ );

    return *subpageCanvas_;
}

bool SubPage::CheckCanvas()
{
    return (subpageCanvas_ != nullptr ? 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, nullptr);
        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::CheckValidVisDefWithIcl(MvIcon& dataUnit, MvRequest& vdRequest, MvIconList& dataUnitList, int ndimFlag)
{
    if (childList_.size() == 0)
        return true;

    MvIconDataBase& dataBase = IconDataBase();
    bool found = false;
    for (auto& child : childList_) {
        // Get dataUnit object
        auto* dataObj = dynamic_cast<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;
}
