/***************************** 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 *************************************/

#include <math.h>
#include <MvRequest.h>
#include <Assertions.hpp>

#include "MagicsGraphicsEngine.h"
#include "Canvas.h"
#include "PlotModConst.h"
#include "Root.h"
#include "SuperPage.h"
#include "DrawingPriority.h"
#include "MagicsTranslator.h"
#include "ObjectList.h"
#include "PlotMod.h"

// ==================================================================
//
// class MagicsGEFactory
class MagicsGEFactory : public GraphicsEngineFactory
{
   virtual GraphicsEngine* Build ( Presentable& owner )
      { return new MagicsGraphicsEngine( owner ); }

public:
   MagicsGEFactory() : GraphicsEngineFactory("Magics") {}
};

// Instance of MagicsGE which is loaded in the "factory"
static MagicsGEFactory magicsGEFactoryInstance;

// ==================================================================
//
// class MagicsGraphicsEngine
MagicsGraphicsEngine::MagicsGraphicsEngine( Presentable& owner ):
   GraphicsEngine ( owner )
{

}

MagicsGraphicsEngine::~MagicsGraphicsEngine()
{

}

#if 0
PSCanvas * 
MagicsGraphicsEngine::MakePSCanvas ( Device&    device, 
				     const Point&     point,
				     const Rectangle& rect,
				     const PaperSize& canvasSize,
				     const PaperSize& fullPaperSize,
			             int   subpageId                ) const
{
	PSCanvas * psCanvas = new PSCanvas ( device, point, rect, 
					     canvasSize, fullPaperSize,
					     subpageId );

return psCanvas;
}

XCanvas * 
MagicsGraphicsEngine::MakeXCanvas  (  Device&      device,  
				     PresentableWidget* parent,
				     const Rectangle&   rect,
				     const PaperSize&   size,
				           int          subpageId ) const 
{
	XCanvas * xc =  new MagicsCanvas ( device, parent, rect, size, subpageId );
	ensure ( xc != 0 );

	return xc;
}
#endif

//U BEGIN
Canvas*
MagicsGraphicsEngine::MakeCanvas  (  Device& device,
				     const Rectangle&  rect,
				     const PaperSize&  size,
				     int  subpageId ) const
{
	Canvas * xc =  new Canvas ( device, rect, size, subpageId );
	ensure ( xc != 0 );

	return xc;
}
//U END

//
//  Draw
//
//  INPUT - magicsRequest: the METVIEW Request which has been "translated"
//                         into a MAGICS request (see the MagicsTranslator class)
//
//  PURPOSE - Add request
void
MagicsGraphicsEngine::Draw ( const MvRequest& magicsRequest, bool translate )
{
   // Add a magicsRequest to the service Request
   // If requested, translate first METVIEW calls into MAGICS calls
   if ( translate )
   {
      MagicsTranslator& mt = MagicsTranslator::Find ( magicsRequest.getVerb() );
      serviceRequest_ += mt.Execute ( magicsRequest );
   }
   else
      serviceRequest_ += magicsRequest;
}

