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

 Copyright 2013 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.h>
#include <ctype.h>
#include <stdio.h>

typedef struct data
{
    boolean serve;
} data;

static option opts[] = {
    {
        "serve",
        NULL,
        "-serve",
        "0",
        t_boolean,
        sizeof(boolean),
        OFFSET(data, serve),
    },
};

static data setup;

const char* reserved[] = {
    "if",
    "then",
    "else",
    "and",
    "not",
    "or",
    "function",
    "return",
    "end",
    "while",
    "global",
    "do",
    "on",
    "for",
    "to",
    "by",
    "in",
    "of",
    "case",
    "otherwise",
    "repeat",
    "until",
    "loop",
    "when",
    "include",
    "extern",
    "tell",
    "task",
    "nil",
    "object",
    "import",
    "export",
    "inline",
};


/* we need the ability to translate when a parameter name is a reserved word */
typedef struct translation
{
    const char* oldName;
    const char* newName;
} translation;

translation python_translations[] = {
    {"class", "class_"}};

translation macro_translations[] = {};

translation* translations = macro_translations;
size_t num_translations = sizeof(macro_translations) / sizeof(translation);


const char* import(FILE* f, const char* icon, const char* className);
static void formula1(FILE* f, const char* ref, math* m, math* p);
static void formula2(FILE* f, math* m);
static int need_case(request* r, parameter* p, value* v);
static int value_need_case(value* w, value* v);
static request* link1(FILE*, request*);
static int get_iconClass(const char* iconPath, char* iconClass);
static const char* low(const char* s, int under, int clean);
static int compare(const char* str1, const char* str2);

static int cvtdates = 0;

static char tab[256] = "";  // tab string or row of spaces
static boolean spaces = false;
static char language_[64] = "macro";  // macro or python
static char prefix_[8] = "";          // empty or "mv."
static char separator_[2] = ":";      // : or =


static const char* translate(const char* from)
{
    int i = 0;

    /* look it up in the translation table */
    for (i = 0; i < num_translations; i++) {
        if (!strcmp(from, translations[i].oldName))
            return translations[i].newName;
    }

    /* if we didn't find the string in the table, return the unchanged input string */
    return from;
}


static void putdate(FILE* f, long date)
{
    if (date > 0)
        fprintf(f, "%ld", date);
    else
        fprintf(f, "today - %ld", -date);
}

static request* find_object(const char* name)
{
    request* u = mars.setup;
    const char* c;

    if (!name)
        return NULL;

    while (u) {
        if (strcmp(u->name, "object") == 0 &&
            (c = get_value(u, "class", 0)) &&
            compare(c, name) == 0)
            return u;
        u = u->next;
    }

    return NULL;
}

static const char* ascii(char a)
{
    static char buf[5];
    if (isspace(a) || a == '_')
        return "_";
    sprintf(buf, "_%02x_", (unsigned char)a);
    return buf;
}

static int compare(const char* str1, const char* str2)
{
    char buf1[256];
    char buf2[256];

    sprintf(buf1, "%s", low(str1, 1, 1));
    sprintf(buf2, "%s", low(str2, 1, 1));

    return strcmp(buf1, buf2);
}

static const char* low(const char* s, int under, int clean)
{
    static char buf[1024];
    int i = 0;
    int c = 0;
    buf[0] = 0;

    if (under && !isalpha(*s)) {
        const char* p = ascii(*s);
        while (*p)
            buf[i++] = *p++;
        s++;
    }

    while (*s) {
        if (*s >= 'A' && *s <= 'Z')
            buf[i++] = *s - 'A' + 'a';
        else {
            c = (unsigned char)(*s);
            if ((under && !isalnum(c)) || (c > 127)) {
                const char* p = ascii(*s);
                while (*p)
                    buf[i++] = *p++;
            }
            else
                buf[i++] = *s;
        }
        s++;
    }

    buf[i] = 0;

    if (clean) {
        for (i = 0; i < NUMBER(reserved); i++)
            if (strcmp(reserved[i], buf) == 0)
                strcat(buf, "_");
    }
    return buf;
}

static void printCannotImport(FILE* f, const char* icon)
{
    fprintf(f, "# Warning: cannot import : %s\n", icon);
    fprintf(f, "# Using the metview function, not available in batch\n\n");
    fprintf(f, "%s = metview(\"%s\")\n\n", low(mbasename(icon), 1, 1), icon);
}

