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

/*
   Quick & dirty BUFR (tropical cyclones) to FDB
   Manuel Fuentes 31-Aug-2004
*/


extern int initfdb(void);
extern int openfdb(char*, int*, char*);
extern int openfdb_remote(char*, int*, char*, char*);
extern int readfdb(int*, void*, int*);
extern int writefdb(int*, void*, int*);
extern int closefdb(int*);
extern int setvalfdb(int*, char*, char*);

typedef struct data {
    char* FdbHost;
    char* Fdbroot;
    char* Fdbname;
    char* mode;

    char* mars_class;
    char* mars_type;
    char* mars_expver;
    char* mars_stream;
    char* mars_domain;

    char* mars_date;
    char* mars_time;

    char* file;
    int ref;
    long fdbbufrlen;
    boolean section2;
} data;

static data setup;

static option opts[] = {
    {
        NULL,
        "FDB_SERVER_HOST",
        "-host",
        "hpcbfdb",
        t_str,
        sizeof(char*),
        OFFSET(data, FdbHost),
    },
    {
        NULL,
        "FDB_ROOT",
        "-R",
        "/ma_fdb",
        t_str,
        sizeof(char*),
        OFFSET(data, Fdbroot),
    },
    {
        NULL,
        "FDB_NAME",
        "-n",
        "fdb",
        t_str,
        sizeof(char*),
        OFFSET(data, Fdbname),
    },
    {
        NULL,
        "FDB_CONFIG_MODE",
        "-m",
        "client",
        t_str,
        sizeof(char*),
        OFFSET(data, mode),
    },
    {
        NULL,
        NULL,
        "-c",
        "od",
        t_str,
        sizeof(char*),
        OFFSET(data, mars_class),
    },
    {
        NULL,
        NULL,
        "-t",
        "tf",
        t_str,
        sizeof(char*),
        OFFSET(data, mars_type),
    },
    {
        NULL,
        NULL,
        "-v",
        "0001",
        t_str,
        sizeof(char*),
        OFFSET(data, mars_expver),
    },
    {
        NULL,
        NULL,
        "-s",
        "oper",
        t_str,
        sizeof(char*),
        OFFSET(data, mars_stream),
    },
    {
        NULL,
        NULL,
        "-d",
        "g",
        t_str,
        sizeof(char*),
        OFFSET(data, mars_domain),
    },
    {
        NULL,
        NULL,
        "-T",
        NULL,
        t_str,
        sizeof(char*),
        OFFSET(data, mars_time),
    },
    {
        NULL,
        NULL,
        "-D",
        NULL,
        t_str,
        sizeof(char*),
        OFFSET(data, mars_date),
    },
    {
        NULL,
        NULL,
        "-f",
        NULL,
        t_str,
        sizeof(char*),
        OFFSET(data, file),
    },
    {
        NULL,
        "FDB_BUFR_LEN",
        NULL,
        "1048576",
        t_long,
        sizeof(long),
        OFFSET(data, fdbbufrlen),
    },
    {
        NULL,
        "FDB_CHECK_RDB_KEY",
        NULL,
        "1",
        t_boolean,
        sizeof(boolean),
        OFFSET(data, section2),
    },
};

void marssetvalfdb(int* fdb, char* par, char* val) {
    marslog(LOG_INFO, "setvalfdb '%s' '%s'", par, val);
    setvalfdb(fdb, par, val);
}