void
MagicsGraphicsEngine::DrawDataVisDef (
                                  MvIcon&     dataUnit,
                                  MvRequest&  dataInfoRequest,
                                  MvIconList& visdefList,
                                  int         nDimFlag
                                 )
{
   // Draw the dataunit
   MvListCursor vdCursor;
   vdCursor = visdefList.begin();
   MvIcon& visdef = *( vdCursor );
   this->DrawDataUnit ( dataUnit, dataInfoRequest, visdef );

   // Check Visdefs. They can be:
   // a) single or multi-visdefs to be applied to the whole fieldset
   // b) multi-visdefs, each one(s) to be applied to specific fields in the fieldset,
   // e.g. MCONT to 1D fields, MWIND to 2d fields.
   MvRequest dataUnitReq = dataUnit.Request();
   MvRequest vdRequest;
   vector<string> vstr;
   ObjectList::VisDefNDimFlags(nDimFlag,vstr);

   // Define Visdefs
   // No info about data dimension. Assumes that all fields have the same
   // dimension; therefore, associate all visdefs to the fieldset.
   if ( vstr.size() ==  0 )
   {
      // Translate METVIEW calls into MAGICS calls
      TranslateVisDefRequest(visdefList,dataUnitReq,vdRequest);
   }
   // Fieldset contains fields with same dimension
   else if ( vstr.size() == 1)
   {
      MvIconList laux = visdefList;
      if ( ObjectList::CheckValidVisDefList (dataUnitReq.getVerb(),laux,vstr[0].c_str() ) )
      {
         // Translate METVIEW calls into MAGICS calls
         TranslateVisDefRequest(laux,dataUnitReq,vdRequest);

         // Update visdef list if only a select visdefs were used
         if ( visdefList.size() != laux.size() )
            visdefList = laux;
      }
   }
   // Multi-dimensional fields, e.g. fieldset contains 1-D and 2-D fields
   else if ( vstr.size() > 1 )
   {
      vdRequest.setVerb("MULTI");

      // Get visdefs related to each dimension
      MvIconList vdList;
      const char* verb = dataUnitReq.getVerb();
      bool vdDefault = false;
      for (unsigned int i = 0; i < vstr.size(); i++)
      {
         MvIconList laux = visdefList;
         const char* caux = vstr[i].c_str();

         // No valid visdef found in the list. Create a default visdef.
         if ( !ObjectList::CheckValidVisDefList (verb,laux,caux) )
         {
            // Get the first default nD-visdef
            string lvd = (const char*)ObjectList::Find ("dataunit",verb,caux );
            size_t pos = lvd.find('/');
            if ( pos != string::npos )
               lvd = lvd.substr(0,pos);

             // Build a default visdef request
             MvRequest vdRequest = ObjectList::CreateDefaultRequest ( lvd.c_str() );

             // Insert the visdef into the list
             MvIcon newVd ( vdRequest );
             laux.clear();
             laux.push_back( newVd );
             vdDefault = true;
         }

         // Translate METVIEW calls into MAGICS calls
         MvRequest vdAux;
         TranslateVisDefRequest(laux,dataUnitReq,vdAux);
         vdRequest(caux) = vdAux;

         // Add used visdefs to the  list
         vdList.splice(vdList.end(),laux);
      }

      // Update visdef list if only a select visdefs were used or
      // a new default visdef was added
      if ( (visdefList.size() != vdList.size()) || vdDefault )
         visdefList = vdList;
   }

   // Draw the visdefs
   MvRequest req("VISDEFS");
   req("ACTIONS") = vdRequest;
   this->Draw ( req );
}

void
MagicsGraphicsEngine::DrawDataUnit ( MvIcon& dataUnit, MvRequest& dataInfoRequest, MvIcon&    visDef )
{
   // Translate METVIEW calls into MAGICS calls
   // Find a metview to magics translator and execute it
   MvRequest vdRequest = visDef.Request();
   MagicsTranslator& mt = MagicsTranslator::Find ( dataUnit.Request().getVerb() );

   MvRequest magicsDataRequest = mt.Execute (  dataUnit.Request(), dataInfoRequest, vdRequest );

   // Draw the data unit (with the appropriate MAGICS request )
   this->Draw ( magicsDataRequest );
}

void
MagicsGraphicsEngine::DrawNewPage( MvRequest& viewRequest )
{
	// Call the magics translator to translate METVIEW calls 
	// into MAGICS calls
	MagicsTranslator& mt = MagicsTranslator::Find ( "PAGE" );
	MvRequest pageRequest = mt.Execute ( viewRequest );

	// Set Background colour
	if ( strcmp (drawType_.c_str(), "BACKGROUND") == 0 ||
	     strcmp (drawType_.c_str(), "DATA") == 0 )
	{
		const char* colour = viewRequest( "SUBPAGE_BACKGROUND_COLOUR" );
		if ( colour != 0 )
			pageRequest ( "SUBPAGE_BACKGROUND_COLOUR" ) = colour;
	}

	// Add extra information to the View, if the View is available.
	// Otherwise, add it to the Page
	pageRequest.advance();
	if (!pageRequest)
		pageRequest.rewind();

	// Add page Id
	pageRequest("METVIEW_ID") = owner_.Id();

	// Add Zoom information
	int zoomNumberOfLevels, zoomCurrentLevel;
	owner_.ZoomInfo(zoomNumberOfLevels,zoomCurrentLevel);
	pageRequest("ZOOM_NUMBER_OF_LEVELS") = zoomNumberOfLevels;
	pageRequest("ZOOM_CURRENT_LEVEL")    = zoomCurrentLevel;

	// Draw the new page
	pageRequest.rewind();
	this->Draw ( pageRequest );
}