static void importing(FILE* f, const char* icon)
{
    static char* tag = NULL;
    if (!tag)
        tag = getenv("MVIMPORT_TAG");
    if (!tag)
        tag = "# Importing : ";
    fprintf(f, "%s%s\n\n", tag, icon);
}

static boolean import_link(FILE* f, const char* icon, request* r)
{
    request* s = link1(f, r);
    int i, j, c = 0, m, n;

    importing(f, icon);
    fprintf(f, "%s = ", low(mbasename(icon), 1, 1));

    fprintf(f, "[");

    n = count_values(s, "Window");

    for (i = 0; i < n; i++) {
        if (c)
            fprintf(f, ",");
        fprintf(f, "%s", get_value(s, "Window", i));
        c++;
    }

    m = count_values(s, "Visdef");
    n = count_values(s, "File");

    for (i = 0; i < n; i++) {
        if (c)
            fprintf(f, ",");
        fprintf(f, "%s", get_value(s, "File", i));
        c++;
        for (j = 0; j < m; j++) {
            if (c)
                fprintf(f, ",");
            fprintf(f, "%s", get_value(s, "Visdef", j));
            c++;
        }
    }

    n = count_values(s, "Data");

    for (i = 0; i < n; i++) {
        if (c)
            fprintf(f, ",");
        fprintf(f, "%s", get_value(s, "Data", i));
        c++;
        for (j = 0; j < m; j++) {
            if (c)
                fprintf(f, ",");
            fprintf(f, "%s", get_value(s, "Visdef", j));
            c++;
        }
    }


    fprintf(f, "]\n\n");

    free_all_requests(s);
    return true;
}

static boolean import_compute(FILE* f, const char* icon, request* r)
{
    math* m;
    if ((m = compmath(no_quotes(get_value(r, "FORMULA", 0))))) {
        formula1(f, icon, m, m);
        importing(f, icon);
        fprintf(f, "%s = ", low(mbasename(icon), 1, 1));
        formula2(f, m);
        free_math(m);
        fprintf(f, "\n\n");
        return true;
    }
    return false;
}

typedef struct cache
{
    struct cache* next;
    const char* name;
    request* lang;
} cache;

static request* get_language(const char* name)
{
    static cache* langs = NULL;
    cache* c;
    char* obj = strcache("object");
    request* r = mars.setup;
    const char* file = NULL;

    while (r) {
        if (r->name == obj) {
            const char* p = get_value(r, "class", 0);
            if (p == name) {
                file = get_value(r, "definition_file", 0);
                break;
            }
        }
        r = r->next;
    }

    strfree(obj);

    if (!file)
        return NULL;

    c = langs;
    while (c) {
        if (c->name == file)
            break;
        c = c->next;
    }

    if (c == NULL) {
        c = NEW_CLEAR(cache);
        c->next = langs;
        langs = c;
        c->name = strcache(file);
        c->lang = read_language_file(file);
    }

    return c->lang;
}

static int value_need_case(value* w, value* v)
{
    while (w) {
        if (w->ref) {
            if (value_need_case(w->ref, v) == 0)
                return 0;
        }
        else {
            value* o = w->other_names;
            if (w->name == v->name)
                return 0;

            while (o) {
                if (o->name == v->name)
                    return 0;
                o = o->next;
            }
        }
        w = w->next;
    }
    return 1;
}

static int need_case(request* r, parameter* p, value* v)
{
    request* l = get_language(r->name);
    while (l) {
        if (l->name == r->name) {
            parameter* q = l->params;
            while (q) {
                if (q->name == p->name)
                    return value_need_case(q->values, v);
                q = q->next;
            }
        }
        l = l->next;
    }
    return 0;
}

static void import_sub_objects(FILE* f, const char* icon, request* r, request* dot)
{
    parameter* p;
    int n;

    /* Look for references */
    request* l = get_language(r->name);

    while (l && (l->name != r->name))
        l = l->next;

    if (l) {
        p = l->params;
        while (p) {
            const char* i = get_value(p->interface, "interface", 0);
            if (i && (strcmp(i, "icon") == 0)) {
                int i = 0;
                const char* q;
                unset_value(r, "_temp");
                while ((q = get_value(r, p->name, i++))) {
                    q = makepath(mdirname(icon), q);
                    add_value(r, "_temp", ".%s", import(f, q, NULL));
                }
                unset_value(r, p->name);
                i = 0;
                while ((q = get_value(r, "_temp", i++)))
                    add_value(r, p->name, "%s", q);
                unset_value(r, "_temp");
            }

            p = p->next;
        }
    }

    /* Check in dot fil for macroparmas */


    if ((n = count_values(dot, "references"))) {
        int i = 0;
        const char* s;
        while ((s = get_value(dot, "references", i++))) {
            int i = 0;
            const char* q;
            unset_value(r, "_temp");
            while ((q = get_value(r, s, i++))) {
                q = makepath(mdirname(icon), q);
                add_value(r, "_temp", ".%s", import(f, q, NULL));
            }
            unset_value(r, s);
            i = 0;
            while ((q = get_value(r, "_temp", i++)))
                add_value(r, s, "%s", q);
            unset_value(r, "_temp");
        }
    }
}


