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

//
//  Methods for the PlotModView class
//
#include <Assertions.hpp>
#include <MvApplication.h>

#include "Page.h"
#include "PlotModView.h"
#include "ObjectList.h"
#include "MapView.h"
#include "SubPage.h"
#include "Root.h"
#include "PlotMod.h"
#include "DataObject.h"
#include "MvLog.h"

// Initialization of factory map
PlotModViewFactory::FactoryMap* PlotModViewFactory::factoryMap_ = nullptr;

PlotModViewFactory::PlotModViewFactory(const Cached& factoryName) :
    name_(factoryName)
{
    if (factoryMap_ == nullptr)
        factoryMap_ = new PlotModViewFactory::FactoryMap();

    (*factoryMap_)[name_] = this;
}

// Make method
PlotModView*
PlotModViewFactory::Make(Page& owner, MvRequest& viewRequest)
{
    require(owner.Id() > 1);

    // Retrieve the class  associated to the viewRequest
    Cached viewClass = viewRequest.getVerb();

    // Find the request associated with the view, which contains the
    // name of the factory to be called and
    // which contains the name of the service to be called (e.g. CrossSection)
    MvRequest setupRequest = ObjectList::Find("view", viewClass);
    Cached factoryName = setupRequest("name");

    // Call the virtual constructor associated with the factory name
    auto i = factoryMap_->find(factoryName);

    if (i == factoryMap_->end())
        return new MapView(owner, viewRequest, setupRequest);

    return (*i).second->Build(owner, viewRequest, setupRequest);
}

//
// Class PlotModView
PlotModView::PlotModView(Page& owner,
                         const MvRequest& viewRequest,
                         const MvRequest&) :
    owner_(&owner),
    viewRequest_(viewRequest),
    matchingCriteria_(viewRequest),
    pageId_(owner.Id()),
    parentDataUnitId_(0)
{
    // Empty
}

PlotModView::PlotModView(const PlotModView& old) = default;

PlotModView::~PlotModView() = default;

bool PlotModView::Match(const MatchingInfo&, const MatchingInfo&) const
{
    //	return matchingCriteria_.Match(lhs,rhs);
    // Remove temporarily the matching criteria, FAMI 06-2009
    // It needs to be discussed with Magics++ who will be responsible
    // for the matching
    return true;
}

string
PlotModView::MacroName()
{
    return (const char*)ObjectInfo::SpaceToUnderscore(this->Name().c_str());
}

// Sets up the drawing area
void PlotModView::DrawNewPage()
{
    // Retrieve the graphics Engine
    GraphicsEngine& ge = Owner().GetGraphicsEngine();

    // Update view (depending on the type of the view)
    UpdateView();

    // Retrieve sizes e location information
    Presentable* sp = Owner().FindSuperPage();
    PaperSize spSize = sp->GetMySize();       // SuperPage size
    Rectangle myLoc = Owner().GetLocation();  // Page location
    PaperSize mySize = Owner().GetMySize();   // Page size

    // Metview origin coordinates is top left corner
    // Magics origin coordinates is lower left corner
    viewRequest_("_WIDTH") = mySize.GetWidth();
    viewRequest_("_HEIGHT") = mySize.GetHeight();
    viewRequest_("_X_ORIGIN") = spSize.GetWidth() * myLoc.left;
    viewRequest_("_Y_ORIGIN") = spSize.GetHeight() - (spSize.GetHeight() * myLoc.bottom);

    // Ask the graphics engine to draw the frame
    ge.DrawNewPage(viewRequest_);
}

#if 0  // D
void
PlotModView::DrawFrame ( Canvas& canvas )
{
	// Retrieve the graphics Engine
	GraphicsEngine& ge = Owner().GetGraphicsEngine();

	// Ask the graphics engine to draw the frame
	ge.DrawFrame ( canvas, viewRequest_ );
}

void
PlotModView::DrawExternalFrame ( Canvas& canvas )
{
	// Retrieve the graphics Engine
	GraphicsEngine& ge = Owner().GetGraphicsEngine();

	// Ask the graphics engine to draw the frame
	ge.DrawExternalFrame ( canvas, viewRequest_ );
}
#endif

void PlotModView::Draw(SubPage* subPage)
{
    DrawPriorMap drawPriorMap;
    subPage->GetDrawPrior(&drawPriorMap);

    CommonDraw(subPage, drawPriorMap);
}

void PlotModView::CommonDraw(SubPage* subPage, DrawPriorMap& drawPriorMap)
{
    if (subPage->NrOfChildren() == 0) {
        // ERROR: Nr of Children = 0
        return;
    }

    // Loop on all data to be plotted
    DrawPriorMap::iterator j;
    MvIcon dataUnit;
    for (j = drawPriorMap.begin(); j != drawPriorMap.end(); ++j) {
        // Get DataObject node
        int currentDO = ((*j).second).first;  // Get DataObject ID
        Presentable* treeNode = Root::Instance().FindBranch(currentDO);
        if (treeNode == nullptr) {
            MvLog().popup().err() << "PlotModView::CommonDraw -> treeNode == 0";
            return;
        }

        // Set draw actions: data + visdef
        treeNode->DrawDataVisDef();
    }
}

