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

//  Metview runs this MvExamineManager when Metview session is started
//  with flag '-e' plus one or two parameters (first is the data type -
//  it is optional, the second is the path to the input file).
//
//  Input parameters are used for creating a suitable Metview request,
//  which is then sent to the right examiner using a MvServiceTask
//  object.
//
//  Data type is converted to capital letters, thus e.g. for type GRIB
//  values 'GRIB', 'grib' and 'Grib' are acceptable.
//
//  This code is heavily based on example file src/libMetview/Demo.cc.
//

#include "Metview.h"
#include "MvServiceTask.h"
#include "MvScanFileType.h"
#include "MvMiscellaneous.h"
#include "Path.h"

#include <QApplication>
#include <QMessageBox>
#include <vector>
#include <algorithm>
#include <assert.h>

class MvExamineManager : public MvApplication, public MvClient
{
public:
    MvExamineManager(int argc, char** argv);

private:
    void endOfTask(MvTask* from) override;
    void progress(const char*) override;
    void runExaminer(const std::string& fileName, const std::string& dataType);
    void formatDataType(std::string& dataType) const;
    bool checkDataType(const std::string& dataType, bool exitOnInvalid);
    void dataTypeErrorExit(const std::string& dataType);
    bool checkInputFile(const std::string& fileName, bool exitOnInvalid);
    void inputFileErrorExit(const std::string& fileName);
    void showErrorAndExit(const std::string& msg, bool printUsage);

    int argc_{0};
    char** argv_;
    std::vector<std::string> supportedTypes_{"GRIB", "BUFR", "GEOPOINTS", "NETCDF", "ODB"};
};


void MvExamineManager::endOfTask(MvTask* from)
{
    marslog(LOG_DBUG, "Got reply from %s: err=%d\n", from->taskName(), from->getError());
    exit(0);
}

//TODO: do we need it: run is blocking call!!!
void MvExamineManager::progress(const char* p)
{
    marslog(LOG_INFO, "Got progress: %s", p);
}

void MvExamineManager::runExaminer(const std::string& fileName, const std::string& dataType)
{
    MvRequest mvReq(dataType.c_str());
    mvReq.setValue("PATH", fileName.c_str());
    mvReq("_ACTION")   = "examine";
    mvReq("_WAIT_APP") = "1";
    mvReq.print();
    MvTask* task = new MvServiceTask(this, "UiAppManager", mvReq);
    task->run();
}

void MvExamineManager::showErrorAndExit(const std::string& msg, bool printUsage)
{
    marslog(LOG_EROR, "%s", msg.c_str());
    {
        QApplication app(argc_, argv_);
        std::string s = "<b>Error</b>: " + msg;
        if (printUsage) {
            s += "<br><br><b>Usage</b>: metview -e [grib|bufr|geopoints|netcdf|odb] file_path";
        }
        QMessageBox::critical(nullptr, "Metview Data Examiner",
                              QString::fromStdString(s));
    }
    exit(13);
}

void MvExamineManager::formatDataType(std::string& dataType) const
{
    metview::toUpper(dataType);
    if (dataType == "ODB_DB") {
        dataType = "ODB";
    }
}

bool MvExamineManager::checkDataType(const std::string& dataType, bool exitOnInvalid)
{
    if (std::find(supportedTypes_.begin(), supportedTypes_.end(), dataType) ==
        supportedTypes_.end()) {
        if (exitOnInvalid) {
            dataTypeErrorExit(dataType);
        }
        return false;
    }
    return true;
}

void MvExamineManager::dataTypeErrorExit(const std::string& dataType)
{
    std::string typeStr;
    for (std::size_t i = 0; i < supportedTypes_.size(); ++i) {
        std::string s = supportedTypes_[i];
        metview::toLower(s);
        typeStr += s;
        if (i < supportedTypes_.size() - 2) {
            typeStr += ", ";
        }
        else if (i == supportedTypes_.size() - 2) {
            typeStr += " and ";
        }
    }
    showErrorAndExit("Invalid type=" + dataType +
                         " is specified! Supported types are: " + typeStr,
                     true);
}

bool MvExamineManager::checkInputFile(const std::string& fileName, bool exitOnInvalid)
{
    Path pp(fileName);
    if (!pp.exists()) {
        if (exitOnInvalid) {
            inputFileErrorExit(fileName);
        }
        return false;
    }
    return true;
}

void MvExamineManager::inputFileErrorExit(const std::string& fileName)
{
    showErrorAndExit("Input file=" + fileName + " cannot be accessed!", true);
}

MvExamineManager::MvExamineManager(int argc, char** argv) :
    MvApplication(argc, argv)
{
    argc_ = argc;
    argv_ = argv;

    if (argc <= 1) {
        showErrorAndExit("No input is specified!", true);
    }

    std::string fileName;
    std::string dataType = std::string(argv[1]);
    formatDataType(dataType);

    if (checkDataType(dataType, false)) {
        if (argc > 2) {
            fileName = std::string(argv[2]);
            checkInputFile(fileName, true);
        }
        else {
            showErrorAndExit("No file_path is specified!", true);
        }
    }
    else {
        fileName = std::string(argv[1]);
        if (!checkInputFile(fileName, false)) {
            if (argc > 2 && strlen(argv[2]) > 0 && argv[2][0] != '-') {
                if (checkInputFile(argv[2], false)) {
                    dataTypeErrorExit(dataType);
                }
                else {
                    inputFileErrorExit(fileName);
                }
            }
            else {
                inputFileErrorExit(fileName);
            }
            assert(0);
        }

        dataType = ScanFileType(fileName.c_str());
        formatDataType(dataType);

        if (!checkDataType(dataType, true)) {
            exit(13);
        }
    }

    assert(dataType.empty() == false);

    runExaminer(fileName, dataType);
}

int main(int argc, char** argv)
{
    MvExamineManager app(argc, argv);
    app.run();
}