static void import_values(FILE* f, request* r, parameter* p)
{
    value* v = p->values;
    int flg = v && v->next;
    int dateflg = 0;

    if (strcmp(p->name, "DATE") == 0 && cvtdates)
        dateflg = 1;

    if (flg)
        fprintf(f, "[");

    while (v) {
        /*Handling multi-line text for ODB query */
        if (strcmp(p->name, "ODB_QUERY") == 0 || strcmp(p->name, "ODB_WHERE") == 0) {
            if (strstr(v->name, "\n") == 0) {
                fprintf(f, "\"%s\"", v->name);
            }
            else {
                char* pch;
                int len = strlen(v->name);
                if (len > 0) {
                    char* buff = (char*)calloc(len + 1, sizeof(char));
                    sprintf(buff, "%s", v->name);
                    pch = strtok(buff, "\n");
                    fprintf(f, "\"%s \"", pch);

                    while (pch != NULL) {
                        pch = strtok(NULL, "\n");
                        if (pch != NULL) {
                            fprintf(f, " &\n");
                            fprintf(f, "%s%s%s%s%s\"%s \"", tab, tab, tab, tab, tab, pch);
                        }
                    }

                    free(buff);
                }
                /*fprintf(f,"\n",pch);*/

                /*fprintf(f,"string inline\n");
                fprintf(f,"%s\n",v->name);
                fprintf(f,"%s%s%stend inline", tab, tab, tab);*/
            }
        }
        else if (isdate(v->name))
            fprintf(f, "%s", v->name);
        else if (is_number(v->name))
            if (dateflg)
                putdate(f, atof(v->name));
            else
                fprintf(f, "%s", v->name); /*-- do not convert to number --*/
        else if (v->name[0] == '.')
            fprintf(f, "%s", v->name + 1);
        else if (*v->name == '"')
            fprintf(f, "%s", v->name);
        else if (need_case(r, p, v))
            fprintf(f, "\"%s\"", v->name);
        else
            fprintf(f, "\"%s\"", low(v->name, 0, 0));
        if (v->next)
            fprintf(f, ",");
        v = v->next;
    }

    if (flg)
        fprintf(f, "]");
}

static boolean import_simple_formula(FILE* f, const char* icon, request* r)
{
    request* lang;
    import_sub_objects(f, icon, r, NULL);

    lang = get_language(r->name);
    expand_flags(0);
    r = expand_all_requests(lang, NULL, r);
    if (!r)
        return false;

    {
        parameter* p0 = find_parameter(r, "PARAMETER");
        parameter* p1 = find_parameter(r, "PARAMETER_1");
        parameter* p2 = find_parameter(r, "PARAMETER_2");
        const char* fn = get_value(r, "FUNCTION", 0);
        const char* op = get_value(r, "OPERATOR", 0);

        if (!(p0 || (p1 && p2)))
            return false;
        importing(f, icon);
        fprintf(f, "%s = ", low(mbasename(icon), 1, 1));

        if (fn) {
            fprintf(f, "%s(", fn);
            if (p1 && p2) {
                import_values(f, r, p1);
                fprintf(f, ",");
                import_values(f, r, p2);
            }
            else if (p0)
                import_values(f, r, p0);
            fprintf(f, ")");
        }
        else if (op) {
            import_values(f, r, p1);
            fprintf(f, " %s", op);
            if (op[strlen(op) - 1] != ' ')
                fprintf(f, " ");
            import_values(f, r, p2);
        }
        else
            return false;
        fprintf(f, "\n");
        return true;
    }
    free_all_requests(r);
}