#if 0
//
// Boundaries, Cities, Rivers and Grid are plotted in the Foreground.
void MagicsGraphicsEngine::DrawGeoLayersBack ( MvRequest& viewRequest )
{
   //bool draw = false;
   //string ON = "ON";

	// Get DrawingPriority from Page
  // DrawingPriority& tmpDrawingPriority = owner_.GetDrawPriority ();

   // Retrieve the GeoLayers request
   MvRequest geoRequest = viewRequest.getSubrequest ("GEO_LAYERS");

   // Account for land-sea shading
   MvRequest landReq = geoRequest.getSubrequest ("GEO_LAND_SHADE");
   if ( landReq )
      this->Draw ( landReq );

   #if 0
   string onoff = (const char*)expRequest ( "MAP_COASTLINE_LAND_SHADE" );
   if ( onoff == ON )
	{
      int drawPrior = tmpDrawingPriority.GetPriority ( "COASTLINE_LAND_SHADE" );
		if ( drawPrior == 999 )
         coastRequest ("MAP_COASTLINE_LAND_SHADE") = "OFF";
		else
			draw = true;
	}

	// Check if Coastline Sea Shade is to be drawn on the background
   onoff = (const char*)expRequest ( "MAP_COASTLINE_SEA_SHADE" );
   if ( onoff == ON )
	{
      int drawPrior = tmpDrawingPriority.GetPriority ( "COASTLINE_SEA_SHADE" );
		if ( drawPrior == 999 )
         coastRequest ("MAP_COASTLINE_SEA_SHADE") = "OFF";
		else
			draw = true;
	}

	// Check if Coastline is to be drawn on the background
   onoff = (const char*)expRequest ( "MAP_COASTLINE" );
   if ( onoff == ON )
	{
      int drawPrior = tmpDrawingPriority.GetPriority ( "COASTLINE" );
		if ( drawPrior == 999 && draw == false )
         coastRequest ("MAP_COASTLINE") = "OFF";
		else
			draw = true;
	}

	// Draw Coastline on the background
	if ( draw )
	{
      // By default Boundaries, Cities and Rivers are plotted in the Foreground.
      coastRequest ("MAP_BOUNDARIES") = "OFF";
      coastRequest ("MAP_CITIES") = "OFF";
      coastRequest ("MAP_RIVERS") = "OFF";

		// Unset other drawings
      coastRequest ("MAP_GRID") = "OFF";
      coastRequest ("MAP_LABEL") = "OFF";
      this->Draw ( coastRequest );
	}
   #endif
}
#endif

#if 0
void
MagicsGraphicsEngine::DrawCoastlines( Canvas& canvas, MvRequest& viewRequest )
{
	// Retrieve the coastlines Request 
	// Call the magics translator to translate METVIEW calls 
	// into MAGICS calls
	MagicsTranslator& mt = MagicsTranslator::Find ( "COASTLINES" );
	MvRequest coastRequest = mt.Execute ( viewRequest );

	// MAGICS does not know about drawing order
	coastRequest.unsetParam ( "MAP_COASTLINE_DRAWING_ORDER" );

	// Draw the coastlines
	this->Draw ( canvas, coastRequest );
}
#endif

void
MagicsGraphicsEngine::StartPicture ( const char *drawType )
{
#if 0
	MvRequest newRequest ( "MAGICS_ENGINE" );

	int id = canvas.PresentableId();
	newRequest ("SUBPAGE_ID" ) = id;

	if ( PlotMod::Instance().IsWindow() )
	{
		Presentable *subpage = Root::Instance().FindBranch ( id );
		if ( subpage )
		  newRequest("PICTURE_ID") = subpage->PictureId(drawType,true);
       	}
	else
		newRequest ("SUPERPAGE_INDEX") = iconId; // Index defined by SubPage

	// Add information
	SuperPage *sp = (SuperPage*)(owner_.FindSuperPage ( ));
	PaperSize psize = sp->GetMySize();
	double width  = psize.GetWidth();
	double height = psize.GetHeight();
	if ( strcmp(drawType,"DATA") == 0 )
		newRequest ( "PRINT_INDEX" ) = sp->IncrementPrintIndex();
	newRequest ( "TYPE" )                = drawType;
	newRequest ( "ICON_ID" )             = iconId;
	newRequest ( "SUPER_PAGE_X_LENGTH" ) = width;
	newRequest ( "SUPER_PAGE_Y_LENGTH" ) = height;
//	newRequest ( "REFRESH" )             = sp->Request()("_REFRESH");
     serviceRequest_ = newRequest;
#endif

	drawType_ = drawType;
	serviceRequest_.clean();
}