#if 0
void
PlotModView::RetrieveTitleFromMagics ( SubPage* subPage )
{
	// Retrieve initial information
	DrawPriorMap drawPriorMap;
	subPage->GetDrawPrior ( &drawPriorMap );
	MvIconDataBase&  dataBase = Owner().IconDataBase();
	Canvas &canvas = subPage->GetCanvas();
  
        DrawPriorMap::iterator j;
	for ( j = drawPriorMap.begin(); j != drawPriorMap.end(); ++j )
	{
		// If the draw exist, don't retrieve the title again
		string dataString = (*j).first;
		if ( canvas.HasSegment ( subPage->Id (), dataString ) )
			return;

		// Retrieve VisDef (VisDef contains informations to PGRIB)
		MvIcon tmpVisDef;
		if ( ! dataBase.RetrieveVisDef( ((*j).second).second, tmpVisDef) )
		{
			PlotMod::SystemError ( "PlotModView::RetrieveTitleFromMagics -> can't find given visdef id" );
			continue;
		}

		// Get DataObject ID
		Presentable*  treeNode = Root::Instance().FindBranch ( ((*j).second).first );
		if ( treeNode == 0 )
		{
			PlotMod::SystemError ( "PlotModView::RetrieveTitleFromMagics -> treeNode == 0" );
			return;
		}
  
		treeNode->RetrieveTitleFromMagics(tmpVisDef);
       }
}
#endif

void PlotModView::RecreateData()
{
    MvIconDataBase& dataBase = Owner().IconDataBase();
    MvIconList duList;

    // Get all data units and resend them to plotmod.
    // dataBase.DataUnitListByPresentableId(Owner().Id(), duList);
    dataBase.RetrieveIcon(PRES_DATAUNIT_REL, Owner().Id(), duList);

    MvListCursor ii;
    for (ii = duList.begin(); ii != duList.end(); ii++) {
        MvIcon currentIcon = *ii;
        MvRequest req = currentIcon.Request();

        MvRequest dropRequest("DROP");
        dropRequest("DROP_ID") = Owner().Id();

        if ((const char*)req("_DROP_X"))
            dropRequest("DROP_X") = req("_DROP_X");

        if ((const char*)req("_DROP_Y"))
            dropRequest("DROP_Y") = req("_DROP_Y");

        // No need to create a new link. Use the current one, provides
        // that it will not be deleted when cleaning the old data
        req("_CREATE_LINK") = Cached("NO");
        dropRequest = dropRequest + req;
        MvApplication::callService("uPlot", dropRequest, nullptr);
    }

    // Clean out data
    Owner().RemoveAllData();

    Owner().InitMatching();

    // Erase drawings
    // U	Owner().EraseBackDraw();
    // U	Owner().EraseForeDraw();
    //	Owner().EraseDraw();

    // Redraw this page
    Owner().RedrawIfWindow();
    Owner().NotifyObservers();
}

bool PlotModView::GetVisDef(Presentable& treeNode, const MvRequest& compare, MvIconList& vdList)
{
    const char* verb = compare.getVerb();

    MvIconList tmpList;

    bool usingDefault = treeNode.DefaultVisDefList(verb, tmpList, GETBYVISDEF);

    MvListCursor ii;
    MvRequest defaultReq = ObjectList::CreateDefaultRequest(verb);
    int count = compare.countParameters();

    for (ii = tmpList.begin(); ii != tmpList.end(); ii++) {
        bool match = true;
        MvIcon currentvd = *ii;
        MvRequest currentreq = currentvd.Request();

        int defaultvd = currentreq("_DEFAULT");
        if (defaultvd) {
            // currentvd.SaveRequest(compare);
            usingDefault = true;
            match = true;
        }
        else {
            // Check that all values in compare request are either the same as in currentReq, or if
            // currentReq value not set, that it is the default
            for (int i = 0; i < count; i++) {
                const char* param = compare.getParameter(i);
                const char* testValue = compare(param);
                const char* value = currentreq(param);
                const char* defValue = defaultReq(param);

                compare.getValue(testValue, param, 0);
                currentreq.getValue(value, param, 0);
                if (!((value && !strcmp(value, testValue)) || (defValue && !strcmp(defValue, testValue)))) {
                    match = false;
                    break;
                }
            }
        }

        if (match)
            vdList.push_back(currentvd);
    }

    return usingDefault;
}

void PlotModView::Owner(Page& xx)
{
    owner_ = &xx;
    pageId_ = xx.Id();
}

bool PlotModView::RetrieveVisdefList(MvRequest& dropRequest, MvRequest& vdRequestList)
{
    // Return if this is not a visdef
    Cached verb = dropRequest.getVerb();
    if (!ObjectList::IsVisDef(verb))
        return false;

    // Get all visdefs
    vdRequestList.rewind();
    if (vdRequestList)
        vdRequestList = empty_request(nullptr);  // Clean visdef list

    while (dropRequest) {
        // Retrieve visdefs
        verb = dropRequest.getVerb();
        dropRequest.print();
        if (ObjectList::IsVisDef(verb)) {
            vdRequestList += dropRequest.justOneRequest();
            dropRequest.advance();
        }
        else
            break;
    }

    return true;
}

void PlotModView::contentsRequest(MvRequest& dropRequest)
{
    // Process the drop
    MvIconList duList;
    while (dropRequest) {
        // VisDefs are processed in one single goal. This is because the processing
        // of a single or a group of visdefs is different. It is assumed that
        // visdefs always come after a dataunit (if it exists).
        MvRequest vdRequestList;
        if (this->RetrieveVisdefList(dropRequest, vdRequestList)) {
            Owner().InsertVisDef(vdRequestList, duList);

            // Clear dataUnitList variable to avoid a possible visdef in the next
            // iteraction to be associate to an old dataUnit.
            if (!duList.empty())
                duList.clear();

            if (!dropRequest)  // no more drops
                break;
        }

        MvRequest request = dropRequest.justOneRequest();
        Cached verb = request.getVerb();

        // Update view (if applicable)
        if (ObjectList::IsView(verb))
            Owner().UpdateView(request);
        else
            Owner().InsertCommonIcons(request);

        dropRequest.advance();
    }
}