const char* clean(const char* p)
{
    static char buf[1024];
    int quote = 0;
    int i;
    const char* q = p;

    for (i = 0; i < NUMBER(reserved); i++)
        if (strcmp(reserved[i], p) == 0)
            quote++;


    if (*p != '_' && !isalpha(*p))
        quote++;

    while (*q) {
        if (*q != '_' && !isalnum(*q))
            quote++;
        q++;
    }

    if (quote) {
        char c = '\'';
        q = p;
        while (*q)
            if (*q++ == '\'')
                c = '"';

        i = 0;
        buf[i++] = c;
        q = p;
        while (*q) {
            if (*q == c)
                buf[i++] = '\\';
            buf[i++] = *q++;
        }
        buf[i++] = c;
        buf[i++] = 0;
        return buf;
    }
    return p;
}

static boolean import_request(FILE* f, const char* icon, request* r, request* u)
{
    const char* m = NULL;
    parameter* p;

    if (!r) {
        return false;
    }

    if (strncmp(r->name, "SAMPLE_FORMULA", strlen("SAMPLE_FORMULA")) == 0)
        return import_simple_formula(f, icon, r);


    /* Note: TABLE_READER does not have an associated macro in
    ObjecList because we want to over-ride it here. */

    if (strncmp(r->name, "TABLE_READER", strlen("TABLE_READER")) == 0)
        m = "read_table";

    u = u ? u : find_object(r->name);

    if (u != NULL && (m || (m = get_value(u, "macro", 0)))) {
        size_t maxlen = 1;

        import_sub_objects(f, icon, r, u);

        importing(f, icon);
        fprintf(f, "%s = ", low(mbasename(icon), 1, 1));

        fprintf(f, "%s%s(\n", prefix_, m);

        p = r->params;

        /* if using spaces, then we can be more clever in how we align
           the colons after the parameter names; start by finding the length
           of the longest parameter name. */
        if (spaces) {
            while (p) {
                const char* str = clean(low(p->name, 0, 0));
                size_t thislen = strlen(str);
                if (thislen > maxlen)
                    maxlen = thislen;

                p = p->next;
            }
        }


        p = r->params; /* reset p */

        while (p) {
            const char* str = translate(clean(low(p->name, 0, 0)));
            size_t thislen = strlen(str);
            size_t i;
            fprintf(f, "%s%s", tab, str);
            if (spaces) {
                for (i = 0; i < (maxlen - thislen) + 1; i++) {
                    fprintf(f, " ");
                }
                fprintf(f, "%s ", separator_);
            }
            else {
                fprintf(f, "%s%s%s", tab, separator_, tab);
            }
            import_values(f, r, p);

            if (p->next)
                fprintf(f, ",");
            fprintf(f, "\n");
            p = p->next;
        }

        fprintf(f, "%s)\n\n", tab);
        return true;
    }
    return false;
}

static boolean import_all_requests(FILE* f, const char* icon, request* r, request* u)
{
    const char* p;
    if (r->next == NULL)
        return import_request(f, icon, r, u);

    fprintf(f, "%s = []\n",
            low(mbasename(icon), 1, 1));

    while (r) {
        import_request(f, "tmp", r, u);
        fprintf(f, "%s = %s & [tmp]\n",
                low(mbasename(icon), 1, 1),
                low(mbasename(icon), 1, 1));
        r = r->next;
    }
    return true;
}

static boolean import_macro(FILE* f, const char* icon, const char* file)
{
    char line[1024];
    FILE* g = fopen(file, "r");
    importing(f, icon);

    if (g) {
        int c, last = -1;
        fprintf(f, "##### Start of macro %s\n", icon);

        while ((c = fgetc(g)) != -1) {
            last = c;
            fputc(c, f);
        }
        fclose(g);
        if (last != '\n')
            fputc('\n', f);
        fprintf(f, "##### End of macro %s\n", icon);
    }
    else
        fprintf(f, "# Cannot read file %s\n", file);


    return true;
}

static boolean import_file(FILE* f, const char* icon, const char* file)
{
    char line[1024];
    importing(f, icon);
    fprintf(f, "%s = %sread(\"%s\")\n\n", low(mbasename(icon), 1, 1), prefix_, file);
    return true;
}

static boolean import_macroparam(FILE* f, const char* icon, request* r)
{
    char buf[1024];
    char name[1024];
    const char* p;

    strcpy(name, low(mbasename(icon), 1, 1));

    // We emulate the old dot request!
    request* dot = empty_request("USER_INTERFACE");
    set_value(dot, "ICON_CLASS", "MACROPARAM");
    set_value(dot, "macro", "");

    import_request(f, icon, r->next, dot);

    fprintf(f, "# NOTE: You may want to change the execute to visualize\n");
    fprintf(f, "%s = execute(%s)\n\n", name, name);

    sprintf(buf, "%s%s",
            getenv("METVIEW_USER_DIRECTORY"),
            makepath(mdirname(icon), get_value(r, "MACRO", 0)));
    import_macro(f, get_value(r, "MACRO", 0), buf);

    free_all_requests(dot);

    return true;
}