void
MagicsGraphicsEngine::EndPicture ()
{
//     const char* type = serviceRequest_ ("TYPE");
     if (drawType_ == "DATA")
     {
#if 0
          // Build SUPERPAGE request for each plot, taking
          // information from the original MAGICS_ENGINE request.
          // Update page position at PAGE request.
          // Copy the remaining requests.
          while ( serviceRequest_ )
          {
               MvRequest req1 =  serviceRequest_.justOneRequest();
               if ( strcmp((const char*)req1.getVerb(),"MAGICS_ENGINE") == 0 )
               {
                    // set verb to SUPERPAGE and send the request to the Superpage
                    req1.setVerb("SUPERPAGE");
                    Presentable* sp = owner_.FindSuperPage();
                    SuperPage *sp1 = (SuperPage*)(owner_.FindSuperPage ( ));
                    superpageRequest_ += req1;
               }
               else
                    fullRequest_ += req1;

               serviceRequest_.advance();
          }
#endif
          fullRequest_ += serviceRequest_;
     }
}

void
MagicsGraphicsEngine::DrawProlog ( )
{
	// Initialize requests
	fullRequest_.clean();
}

void
MagicsGraphicsEngine::DrawPageHeader ( Canvas& canvas )
{
	MvRequest newRequest ( "MAGICS_ENGINE" );
	newRequest ("SUBPAGE_ID") = canvas.PresentableId ();
	newRequest ("SUPERPAGE_INDEX") = owner_.Parent()->PaperPageIndex();
	newRequest ("TYPE") = "PAGEHEADER";

	MvRequest printerRequest = canvas.PrinterRequest ();
	newRequest ("PRINTER") = printerRequest;	
	serviceRequest_ = newRequest;

	this->EndPicture ();
}

#if 0
void
MagicsGraphicsEngine::DrawTrailer ( Canvas& canvas ) //CHECK IF THIS IS STILL NEEDED
{
        // Call MAGICS to process the request
        MagPlusService::Instance().SetRequest ( fullRequest_ );

#if 0 //D
	MvRequest newRequest ( "MAGICS_ENGINE" );

	newRequest ("SUBPAGE_ID")      = canvas.PresentableId ();
	newRequest ("SUPERPAGE_INDEX") = owner_.MyParent()->PaperPageIndex();
	newRequest ("TYPE")            = "TRAILER";

	MvRequest printerRequest = canvas.PrinterRequest ();
	newRequest ("PRINTER") = printerRequest;	
	serviceRequest_ = newRequest;

	this->EndPicture ();
#endif
}
#endif

void
MagicsGraphicsEngine::DrawTrailer ( MvRequest& fullReq )
{
        fullReq += fullRequest_;
}

double
MagicsGraphicsEngine::ComputeAspectRatio( Canvas& canvas, MvRequest& req )
{
	double spwidth, spheight;

	// Retrieve canvas size
	double ar     =1.;
	double cwidth  = canvas.GetWidth();
	double cheight = canvas.GetHeight();
	if (cwidth == 0. || cheight == 0.) return ar;

	// Retrieve superpage size. If SuperPage does not exist,
	// it is because request came from Assist buttom in the
	// Edit Window
	Presentable* pr = Root::Instance().FindBranch(canvas.PresentableId());
	if ( pr )
	{
		Presentable* sp = pr->FindSuperPage();
                spwidth  = sp->Request()("CUSTOM_WIDTH");
		spheight = sp->Request()("CUSTOM_HEIGHT");
	}
	else
	{
		spwidth  = req("_WIDTH");
		spheight = req("_HEIGHT");
	}
	if (spwidth == 0. || spheight == 0.) return ar;

	// Compute aspect ratio
	ar = (cwidth > cheight) ? (cheight/spheight) : (cwidth/spwidth);

	return sqrt(ar);
}

void
MagicsGraphicsEngine::TranslateVisDefRequest( MvIconList& iList, MvRequest& du, MvRequest& req )
{
   // Prepare the request to be drawn
   // Translate METVIEW calls into MAGICS calls
   // For each icon, find a metview to magics translator and execute it
   MvListCursor iCursor;
   for ( iCursor = iList.begin(); iCursor != iList.end(); ++iCursor)
   {
      MvRequest idef = (*( iCursor )).Request();
      MagicsTranslator& mt2 = MagicsTranslator::Find ( idef.getVerb() );
      req += mt2.Execute ( idef, du );
   }
}
