/***************************** 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 "Metview.h"
#include "MvMiscellaneous.h"
#include "MvTmpFile.h"
#include "GribMetaData.h"
#include "BufrMetaData.h"
#include "MvGeoPoints.h"
#include "MvKeyProfile.h"

#include <list>
#include <memory>

class IconDescMaker : public MvService
{
public:
    IconDescMaker(const char* kw);
    ~IconDescMaker() = default;
    void serve(MvRequest&, MvRequest&);
    void getGribDesc(const std::string&, std::string&);
    void getBufrDesc(const std::string&, std::string&);
    void getGeopointsDesc(const std::string&, const std::string& imgPath, std::string&);
    void getCoastlinesDesc(const std::string&, const std::string& imgPath, std::string&);
    void makePreviewImage(const std::string& inPath, const std::string& imgPath);
};

IconDescMaker::IconDescMaker(const char* kw) :
    MvService(kw)
{
}

void IconDescMaker::serve(MvRequest& in, MvRequest& out)
{
    //    std::cout << "IconDescMaker::serve in" << std::endl;
    //    in.print();

    // get verb
    std::string verb;
    if (const char* ch = in.getVerb()) {
        verb = std::string(ch);
    }
    else {
        marslog(LOG_EROR, "No verb specified!");
        exit(13);
    }

    // input path
    std::string inPath;
    if (const char* ch = in("PATH")) {
        inPath = std::string(ch);
    }
    else {
        marslog(LOG_EROR, "No PATH specified!");
        exit(13);
    }

    // outpath
    std::string outPath;
    if (const char* ch = in("OUTPATH")) {
        outPath = std::string(ch);
    }

    out = MvRequest("ICONDESC");
    if (verb == "PDF" || verb == "PSFILE") {
        MvTmpFile ftmp(false);
        std::string typeStr = verb;
        if (typeStr == "PSFILE") {
            typeStr = "ps";
        }
        else {
            metview::toLower(typeStr);
        }

        std::string cmd = "convert " + typeStr + ":\"" + inPath +
                          "\"[0] png:\"" + ftmp.path() + "\"";
        //        std::cout << "cmd=" << cmd << std::endl;
        system(cmd.c_str());
        out("FORMAT")         = "IMAGE";
        out("TMP_IMAGE_PATH") = ftmp.path().c_str();
    }
    else if (verb == "GRIB") {
        std::string txt;
        getGribDesc(inPath, txt);
        out("FORMAT") = "TEXT";
        out("TEXT")   = txt.c_str();
    }
    else if (verb == "BUFR") {
        std::string txt;
        getBufrDesc(inPath, txt);
        out("FORMAT") = "TEXT";
        out("TEXT")   = txt.c_str();
    }
    else if (verb == "GEOPOINTS") {
        MvTmpFile ftmp(false);
        std::string txt;
        getGeopointsDesc(inPath, ftmp.path(), txt);
        out("FORMAT")         = "IMAGE_AND_TEXT";
        out("TEXT")           = txt.c_str();
        out("TMP_IMAGE_PATH") = ftmp.path().c_str();
    }
    else if (verb == "MCOAST" || verb == "GEOVIEW" || verb == "PCOAST" || verb == "MAPVIEW") {
        MvTmpFile ftmp(false);
        makePreviewImage(inPath, ftmp.path());
        out("FORMAT")         = "IMAGE";
        out("TMP_IMAGE_PATH") = ftmp.path().c_str();
    }

    //    std::cout << "IconDescMaker::serve out" << std::endl;
    //    out.print();
}

void IconDescMaker::getGribDesc(const std::string& inPath, std::string& txt)
{
    GribMetaData data;
    data.setFileName(inPath);
    int num = data.computeTotalMessageNum();
    txt     = "message num: " + std::to_string(num);
    if (num > 0) {
        txt += "<div class='title'>first message</div>";
        MvKeyProfile prof("tmp");
        std::vector<std::string> keyNames = {
            "shortName", "units", "date", "time", "step",
            "validityDate", "validityTime", "level", "typeOfLevel",
            "number", "gridType", "iDirectionIncrementInDegrees",
            "jDirectionIncrementInDegrees", "N", "numberOfDataPoints",
            "numberOfMissing"};

        std::vector<std::string> labels = {
            "shortName", "units", "date", "time", "step",
            "validityDate", "validityTime", "level", "typeOfLevel",
            "number", "gridType", "dx",
            "dx", "N", "pointNum",
            "missingNum"};

        for (auto name : keyNames) {
            MvKey* key = prof.addKey();
            key->setName(name);
        }
        data.getKeyProfileForFirstMessage(&prof);
        if (prof.valueNum() == 1) {
            txt += "<table width=\"100%\">";
            for (size_t i = 0; i < prof.size(); i++) {
                std::string val = prof.at(i)->valueAsString(0);
                if (!val.empty() && val != "N/A" && val != "MISSING" &&
                    !(prof.at(i)->name() == "number" && val == "0")) {
                    txt += "<tr><td class=\"key\">" + labels[i] + ": </td><td>" +
                           val + "</td></tr>";
                }
            }
            txt += "</table>";
        }
    }
}

void IconDescMaker::getBufrDesc(const std::string& inPath, std::string& txt)
{
    BufrMetaData data;
    data.setFileName(inPath);
    int num = data.computeTotalMessageNum();
    txt     = "message num: " + std::to_string(num);
    if (num > 0) {
        txt += "<div class='title'>first message</div>";
        MvKeyProfile prof("tmp");
        std::vector<std::string> keyNames = {
            "dataCategory", "dataSubCategory", "bufrHeaderCentre",
            "masterTablesVersionNumber", "localTablesVersionNumber",
            "numberOfSubsets", "section1Flags",
            "typicalDate", "typicalTime", "ident"};

        std::vector<std::string> labels = {
            "type", "subType", "centre",
            "masterTable", "localTable",
            "subsets", "hasLocalSection",
            "typicalDate", "typicalTime", "ident"};

        for (auto name : keyNames) {
            MvKey* key = prof.addKey();
            key->setName(name);
        }
        data.getKeyProfileForFirstMessage(&prof);
        if (prof.valueNum() == 1) {
            txt += "<table width=\"100%\">";
            for (size_t i = 0; i < prof.size(); i++) {
                std::string val = prof.at(i)->valueAsString(0);
                if (!val.empty() && val != "N/A" && val != "MISSING" &&
                    !(prof.at(i)->name() == "number" && val == "0")) {
                    txt += "<tr><td class=\"key\">" + labels[i] + ": </td><td>" +
                           val + "</td></tr>";
                }
            }
            txt += "</table>";
        }
    }
}

void IconDescMaker::getGeopointsDesc(const std::string& inPath, const std::string& imgPath, std::string& txt)
{
    MvGeoPoints data(inPath.c_str(), 1);
    txt = "<table width=\"100%\">";
    txt += "<tr><td>format:</td><td>" + data.sFormat() + "</td></tr>";
    txt += "<tr><td>point num:</td><td>" + std::to_string(data.count()) + "</td></tr>";
    txt += "</table>";

    makePreviewImage(inPath, imgPath);
}

void IconDescMaker::makePreviewImage(const std::string& inPath, const std::string& imgPath)
{
    //Generate a preview image with a macro
    std::string previewMacro;
    char* mvbin = getenv("METVIEW_BIN");
    if (mvbin == nullptr) {
        marslog(LOG_EROR, "No METVIEW_BIN env variable is defined. Cannot locate mv_preview.mv macro!");
        setError(13);
        return;
    }
    else {
        previewMacro = std::string(mvbin) + "/mv_preview.mv";
    }

    std::vector<std::string> param;

    //The first param indicates that we are not in test mode
    param.push_back(inPath);
    param.push_back(imgPath);

    //Build request to be sent to Macro
    MvRequest req("MACRO");
    req("PATH")    = previewMacro.c_str();
    req("_CLASS")  = "MACRO";
    req("_ACTION") = "execute";
    // req("_REPLY")         = processName.c_str();
    req("_EXTENDMESSAGE") = "1";

    //Define argument list for the macro!
    for (auto& it : param) {
        req.addValue("_ARGUMENTS", it.c_str());
    }

    //Run macro
    //    marslog(LOG_INFO, "Execute macro: %s", previewMacro.c_str());
    int err = 0;
    MvApplication::waitService("macro", req, err);
}

int main(int argc, char** argv)
{
    MvApplication theApp(argc, argv);

    std::vector<std::string> verbs = {"PDF", "PSFILE", "GRIB",
                                      "GEOPOINTS", "BUFR", "GEOVIEW", "MCOAST",
                                      "MAPVIEW", "PCOAST"};

    //we use a list here instead of a vector to avoid using the
    //copy constructor in emplace_back(). MvService is non-copiable!
    using IconDescMaker_ptr = std::shared_ptr<IconDescMaker>;
    std::list<IconDescMaker_ptr> svcList;
    for (auto v : verbs) {
        svcList.emplace_back(std::make_shared<IconDescMaker>(v.c_str()));
    }

    theApp.run();
}
