/***************************** LICENSE START ***********************************

 Copyright 2020 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 "MvRequest.h"
#include "MvProtocol.h"
#include "MvApplication.h"
#include "PmContext.h"
#include "MagPlusBatchService.h"
#include "PlotModBatch.h"
#include "Root.h"

class uPlotBatchApp;

class uPlotBatchTransaction : public MvTransaction
{
public:
    uPlotBatchTransaction(uPlotBatchApp* app, const std::string& outReqPath) :
        MvTransaction((const char*)nullptr),
        app_(app),
        outReqPath_(outReqPath) {}

    uPlotBatchTransaction(uPlotBatchTransaction* o) :
        MvTransaction(o),
        app_(o->app_),
        outReqPath_(o->outReqPath_) {}

    MvTransaction* cloneSelf() override { return new uPlotBatchTransaction(this); }

    void callback(MvRequest& in) override;

private:
    uPlotBatchApp* app_;
    std::string outReqPath_;
};


class uPlotBatchApp : public MvApplication
{
public:
    uPlotBatchApp(int& ac, char** av, const char* name, const std::string&);
    ~uPlotBatchApp() override = default;

    uPlotBatchApp(const uPlotBatchApp&) = delete;
    uPlotBatchApp& operator=(const uPlotBatchApp&) = delete;

    void serve(MvProtocol&, MvRequest&);
};


void uPlotBatchTransaction::callback(MvRequest& in)
{
    app_->serve(*this, in);

    MvRequest out;
    if (getError()) {
        out.setVerb("ERROR");

        // Get icon name(s) involved on the operation
        //        if ((const char*)in("ICON_NAME")) {
        //            int cnt = in.iterInit("ICON_NAME");
        //            for (int i = 0; i < cnt; i++) {
        //                const char* iconChName;
        //                in.iterGetNextValue(iconChName);
        //                if (iconChName) {
        //                    if (i == 0)
        //                        out.setValue("ICON_NAME", iconChName);
        //                    else
        //                        out.addValue("ICON_NAME", iconChName);
        //                }
        //            }
        //        } else if ((const char*)in("PATH")) {
        //            const char* iconChName = mbasename((const char*)in("PATH"));
        //            out.setValue("ICON_NAME", iconChName);
        //        }

        // Get error message(s)
        const char* msg = nullptr;
        int n = 0;
        while ((msg = getMessage(n++))) {
            if (n == 1)
                out.setValue("MESSAGE", msg);
            else
                out.addValue("MESSAGE", msg);
        }
    }

    if (out && !outReqPath_.empty()) {
        out.save(outReqPath_.c_str());
    }

    sendReply(out);

    // the batch task finished/failed, so we stop the mars event loop and exit normally!
    app_->destroyService();
    exit(0);
}

uPlotBatchApp::uPlotBatchApp(int& ac, char** av, const char* name, const std::string& outReqPath) :
    MvApplication(ac, av, name)
{
    // Create the object for mars communication. It will handle incoming reuests and
    // call the serve method
    new uPlotBatchTransaction(this, outReqPath);

    Root::Create();

    auto* batch = new MagPlusBatchService();
    MagPlusService::Instance(batch);

    auto* pm = new PlotModBatch();
    PlotMod::Instance(pm);
}

void uPlotBatchApp::serve(MvProtocol& proto, MvRequest& inRequest)
{
    // Create a new context, based on the input Request
    auto* context = new PmContext(proto, inRequest);

    // Increment the reference counting
    context->Attach();

    // It is a new call, so it first needs to clean the previous structure
    // Root::Instance().Clean();

    // Execute the context
    context->Execute();
}

int main(int argc, char** argv)
{
    if (argc < 2) {
        marslog(LOG_EROR, "No arguments are specified");
        exit(1);
    }

    std::string appName = MvApplication::buildAppName("uPlotBatchApp");

    // Read input request
    MvRequest in;
    in.read(argv[1], false, true);

    std::string outPath;
    if (const char* outPathCh = in("_OUT_REQ_FILE")) {
        outPath = std::string(outPathCh);
    }
    else {
        marslog(LOG_EROR, "No output request specified via _OUT_REQ_FILE!");
        exit(1);
    }

    if (!in) {
        std::string msg = "No request could be read from request file=" + std::string(argv[1]);
        marslog(LOG_EROR, msg.c_str());
        MvApplication::saveErrorAsRequest(outPath, appName + " - " + msg);
        exit(1);
    }

    //    marslog(LOG_INFO,"Request:");
    //    in.print();

    // skip UPLOT_MANAGER request
    if (strcmp(in.getVerb(), "UPLOT_MANAGER") == 0) {
        in.advance();
    }

    if (!in) {
        std::string msg = "No request could be read from request file=" + std::string(argv[1]);
        marslog(LOG_EROR, msg.c_str());
        MvApplication::saveErrorAsRequest(outPath, appName + " - " + msg);
        exit(1);
    }

    // Create application. The appname must be unique!
    uPlotBatchApp app(argc, argv, appName.c_str(), outPath);

    // Calls itself with the input request
    MvApplication::callService(appName.c_str(), in, nullptr);

    app.run();
}
