/***************************** 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 <MvApplication.h>
#include <MvRequestUtil.hpp>
#include "MvStopWatch.h"
#include "PlotMod.h"
#include "PlotModConst.h"
#include "Root.h"

#include <assert.h>

// Methods for the PlotMod class
//
// --- METHOD:  Instance
//
// Methods to provide access to the singleton
//              PlotMod class

PlotMod* PlotMod::plotModInstance_ = 0;

void PlotMod::Instance(PlotMod* pm)
{
    plotModInstance_ = pm;
}

PlotMod&
PlotMod::Instance()
{
    assert(plotModInstance_ != 0);
    return *plotModInstance_;
}

PlotMod::PlotMod() :
    exitOnErrorMessage_(true),
    popupErrorMessage_(true),
    calledFromMacro_(false),
    calledFirstFromMacro_(false)
{
    watch_ = 0;
}

bool PlotMod::IsWindow(const char* driver)
{
    return !strcmp(MVSCREEN_DRIVER, driver);
}

bool PlotMod::CalledFromMacro(MvRequest& req)
{
    // Check if the request comes from a Macro
    int calledFromMacro = req("_CALLED_FROM_MACRO");

    // Only updates the plot environment if the request
    // comes from Macro. Otherwise, keeps the current status.
    if (calledFromMacro) {
        calledFromMacro_      = calledFromMacro;
        calledFirstFromMacro_ = calledFromMacro;
    }

    return calledFromMacro;
}

MvRequest
PlotMod::OutputFormat()
{
    if (!outFormat_)
        outFormat_ = MakeDefaultOutputFormat();

    return outFormat_;
}

void PlotMod::OutputFormat(MvRequest& req)
{
    outFormat_ = req;
    if ((const char*)outFormat_("DESTINATION")) {
        plotDestination_ = (const char*)outFormat_("DESTINATION");
        if (plotDestination_ == MVPRINTER && (const char*)outFormat_("PRINTER_NAME"))
            printerName_ = (const char*)outFormat_("PRINTER_NAME");
    }
    else
        plotDestination_ = MVSCREEN;
}

MvRequest
PlotMod::MakeDefaultOutputFormat()
{
    MvRequest req("PRINTER_MANAGER");
    MvRequest gldriverReq(MVSCREEN_DRIVER);
    gldriverReq("DESTINATION") = MVSCREEN;
    req("OUTPUT_DEVICES")      = gldriverReq;

    return req;
}

MvRequest
PlotMod::OutputFormatInfo()
{
    MvRequest info("OUTPUTINFO");
    info("DESTINATION")   = plotDestination_.c_str();
    info("PRINTER_NAME")  = printerName_.c_str();
    info("NUMBER_COPIES") = (int)outFormat_("NUMBER_COPIES");
    MvRequest subReq      = outFormat_.getSubrequest("OUTPUT_DEVICES");
    if (subReq) {
        if ((const char*)subReq("OUTPUT_NAME"))
            info("FILE_NAME") = subReq("OUTPUT_NAME");
        else if ((const char*)subReq("OUTPUT_FULLNAME"))
            info("FILE_NAME") = subReq("OUTPUT_FULLNAME");
        else
            info("FILE_NAME") = "defaultFilename";  //default filename
    }

    return info;
}

#if 0
bool
PlotMod::CheckSetWindowOutputFormat( MvRequest& req )
{
	// Default value;
	isWindow_ = true;

	if ( IsParameterSet(req,"OUTPUT_DEVICES") )
	{
		// Output device definition was set by the user
		// Try to find the screen driver
		MvRequest outDev = req("OUTPUT_DEVICES");
		while ( outDev )
		{
			if ( strcmp(outDev.getVerb(),MVSCREEN_DRIVER) == 0 )
				return true;  //screen driver found

			outDev.advance();
		}

		// Screen driver not found
		isWindow_ = false;
		return false;
	}

	return true;
}
#endif


// ----------------------------------------------------------------------------
// PlotMod::infoMessage, warningMessage, errorMessage
// Receives message callbacks from Magics
// ----------------------------------------------------------------------------

void PlotMod::infoMessage(const string& msg)
{
    printMessage("Info: " + msg, false);
}

void PlotMod::warningMessage(const string& msg, bool onScreen)
{
    string smsg = "Warning: " + msg;
    printMessage(smsg, true);

    if (popupErrorMessage() && onScreen)
        UserMessage(smsg);
}

void PlotMod::errorMessage(const string& msg)
{
    string text = "Error: " + msg;

    // intercept the 'spherical harmonics' message and add our own information
    if (msg.find("Grib Decoder") != string::npos &&
        msg.find("Representation [sh]") != string::npos) {
        text +=
            "\nIf you wish to plot this field, you should first convert it to a grid. "
            "If the data comes from a Mars Retrieval icon, you should set its GRID "
            "parameter; if it is from a GRIB file, you should use the GRIB Filter icon "
            "and set its GRID parameter.";
    }

    // Send message to the console
    printMessage(text, true);

    // Popup an error message window
    if (popupErrorMessage())
        UserMessage(text);

    // Exit module
    if (exitOnErrorMessage())
        exitWithError();
}

// -----------------------------------------------------------------
// General handling of info/warning/error messages.
// Combine functions: infoMessage, warningMessage and errorMessage.
// It can be used to avoid testing which of the three above
// functions should be called.
// -----------------------------------------------------------------
void PlotMod::MetviewError(const string& message, const string& errorlevel)
{
    string fullMsg;
    bool priority = false;
    bool exit     = false;

    // Check type of message
    if (errorlevel.length()) {
        // Check if errorlevel is to be added to the output message
        std::size_t ipos = message.find(" ");
        string sfirst    = (ipos != std::string::npos) ? message.substr(0, ipos) : message;
        if (sfirst != errorlevel)
            fullMsg = errorlevel + ": ";

        // If infoMessage then just print the message;
        // otherwise, set the flags accordingly
        if (errorlevel == "Warn" || errorlevel == "WARNING" || errorlevel == "Warning")
            priority = true;
        else if (errorlevel == "Error" || errorlevel == "ERROR") {
            priority = true;
            exit     = true;
        }
    }

    fullMsg += message;
    printMessage(fullMsg, priority);

    if (priority)
        UserMessage(fullMsg);

    if (exit)
        exitWithError();
}

// The module will exit with an error code
void PlotMod::exitWithError()
{
    // FAMI20180916: forces uPlot to exit, if running in interactive
    // mode (because it was called from uPlotManager as a system
    // command). The ideal would be to remove the last request (the
    // one that is causing this error) from the internal MvRequest
    // and keep uPlot alive.
    // If running in batch, send an error message to the caller.

    svc* service = MvApplication::instance().getService();
    if (service != NULL)
        set_svc_err(service->id, 1);

    // This is not ideal unless we find a way to communicate
    // back to Metview
    if (IsInteractive()) {
        // FAMI20181120 Let MvTemplates.h to kill uPlot at the end of the
        // process. Otherwise, any error message will not be sent forward.
        //
        // Clean structures and auxiliary files/links
        //      Root::Instance().Clean();
        //      exit(0);
    }
}

// Timming functions
//Starts and names the session
void PlotMod::watchStart(const char* txt)
{
    if (watch_) {
        marslog(LOG_WARN, "stopwatch_start - watch already running, replace old watch!");
        delete watch_;
    }
    watch_ = new MvStopWatch(txt);
}

// Prints the laptime since the previous call to
// watchLaptime() or from watchStart()
void PlotMod::watchLaptime(const char* txt)
{
    if (!watch_) {
        marslog(LOG_WARN, "stopwatch_laptime - watch not running, starting now!");
        watch_ = new MvStopWatch("tictac");
    }
    else
        watch_->lapTime(txt);
}

// Stops and prints a report
void PlotMod::watchStop()
{
    if (watch_) {
        delete watch_;
        watch_ = 0;
    }
    else
        marslog(LOG_WARN, "stopwatch_stop - no watch running!");
}