static boolean import_shell(FILE* f, const char* icon, const char* file)
{
    importing(f, icon);
    fprintf(f, "%s = shell(\"%s\")\n\n", low(mbasename(icon), 1, 1), file);
    return true;
}

static boolean import_magml(FILE* f, const char* icon, const char* file)
{
    importing(f, icon);
    fprintf(f, "%s = magml(\"%s\")\n\n", low(mbasename(icon), 1, 1), file);
    return true;
}

static boolean import_note(FILE* f, const char* icon, const char* file)
{
    FILE* g = fopen(file, "r");
    importing(f, icon);

    if (g) {
        int c, last = -1;
        fprintf(f, "%s = string inline\n", low(mbasename(icon), 1, 1));
        while ((c = fgetc(g)) != -1) {
            last = c;
            fputc(c, f);
        }
        if (last != '\n')
            fputc('\n', f);
        fprintf(f, "end inline\n");
        fclose(g);
    }
    else
        fprintf(f, "# Cannot read file %s\n", file);

    return true;
}

boolean import_read(FILE* f, const char* icon, request* r)
{
    /* if only PATH is set, use the read function */
    parameter* p = r->params;
    const char* path = get_value(r, "SOURCE", 0);
    int n = 0;

    while (p) {
        if (*p->name != '_')
            n++;
        p = p->next;
    }

    if (path && (n == 1)) {
        importing(f, icon);
        fprintf(f, "%s = %sread(\"%s\")\n\n", low(mbasename(icon), 1, 1), prefix_, no_quotes(path));
        return true;
    }

    return import_request(f, icon, r, NULL);
}


boolean import_table_reader(FILE* f, const char* icon, request* r)
{
    /* DATA is set, then it will be more efficient to change it to SOURCE */

    const char* data = get_value(r, "DATA", 0);

    if (data != NULL) {
        set_value(r, "TABLE_FILENAME", data);
        unset_value(r, "DATA");
    }


    return import_request(f, icon, r, NULL);
}

boolean import_table_visualiser(FILE* f, const char* icon, request* r)
{
    /* TABLE_DATA is set, then it will be more efficient to change it to SOURCE */

    const char* data = get_value(r, "TABLE_DATA", 0);

    if (data != NULL) {
        set_value(r, "TABLE_FILENAME", data);
        unset_value(r, "TABLE_DATA");
    }


    return import_request(f, icon, r, NULL);
}


boolean import_rttov_visualiser(FILE* f, const char* icon, request* r)
{
    /* DATA is set, then it will be more efficient to change it to SOURCE */

    const char* data = get_value(r, "RTTOV_DATA", 0);

    if (data != NULL) {
        set_value(r, "RTTOV_FILENAME", data);
        unset_value(r, "RTTOV_DATA");
    }


    return import_request(f, icon, r, NULL);
}

boolean import_weather_symbol(FILE* f, const char* icon, const char* className)
{
    char buf[1024];
    request* r = NULL;
    boolean ret = false;
    if (strcmp(className, "WS_COLLECTION") == 0) {
        r = empty_request("WS_COLLECTION");
        set_value(r, "PATH", icon);
        ret = import_request(f, icon, r, NULL);
    }
    else {
        const char* fTmp = marstmp();
        sprintf(buf, "$METVIEW_BIN/MvWsToRequest \"%s\" %s", icon, fTmp);
        if (system(buf) == 0) {
            r = read_request_file(fTmp);
            ret = import_request(f, icon, r, NULL);
        }
    }
    free_all_requests(r);
    return ret;
}

