/*
 * © Copyright 1996-2012 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#include "mars.h"

int one     = 0;
int obs     = 0;
int compare = 0;

char* sorted[] = {
    "level",
    "levelist",
};

int sort_cnt = NUMBER(sorted);

FILE* out = NULL;

void clean_exit(int s) {
    if (out != stdout && out != NULL)
        fclose(out);

    exit(s);
}

void p_values(value* r) {
    int col = 0;

    while (r) {
        col += fprintf(out, "%s", r->name);
        if (r->next) {
            putc('/', out);
            col++;
            if (col > 50) {
                putc('\n', out);
                col = 0;
            }
        }
        r = r->next;
    }
}

void p_sorted_values(value* r) {
    request* d = empty_request("dummy");
    value* s   = r;
    int ok     = 1;

    while (s) {
        if (!is_number(s->name))
            ok = 0;
        s = s->next;
    }

    if (!ok) {
        p_values(r);
        return;
    }


    while (r) {
        add_ordered_value(d, "dummy", "%s", r->name);
        r = r->next;
    }
    p_values(d->params->values);
    free_all_requests(d);
}

void p_parameters(const char* name, parameter* r) {
    while (r) {
        if ((r->default_values == NULL) || (strcasecmp(r->default_values->name, name) == 0)) {
            if (*r->name != '_') {
                int sort = 0;
                int i;

                fprintf(out, ",\n    %-10s = ", r->name);
                for (i = 0; i < sort_cnt; i++) {
                    if (EQ(r->name, sorted[i]))
                        sort = 1;
                }
                if (sort)
                    p_sorted_values(r->values);
                else
                    p_values(r->values);
            }
        }

        r = r->next;
    }
    putc('\n', out);
}


void p_requests(const char* name, request* r) {
    while (r) {
        fprintf(out, "%s", r->name);
        p_parameters(name, r->params);
        putc('\n', out);
        r = r->next;
    }
}


static void setverb(request* r, const char* name, const char* file1, const char* file2, const char* unset) {
    while (r) {
        strfree(r->name);
        r->name = strcache(name);

        unset_value(r, unset);
        set_value(r, file2, "%s", get_value(r, file1, 0));
        r = r->next;
    }
}

void usage() {
    fprintf(stderr, "marsgen [-c][-o][-1][-s sorted_param][-f <output file>]\n");
    clean_exit(1);
}

void dump(request* r) {
    int cnt    = 0;
    request* s = r;

    while (s) {
        set_value(s, "_MARSBLK_", "\"marsblk.%d\"", cnt);
        set_value(s, "_MARSUBK_", "\"marsubk.%d\"", cnt);
        set_value(s, "_MARSRET_", "\"marsret.%d\"", cnt);
        s = s->next;
        cnt++;
    }

    if (obs) {
        int cnt = 0;
        s       = r;
        while (s) {
            ++cnt;
            fprintf(out, "$PREOBS $header.%d $data.%d %s\n", cnt, cnt,
                    get_value(s, "_MARSUBK_", 0));
            s = s->next;
        }
    }
    else {
        fprintf(out, "$MARS_FROM_FDB << @\n\n");
        setverb(r, "RETRIEVE", "_MARSBLK_", "TARGET", "SOURCE");
        p_requests("FDB_RETRIEVE", r);
        fprintf(out, "\n\n@\n\n$FDB_COMPLETE\n\n");

        s = r;
        while (s) {
            fprintf(out, "$UNBLOCK %s %s\n",
                    get_value(s, "_MARSBLK_", 0),
                    get_value(s, "_MARSUBK_", 0));
            s = s->next;
        }
    }
    fprintf(out, "\n\n");


    fprintf(out, "$MARS_TO_IBM << @\n\n");
    setverb(r, "ARCHIVE", "_MARSUBK_", "SOURCE", "TARGET");
    p_requests("ARCHIVE", r);
    fprintf(out, "\n\n@\n\n$ARC_COMPLETE\n\n");

    if (compare) {

        fprintf(out, "$MARS_FROM_IBM << @\n\n");
        setverb(r, "RETRIEVE", "_MARSRET_", "TARGET", "SOURCE");
        p_requests("IBM_RETRIEVE", r);
        fprintf(out, "\n\n@\n\n");

        s = r;
        while (s) {
            fprintf(out, "%s %s %s\n",
                    obs ? "$COMPOBS" : "$COMPARE",
                    get_value(s, "_MARSBLK_", 0),
                    get_value(s, "_MARSRET_", 0));
            s = s->next;
        }
        fprintf(out, "\n\n$CMP_COMPLETE\n\n");
    }
}

void sorting(char* order) {
    fprintf(stderr, "Sorting not implemented\n");
    exit(1);
}

int main(int argc, char** argv) {
    request* r = read_request_file(NULL);
    request* s;
    int cnt = 0;
    parameter* p;
    int c;
    extern int optind;
    extern char* optarg;
    int test;

    out = stdout;
    while ((c = getopt(argc, argv, "co1f:s:")) != -1) {
        switch (c) {
            case 'c':
                compare = 1;
                break;

            case 'o':
                obs = 1;
                break;

            case 's':
                sorting(optarg);
                break;

            case '1':
                one = 1;
                one = 0;
                break;

            case 'f':
                if ((out = fopen(optarg, "w")) == NULL) {
                    printf("Error opening %s\n", optarg);
                    clean_exit(1);
                }
                break;

            default:
                usage();
                break;
        }
    }

    if (argc != optind)
        usage();

    if (r == NULL)
        clean_exit(1);

    if (strcasecmp(r->name, "loop") == 0) {
        request* z = NULL;
        request* y = NULL;
        p          = r->params;
        r          = r->next;

        while (p) {
            value* v = p->values;
            while (v) {
                request* t = r;
                s          = clone_all_requests(r);

                if (z == NULL)
                    z = s;
                else
                    y->next = s;

                while (s) {
                    /* copy def values */

                    parameter* a = t->params;
                    parameter* b = s->params;

                    while (a && b) {
                        b->default_values = a->default_values;
                        a                 = a->next;
                        b                 = b->next;
                    }

                    y = s;
                    set_value(s, p->name, "%s", v->name);


                    /* check for tests */
                    test = 0;
                    a    = t->params;
                    b    = s->params;
                    while (a) {
                        if (a->default_values)
                            if (strcasecmp(a->default_values->name, "if") == 0) {
                                const char* q     = get_value(s, a->name, 0);
                                b->default_values = NULL;
                                if (q) {
                                    value* w = a->values;
                                    while (w) {
                                        if (strcasecmp(w->name, q) == 0)
                                            test = 1;
                                        w = w->next;
                                    }
                                }
                            }
                        a = a->next;
                        b = b->next;
                    }

                    /* check for tests */
                    a = t->params;
                    b = s->params;
                    while (a) {
                        if (a->default_values)
                            if (strcasecmp(a->default_values->name, "then") == 0) {
                                value* w          = a->values;
                                b->default_values = NULL;
                                b->name           = strcache("_dummy");
                                if (test) {
                                    unset_value(s, a->name);
                                    while (w) {
                                        if (w == a->values)
                                            set_value(s, a->name, "%s", w->name);
                                        else
                                            add_value(s, a->name, "%s", w->name);
                                        fprintf(out, "%s ", w->name);
                                        w = w->next;
                                    }
                                    fprintf(out, "\n");
                                }
                            }
                        a = a->next;
                        b = b->next;
                    }

                    s = s->next;
                    t = t->next;
                }
                v = v->next;
            }
            p = p->next;
        }
        r = z;
    }

    if (one) {
        request* u = empty_request("dummy");
        while (r) {
            request* s = r->next;
            r->next    = 0;
            reqcpy(u, r);
            dump(u);
            r = s;
        }
    }
    else
        dump(r);

    clean_exit(0);
}
