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

 Copyright 2014 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 "MvIconLanguage.h"

#include "Path.h"
#include "MvIconClassCore.h"
#include "MvIconParameter.h"
#include "MvLog.h"

using Map = std::map<std::string, MvIconLanguage*>;
static Map languages;

static std::map<std::string, request*> lang_cache;
static std::map<std::string, rule*> rule_cache;

MvIconLanguage::MvIconLanguage(const MvIconClassCore* c) :
    inited_(false),
    lang_(0),
    rule_(0),
    flags_(0),
    class_(c)
{
    languages[c->name()] = this;
    if (languages.size() == 1)
        expand_flags(EXPAND_2ND_NAME);
}

MvIconLanguage::~MvIconLanguage()
{
    // Not called
    free_all_requests(lang_);
}
const char* MvIconLanguage::getInfo() const
{
    return lang_->info ? lang_->info : "";
}

const char* MvIconLanguage::getKind() const
{
    return lang_->kind;
}

MvIconLanguage& MvIconLanguage::find(const MvIconClassCore* c)
{
    auto j = languages.find(c->name());
    if (j != languages.end())
        return *(*j).second;
    return *(new MvIconLanguage(c));
}

void MvIconLanguage::init()
{
    if (inited_)
        return;

    inited_ = true;

    std::string langs = class_->definitionFile().str();
    auto j            = lang_cache.find(langs);
    if (j != lang_cache.end())
        lang_ = (*j).second;
    else {
        //std::cout << "read " << langs << std::endl;
        lang_             = read_language_file(langs.c_str());
        lang_cache[langs] = lang_;
    }

    std::string rules = class_->rulesFile().str();
    auto k            = rule_cache.find(rules);
    if (k != rule_cache.end())
        rule_ = (*k).second;
    else {
        //std::cout << "read " << rules << std::endl;
        rule_             = read_check_file(rules.c_str());
        rule_cache[rules] = rule_;
    }

    flags_ = class_->expandFlags();

    // Position to the right request
    while (lang_ && !(class_->name() == lang_->name))
        lang_ = lang_->next;

    if (lang_ == 0) {
        lang_ = empty_request(0);
    }

    // Create the Parameters

    parameter* p = lang_ ? lang_->params : 0;
    while (p) {
        params_.push_back(new MvIconParameter(*this, p));
        p = p->next;
    }
}


Request MvIconLanguage::expand(const Request& r, long flags, bool ruleFlag)
{
    Request result = r;

    init();

    if (lang_ == 0)
        return result;

    reset_language(lang_);
    long save = expand_flags(flags);
    request* a;
    if (ruleFlag)
        a = expand_all_requests(lang_, rule_, result);
    else
        a = expand_all_requests(lang_, 0, result);

    expand_flags(save);

    if (a == 0)
        return result;

    return MvRequest(a, false);
}

Request MvIconLanguage::expand(const Request& r)
{
    return expand(r, flags_);
}


std::vector<std::string> MvIconLanguage::interfaces(const char* name)
{
    init();

    // Need something a little bit more "C++"

    std::vector<std::string> result;
    parameter* p = lang_->params;
    while (p) {
        if (p->interface) {
            const char* q = get_value(p->interface, "interface", 0);
            if (q && strcmp(q, name) == 0)
                result.push_back(p->name);
        }
        p = p->next;
    }

    return result;
}

Request MvIconLanguage::interfaceRequest(const char* name)
{
    init();
    parameter* p = lang_->params;
    while (p) {
        if (p->interface) {
            if (p->name && strcmp(p->name, name) == 0) {
                return Request(p->interface);
            }
        }

        p = p->next;
    }

    return Request();
}

void MvIconLanguage::scan(MvIconLanguageScanner& s)
{
    init();
    for (auto& param : params_)
        s.next(*param);
}

static value* find_values(const request* r, const char* parname)
{
    if (r) {
        parameter* p = r->params;
        while (p) {
            if (strcmp(p->name, parname) == 0)
                return p->values;
            p = p->next;
        }
    }
    return nullptr;
}

MvIconLanguage::MvIconLanguage(const MvIconClassCore* c, const Request& def) :
    inited_(true),
    rule_(0),
    class_(c)
{
    const char *name, *ui;
    parameter *param, *lastparam = nullptr;
    value* val = nullptr;

    const request* r = def;

    lang_                = empty_request(c->name().c_str());
    languages[c->name()] = this;

    while (r) {
        name = get_value(r, "name", 0);
        if (!name)
            break;
        param                 = new_parameter(strcache(name), nullptr);
        ui                    = r->name;
        val                   = find_values(r, "values");
        param->values         = clone_all_values(val);
        val                   = find_values(r, "default");
        param->default_values = clone_all_values(val);
        param->current_values = clone_all_values(val);
        param->interface      = clone_one_request(r);

        set_value(param->interface, "interface", ui);
        strfree(param->interface->name);
        param->interface->name = strcache(name);

        if (lastparam)
            lastparam->next = param;
        else
            lang_->params = param;
        lastparam = param;

        r = r->next;
    }
    flags_ = class_->expandFlags();


    // Position to the right request
    while (lang_ && !(class_->name() == lang_->name))
        lang_ = lang_->next;

    // Create the Parameters

    parameter* p = lang_ ? lang_->params : 0;
    while (p) {
        params_.push_back(new MvIconParameter(*this, p));
        p = p->next;
    }
}