const char* import(FILE* f, const char* name, const char* className)
{
    char buf[1024];
    char dot[1024];
    char icon[1024];
    char cname[256];
    boolean ok = false;
    const char* c = 0;
    request *r = NULL, *u = NULL;

    strcpy(icon, name);

    const char* mvhome = getenv("METVIEW_USER_DIRECTORY");
    if (!mvhome)
        return 0;

    if (strstr(name, mvhome) != 0)
        sprintf(buf, "%s", icon);
    else
        sprintf(buf, "%s/%s", mvhome, icon);

    if (className) {
        strcpy(cname, className);
        c = &cname[0];
    }
    // If the classname is not present we need to find out the iconclass from
    // the folder-level dot file
    else {
        if (get_iconClass(buf, cname) == 0)
            c = &cname[0];
    }

    /* We handle Weather Symbols (GeoJSON) first */
    if (c && strncmp(c, "WS_", 3) == 0) {
        if (!import_weather_symbol(f, buf, c)) {
            importing(f, icon);
            printCannotImport(f, icon);
        }
        return low(mbasename(icon), 1, 1);
    }

    r = read_request_file(buf);

    /*We need to read the request again (into *u) otherwise mars cannot read the
     PARAMETER_2 subrequest from the request below:

    SIMPLE_FORMULA_FAMILY,
                FORMULA    = SAMPLE_FORMULA_DOD

    SAMPLE_FORMULA_DOD,
            PARAMETER_1 = GRIB Filter 1,
        PARAMETER_2 = GRIB Filter 1

    The reason for this behaviour is unclear at the moment*/

    u = read_request_file(buf);

    if (c) {
        if (strcmp(c, "NOTE") == 0)
            ok = import_note(f, icon, buf);
        else if (strcmp(c, "SHELL") == 0)
            ok = import_shell(f, icon, buf);
        else if (strcmp(c, "MAGML") == 0)
            ok = import_magml(f, icon, buf);
        else if (strcmp(c, "READ") == 0)
            ok = import_read(f, icon, r);
        else if (strcmp(c, "GEOPOINTS") == 0)
            ok = import_file(f, icon, buf);
        else if (strcmp(c, "MACROPARAM") == 0)
            ok = import_macroparam(f, icon, r);
        else if (strcmp(c, "TABLE_READER") == 0)
            ok = import_table_reader(f, icon, r);
        else if (strcmp(c, "TABLE_VISUALISER") == 0)
            ok = import_table_visualiser(f, icon, r);
        else if (strcmp(c, "RTTOV_VISUALISER") == 0)
            ok = import_rttov_visualiser(f, icon, r);
    }

    if (!ok && (r != NULL || c != NULL)) {
        request* v = find_object(c ? c : r->name);
        const char* t = v ? get_value(v, "type", 0) : NULL;

        if (t) {
            if (strcmp(t, "Macro") == 0)
                ok = import_macro(f, icon, buf);
            else if (strcmp(t, "Family") == 0 || strcmp(t, "FamilyWindow") == 0)
                ok = import_request(f, icon, r ? r->next : NULL, NULL);
            else if (strcmp(t, "File") == 0)
                ok = import_file(f, icon, buf);
            else
                ;
            /* printf("Type is %s\n",t); */
        }
    }

    if (!ok && (r != NULL)) {
        c = r->name;
        if (c) {
            if (strcmp(c, "LINK") == 0)
                ok = import_link(f, icon, r);
            else if (strcmp(c, "COMPUTE") == 0)
                ok = import_compute(f, icon, r);
            else
                ok = import_request(f, icon, r, NULL);
        }
    }

    if (!ok) {
        importing(f, icon);
        printCannotImport(f, icon);
        //        fprintf(f, "# Warning: cannot import : %s\n", icon);
        //        fprintf(f, "# Using the metview function, not available in batch\n\n");
        //        fprintf(f, "%s = metview(\"%s\")\n\n", low(mbasename(icon), 1, 1), icon);
        //        /* print_all_requests(r); */
        //        /* print_all_requests(u); */
    }

    free_all_requests(r);
    free_all_requests(u);
    return low(mbasename(icon), 1, 1);
}

static int get_iconClass(const char* iconPath, char* iconClass)
{
    char dot[1024];
    char buff[1024];
    const char* iconName;
    char name[256];

    sprintf(dot, "%s/._mv_iconlist_", mdirname(iconPath));
    iconName = mbasename(iconPath);

    FILE* fp;
    if ((fp = fopen(dot, "r")) != NULL) {
        while (fgets(buff, 1024, fp)) {
            // Remove end of line
            char* nlptr = strchr(buff, '\n');
            if (nlptr)
                *nlptr = '\0';

            // Find string after the third ","
            int cnt = 0;
            const char* cp = &buff[0];
            const char *c1 = 0, *c2 = 0;
            cp = strstr(cp, ",");
            if (cp++) {
                c1 = strstr(cp, ",");
                if (c1++)
                    c2 = strstr(c1, ",");
            }

            if (c1 && c2++) {
                if (strcmp(c2, iconName) == 0) {
                    strncpy(iconClass, c1, c2 - c1 - 1);
                    iconClass[c2 - c1 - 1] = '\0';
                    fclose(fp);
                    return 0;
                }
            }
        }

        fclose(fp);
    }

    return 1;
}