int main(int argc, char** argv) {
    FILE* fd     = 0;
    int e        = NOERR;
    int cnt      = 0;
    long buflen  = 1024 * 1024 * 1; /* 1 Mb to read from file */
    long length  = buflen;
    char* buffer = reserve_mem(length);

    int current_len = 0;
    char* fdbbufr;

    char buf[80];
    char date[24], time[24];

    marsinit(&argc, argv, &setup, NUMBER(opts), opts);

    fdbbufr = reserve_mem(setup.fdbbufrlen);

    if (!setup.file) {
        printf("Usage: %s -c cc [-t tt] -v vvvv -s ssss [-T HHMM] [-D YYYYMMDD] [-R root] [-host hhhhhhh] [-n nnnnn]-f <file>\n", argv[0]);
        printf("Defaults: %s -c %s -t %s -v %s -s %s -d %s -R %s -host %s -n %s\n",
               argv[0],
               setup.mars_class, setup.mars_type, setup.mars_expver, setup.mars_stream, setup.mars_domain,
               setup.Fdbroot, setup.FdbHost, setup.Fdbname);
        marsexit(1);
    }

    start_timer();
    if ((fd = fopen(setup.file, "r")) == NULL) {
        marslog(LOG_EROR | LOG_PERR, "fopen: %s", setup.file);
        marsexit(1);
    }

    while ((e = _readany(fd, buffer, &length)) == NOERR) {
        cnt++;

        if (setup.section2) {
            packed_key ky;
            packed_key* k = &ky;

            marslog(LOG_INFO, "Reading section 2");
            if (!get_packed_key(buffer, k)) {
                marslog(LOG_WARN, "BUFR message %d has no key. Skipping", cnt);
                return -1;
            }

            sprintf(date, "%04d%02d%02d", KEY_YEAR(k), KEY_MONTH(k), KEY_DAY(k));
            sprintf(time, "%02d%02d", KEY_HOUR(k), KEY_MINUTE(k));
            setup.mars_date = date;
            setup.mars_time = time;
        }
        else {
            if (!setup.mars_date || !setup.mars_time) {
                marslog(LOG_EROR, "FDB_CHECK_RDB_KEY = 0, but DATE or TIME missing");
                marslog(LOG_EROR, "Please, specify  -D YYYYMMDD -T HHMM");
                return -1;
            }
        }

        if (!e) {
            if ((current_len + length) > setup.fdbbufrlen) {
                marslog(LOG_EROR, "Buffer too small (%sbyte) to write data to FDB", bytename(setup.fdbbufrlen));
                marslog(LOG_EROR, "Set env. var. FDB_BUFR_LEN to a larger number of bytes (currently %ld)", setup.fdbbufrlen);
                marsexit(1);
            }
            else {
                marslog(LOG_INFO, "Found %ld bytes long BUFR message", length);
            }
            memcpy(fdbbufr + current_len, buffer, length);
            current_len += length;
        }
        length = buflen;
    }

    if (current_len > 0) {
        e = initfdb();
        if (!e && (e = openfdb(setup.Fdbname, &setup.ref, "w")) != NOERR) {
            marslog(LOG_EROR, "Error %d while openfdb_remote(%s,&ref,w,%s)", e, setup.Fdbname, setup.FdbHost);
            marsexit(1);
        }

        if (!e) {
            marssetvalfdb(&setup.ref, "class", setup.mars_class);
            marssetvalfdb(&setup.ref, "stream", setup.mars_stream);
            marssetvalfdb(&setup.ref, "expver", setup.mars_expver);
            marssetvalfdb(&setup.ref, "domain", setup.mars_domain);

            marssetvalfdb(&setup.ref, "type", setup.mars_type);
            marssetvalfdb(&setup.ref, "date", setup.mars_date);
            marssetvalfdb(&setup.ref, "time", setup.mars_time);
        }

        /* Write to FDB */
        if ((e = writefdb(&setup.ref, fdbbufr, &current_len)) != NOERR) {
            marslog(LOG_EROR, "Error %d while writefdb(&ref,buffer,%d)", e, current_len);
            marsexit(1);
        }

        if (closefdb(&setup.ref) != NOERR) {
            marslog(LOG_EROR, "Error while closefdb()", e);
            marsexit(1);
        }
    }

    marslog(LOG_INFO, "write %d bytes to fdb, %d BUFR messages", current_len, cnt);

    switch (e) {
        case EOF:
            e = NOERR;
        case NOERR:
            marslog(LOG_INFO, "Decoded %d BUFR messages", cnt);
            break;
        case BUF_TO_SMALL:
            marslog(LOG_WARN, "Buffer is too small (%d bytes, %d needed.)", buflen, length);
            break;
        case NOT_FOUND_7777:
            marslog(LOG_EROR, "Group 7777 not found at the end of BUFR message");
            break;
        default:
            marslog(LOG_EROR, "readany: got unknown error %d", e);
            break;
    }

    if (buffer)
        release_mem(buffer);
    if (fdbbufr)
        release_mem(fdbbufr);

    stop_timer(buf);
    timer_cpu();
    if (*buf)
        marslog(LOG_INFO, "Request time: %s", buf);

    print_memory_usage(NULL);
    marsexit(e);
}
