/***************************** 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 "mars_client_config.h"
#include <Metview.h>
#include <stdarg.h>

svc* MvApplication::service_ = nullptr;
MvApplication* MvApplication::app_ = nullptr;
int KeepAlive::count_ = 0;
static const char* cPreferencesFile = "General";

// This class is used by method MvApplication::wantPreferences().
class MvPreferenceMessage : public MvMessage
{
    MvApplication* appl;

public:
    MvPreferenceMessage(const char* name, MvApplication* a) :
        MvMessage(name), appl(a) {}
    virtual void callback(MvRequest& r) { appl->newPreferences(r); }
};

MvApplication::MvApplication(int& argc, char** argv, const char* name, void* aparg, int nopt, option* opt) :
    MvAbstractApplication(name)
{
    app_ = this;

    marsinit(&argc, argv, aparg, nopt, opt);
    service_ = create_service(name ? name : progname());
    MvServiceFactory::installServices();
}

boolean MvApplication::stopAll(const char* fmt, ...)
{
    char* p = getenv("METVIEW_MODE");

    if (!p)
        return false;

    if (strcmp(p, "batch") != 0)
        return false;

    va_list list;
    char buf[1024];

    va_start(list, fmt);
    vsprintf(buf, fmt, list);
    va_end(list);

    stop_all(service_, buf, 1);

    return true;
}

void MvApplication::setKeepAlive(boolean on)
{
    keep_alive(service_, on ? 1 : 0);
}

void MvApplication::setMaximum(int n)
{
    set_maximum(service_, n);
}

void MvApplication::setExitTimeout(int minutes)
{
    exit_timeout(service_, minutes);
}

void MvApplication::run()
{
    /* if (!mars.nofork) setExitTimeout(1); // 1 minute */
    service_run(service_);
}

void MvApplication::callService(const char* s, const MvRequest& r, void* d)
{
    /*
    if(ProcessReplies)
        while(service_ready(Service))
        {
            marslog(LOG_DBUG,"Processing async replies...");
            process_service(Service);
        }
*/
    call_service(service_, s, r, (long)d);
}

MvRequest MvApplication::waitService(const char* s, const MvRequest& r, int& err)
{
    err = 0;
    return MvRequest(wait_service(service_, (char*)s, r, &err), false);
}

void MvApplication::callFunction(const char* s, const MvRequest& r, void* d)
{
    call_function(service_, s, r, (long)d);
}

void MvApplication::destroyService()
{
    destroy_service(service_);
}

void MvApplication::sendMessage(const MvRequest& r)
{
    send_message(service_, r);
}

void MvApplication::sendMessage(const std::string& msg)
{
    char c_msg[1024];
    strncpy(c_msg, msg.c_str(), 1023);

    // copy the message into a C string and send it to the service
    if (service_ != nullptr && service_->id != nullptr) {
        set_svc_msg(service_->id, c_msg);
    }
}

void MvApplication::recordOn(boolean on)
{
    recording(service_, on);
}

void MvApplication::setFork(boolean on)
{
    mars.nofork = !on;
}

void MvApplication::notifyIconCreation(const char* icon_name, const char* icon_class)
{
    request* u = empty_request("STATUS");
    set_value(u, "NAME", "%s", icon_name);
    set_value(u, "CLASS", "%s", icon_class);
    set_value(u, "STATUS", "CREATED");
    send_message(service_, u);
    free_all_requests(u);
}

void MvApplication::notifyIconModified(const char* icon_name, const char* icon_class)
{
    request* u = empty_request("STATUS");
    set_value(u, "NAME", "%s", icon_name);
    set_value(u, "CLASS", "%s", icon_class);
    set_value(u, "STATUS", "MODIFIED");
    send_message(service_, u);
    free_all_requests(u);
}

MvRequest MvApplication::getPreferences(const char* name)
{
    const char* prefs_name = (name == nullptr) ? cPreferencesFile : name;
    char buf[1024];
    sprintf(buf, "%s/System/Preferences/%s",
            getenv("METVIEW_USER_DIRECTORY"), prefs_name);

    return read_request_file(buf);
}

MvRequest MvApplication::getExpandedPreferences(const char* name)
{
    const char* prefs_name = (name == nullptr) ? cPreferencesFile : name;
    // MvRequest req=get_preferences(Service,prefs_name);
    MvRequest req = getPreferences(prefs_name);

    char* shareDir = getenv("METVIEW_DIR_SHARE");
    if (!shareDir)
        return req;

    std::string path(shareDir);
    path.append("/etc/");

    std::string defFileName = path + "ConfigDef";
    std::string rulesFileName = path + "ConfigRules";

    MvLanguage langMetview(defFileName.c_str(),
                           rulesFileName.c_str(),
                           EXPAND_DEFAULTS);

    return langMetview.expandOne(req);
}

void MvApplication::setPreferences(const MvRequest& req, const char* name)
{
    const char* prefs_name = (name == nullptr) ? cPreferencesFile : name;
    char buf[1024];
    sprintf(buf, "%s/System/Preferences/%s",
            getenv("METVIEW_USER_DIRECTORY"), prefs_name);

    req.save(buf);
}

void MvApplication::wantPreferences(const char* name)
{
    new MvPreferenceMessage(name ? name : "GENERAL_PREFERENCES", this);
}

void MvApplication::addInputCallback(FILE* f, inputproc p, void* data)
{
    add_input_callback(service_, f, p, data);
}

MvRequest MvApplication::poolFetch(const char* icon_name, const char* icon_class)
{
    return pool_fetch(service_, icon_name, icon_class);
}
void MvApplication::poolStore(const char* icon_name, const char* icon_class,
                              const MvRequest& r)
{
    pool_store(service_, icon_name, icon_class, r);
}

void MvApplication::poolLink(const char* name1, const char* name2)
{
    pool_link(service_, name1, name2);
}

void MvApplication::saveErrorAsRequest(const std::string& outPath, const std::string& msg)
{
    MvRequest out("ERROR");
    out("MESSAGE") = msg.c_str();
    out.save(outPath.c_str());
}

std::string MvApplication::buildAppName(const std::string& name)
{
    return name + "@" + std::to_string(getpid());
}

void MvApplication::writeToLog(const std::string& msg, MvLogLevel level)
{
    // TODO: figure out when sendMessage is to use
    //    sendMessage(msg);

    // TODO: break multiline text into lines for marslog
    if (level == MvLogLevel::INFO) {
        marslog(LOG_INFO, msg.c_str());
    }
    else if (level == MvLogLevel::WARN) {
        marslog(LOG_WARN, msg.c_str());
    }
    else if (level == MvLogLevel::ERROR) {
        marslog(LOG_EROR, msg.c_str());
    }
    else if (level == MvLogLevel::DBG) {
        marslog(LOG_INFO, msg.c_str());
        //        marslog(LOG_DBUG, msg.c_str());
    }
    // marslog(LOG_INFO | LOG_PERR, msg.c_str());
}

// The module will exit with an error code
void MvApplication::exitWithError()
{
    if (service_ != nullptr)
        set_svc_err(service_->id, 1);
}

MvApplication::SaveAsFolderTarget MvApplication::saveAsFolderTarget()
{
    MvRequest r = MvApplication::getPreferences();
    if (const char* ch = (const char*)r("DEFAULT_FOLDER_FOR_FILE_DIALOGS")) {
        return (strcmp(ch, "CURRENT") == 0) ? CurrentFoldertarget : PreviousFolderTarget;
    }
    return CurrentFoldertarget;
}