static void formula3(math* m, char* name, const char* sub)
{
    if (m) {
        if (m->name && (m->arity <= 0) && (name == m->name)) {
            strfree(name);
            m->name = strcache(sub);
        }
        formula3(m->left, name, sub);
        formula3(m->right, name, sub);
    }
}

static void formula1(FILE* f, const char* ref, math* m, math* x)
{
    if (m) {
        formula1(f, ref, m->left, x);
        formula1(f, ref, m->right, x);
        if (m->name)
            if (m->arity <= 0)
                if (!is_number(m->name) && (m->name[0] != '.')) {
                    char buf[1024];
                    char *y, *name;
                    const char* p = buf;
                    char* o;
                    sprintf(buf, "%s", m->name);

                    if (*buf == '"' || *buf == '\'') {
                        p = buf + 1;
                        buf[strlen(buf) - 1] = 0;
                    }

                    if (*p != '/')
                        p = makepath(mdirname(ref), p);

                    o = strcache(p);
                    y = strcache(import(f, o, NULL));
                    strfree(o);

                    name = strcache(m->name);
                    sprintf(buf, ".%s", y);
                    formula3(x, name, buf);
                    strfree(name);
                    strfree(y);
                }
    }
}

static void formula2(FILE* f, math* m)
{
    if (m) {
        if (m->name)
            if (m->arity <= 0) {
                if (is_number(m->name))
                    fprintf(f, "%.12g", atof(m->name));
                else {
                    fprintf(f, "%s", m->name + 1);
                }
                if (m->arity < 0) {
                    fprintf(f, "[");
                    formula2(f, m->left);
                    fprintf(f, "]");
                }
            }
            else {
                fprintf(f, "(");
                if (isalpha(m->name[0])) {
                    fprintf(f, "%s(", m->name);
                    formula2(f, m->left);
                    fprintf(f, ")");
                }
                else {
                    formula2(f, m->left);
                    fprintf(f, " %s ", m->name);
                    formula2(f, m->right);
                }
                fprintf(f, ")");
            }
        else {
            formula2(f, m->left);
            if (m->right)
                fprintf(f, ",");
            formula2(f, m->right);
        }
    }
}


static request* link1(FILE* f, request* r)
{
    request* s = empty_request(NULL);
    r = r->next;
    while (r) {
        request* u = find_object(get_value(r, "CLASS", 0));
        if (u)
            add_value(s, get_value(u, "type", 0), "%s",
                      import(f, get_value(r, "NAME", 0), NULL));
        r = r->next;
    }
    return s;
}


static void import_window(FILE* f, request* r)
{
    const char* user = getenv("METVIEW_USER_DIRECTORY");
    char icon[1024];
    int m = 0;
    int i = 0;
    const char* p;

    while (r) {
        int w = strcmp(r->name, "PLOTWINDOW") == 0 || strcmp(r->name, "CAROUSEL") == 0;

        strcpy(icon, "y");

        if (w)
            fprintf(f, "if not retval then # Don't create windows\n");

        /* It's an object */
        if ((p = get_value(r, "_NAME", 0))) {
            char buf[1024];
            sprintf(buf, "%s/%s", user, p);
            if (access(buf, 0) == 0) {
                fprintf(f, "if use_objects then\n");
                fprintf(f, "  %s = metview(\"%s\")\n", icon, p);
                fprintf(f, "else\n");
                fprintf(f, "	%s = %s\n", icon, import(f, p, NULL));
                fprintf(f, "end if\n");
            }
            else
                p = NULL;
        }
        if (!p) {
            if (!import_request(f, icon, r, NULL)) {
                /* The request is not known */
                request* u = empty_request(NULL);
                set_value(u, "macro", "");
                set_value(r, "_VERB", "%s", r->name);
                import_request(f, icon, r, u);
                free_all_requests(u);
            }
        }
        fprintf(f, "x = x & [%s]\n", icon);

        if (w)
            fprintf(f, "end if\n");

        r = r->next;
    }
}

static void serve(svcid* id, request* r, void* misc)
{
    char name[1024];
    char icon[1024];
    FILE* f;
    request* u;
    const char* p;
    int n = 0;
    const char* userd = getenv("METVIEW_USER_DIRECTORY");
    long now = mars_julian_to_date(mars_date_to_julian(0), 1);

    /* mars.debug = 1; */
    /* print_all_requests(r); */
    /* mars.debug = 0; */

    p = get_value(r, "TITLE", 0);
    if (!p)
        p = "Plot Window";

    sprintf(name, "%s/Content of %s", userd, p);
    while (access(name, 0) == 0)
        sprintf(name, "%s/Content of %s (%d)", userd, p, ++n);

    if (r && strcmp(r->name, "VISTOOL") == 0)
        r = r->next;

    cvtdates = 1;

    sprintf(icon, "%s/.%s", userd, mbasename(name));
    f = fopen(icon, "w");
    fprintf(f, "USER_INTERFACE,ICON_CLASS=WMACRO\n");
    fclose(f);

    f = fopen(name, "w");
    fprintf(f, "# This macro was gererated automagicaly\n");
    fprintf(f, "# by %s the %ld\n", user(NULL), now);
    fprintf(f, "# The metview user directory was %s", userd);
    fprintf(f, "\n\n");
    fprintf(f, "# You can control the playback of the macro by changing\n");
    fprintf(f, "# the two following variables (set to 0 or 1):\n\n");

    fprintf(f, "use_objects = 1 # Use the original icon definitions.\n");
    fprintf(f, "rel_dates   = 1 # Make date relatives to today.\n");

    fprintf(f, "\n#\n\n");
    fprintf(f, "retval = runmode(\"visualise\")\n\n");
    fprintf(f, "if rel_dates then\n");
    fprintf(f, "   today = 0\n");
    fprintf(f, "else\n");
    fprintf(f, "   today = %ld\n", now);
    fprintf(f, "end if\n\n");

    fprintf(f, "if use_objects then\n");
    fprintf(f, "   putenv(METVIEW_USER_DIRECTORY : '%s' )\n", userd);
    fprintf(f, "end if\n\n");

    fprintf(f, "x = []\n\n");

    /* Mode ? */
    if ((u = get_subrequest(r, "_MODE", 0))) {
        import_window(f, u);
        free_all_requests(u);
    }
    else
        import_window(f, r);

    fprintf(f, "\n");
    fprintf(f, "if retval then\n");
    fprintf(f, "   return x\nelse\n   plot(x)\nend if\n");

    fclose(f);


    u = empty_request("STATUS");
    set_value(u, "NAME", "/%s", mbasename(name));
    set_value(u, "CLASS", "WMACRO");
    set_value(u, "STATUS", "CREATED");
    send_message(id->s, u);
    free_all_requests(u);

    u = empty_request("USER_MESSAGE");
    set_value(u, "INFO", "Window content saved in '%s'", mbasename(name));
    send_message(id->s, u);
    free_all_requests(u);

    u = empty_request("STRING");
    set_value(u, "VALUE", "Window content saved in '%s'", mbasename(name));
    send_reply(id, u);
    free_all_requests(u);
}

int main(int argc, char** argv)
{
    // Service environment
    marsinit(&argc, argv, &setup, NUMBER(opts), opts);
    if (setup.serve) {
        svc* s = create_service(progname());
        add_service_callback(s, NULL, serve, NULL);
        service_run(s);
        return 0;
    }

    // Standalone environment
    // argv[1]: path to icon file
    // argv[2]: path to output file
    // argv[3]: icon class (optional)
    // argv[4]: number of spaces to use instead of tabs (optional)
    // argv[5]: macro or python (optional, default=macro)

    // Minimum number of arguments
    if (argc < 3) {
        // Send an error message
        printf("mvImportDesktop error: minimum number of arguments is 2 \n");
        return -1;
    }

    // Get input info
    const char* iconName = argv[1];
    FILE* f = fopen(argv[2], "w");
    const char* cclass = NULL;
    strcpy(tab, "\t");

    if (argc == 4)
        cclass = argv[3];
    else if (argc == 5) {
        int num_spaces = atoi(argv[4]);
        int i;
        cclass = argv[3];

        spaces = true;
        for (i = 0; i < num_spaces; i++)
            tab[i] = ' ';
        tab[num_spaces] = '\0';
    }
    else {
        int num_spaces = atoi(argv[4]);
        int i;
        cclass = argv[3];

        spaces = true;
        for (i = 0; i < num_spaces; i++)
            tab[i] = ' ';
        tab[num_spaces] = '\0';
        strcpy(language_, argv[5]);
    }

    if (strcmp(language_, "python") == 0) {
        strcpy(prefix_, "mv.");
        strcpy(separator_, "=");
        translations = python_translations;
        num_translations = sizeof(python_translations) / sizeof(translation);
    }

    import(f, iconName, cclass);

    return 0;
}
