/*
 * © 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 <ctype.h>
#include "mars.h"


static void file_init(void);
static err file_open(void* data, request*, request*, int);
static err file_close(void* data);
static err file_read(void* data, request* r, void* buffer, long* length);
static err file_write(void* data, request* r, void* buffer, long* length);
static boolean file_check(void* data, request* r);

typedef struct filedata {
    char* root;
    char* tmplate;
    char* order;
    request* w;
    request* u;
} filedata;

static option opts[] = {
    {"fileroot", "FILE_ROOT", NULL, "/tmp/",
     t_str, sizeof(char*),
     OFFSET(filedata, root)},

    {"template", "TEMPLATE", NULL,

     "(TYPE,%s).(STREAM,%s).(LEVTYPE,%s).(DOMAIN,%s)"
     ".(NUMBER,%04d).(_EXPVER,%04X).(_REPRES,%02s)"
     "/(_AREA_N,%.6f).(_AREA_W,%.6f).(_AREA_S,%.6f).(_AREA_E,%.6f)."
     "(_ROTATION_LAT,%.6f).(_ROTATION_LON,%.6f)."
     "(_GRID_NS,%.6f).(_GRID_EW,%.6f).(_GAUSSIAN,%4d).(_GRIDNAME,%s).(_TRUNCATION,%4d)/"
     "(PARAM,%03d).(LEVELIST,%04d).(DATE,%06d).(TIME,%04d).(STEP,%04d)"

     ,

     t_str, sizeof(char*),
     OFFSET(filedata, tmplate)},

    {"fileorder", "FILE_ORDER", NULL, NULL, /* default : mars order */
     t_str, sizeof(char*),
     OFFSET(filedata, order)},
};


base_class _filebase = {

    NULL,       /* parent class */
    "filebase", /* name         */

    false, /* inited       */

    sizeof(filedata), /* private size */
    NUMBER(opts),     /* option count */
    opts,             /* options      */

    file_init, /* init         */

    file_open,  /* open         */
    file_close, /* close        */

    file_read,  /* read         */
    file_write, /* write        */

    NULL, /* control      */

    file_check, /* check        */

};


base_class* filebase = &_filebase;

static void file_init(void) {
}

static err file_open(void* data, request* r, request* e, int mode) {
    filedata* file = (filedata*)data;

    if (file->order == NULL)
        file->u = unwind_one_request(r);
    else {
        char** names;
        int cnt = 1;
        int i   = 0;
        char* p = file->order;
        char* q;

        while (*p) {
            if (*p == ',')
                cnt++;
            p++;
        }

        names = NEW_ARRAY(char*, cnt);

        p = file->order;
        while ((q = strtok(p, ","))) {
            names[i++] = q;
            p          = NULL;
        }


        file->u = custom_unwind_one_request(r, cnt, names);

        for (i = 1; i < cnt; i++) {
            p     = names[i];
            p[-1] = 0;
        }

        FREE(names);
    }
    file->w = file->u;
    return NOERR;
}

static err file_close(void* data) {
    filedata* file = (filedata*)data;
    free_all_requests(file->u);
    return 0;
}

static err findpath(filedata* file, request* r, char* path) {
    char* p   = file->tmplate;
    int i     = 0;
    int state = 0;

    char word[64];
    char ident[64];
    char buf[64];
    const char *s, *q;
    char c;

    strcpy(path, file->root);

    while (*p) {
        switch (*p) {
            case '(':
                word[i] = 0;
                strcat(path, word);
                state = 1;
                i     = 0;
                break;

            case ',':
                if (state != 1)
                    return -9;
                word[i] = 0;
                strcpy(ident, word);
                state = 2;
                i     = 0;
                break;

            case ')':
                if (state != 2)
                    return -9;
                word[i] = 0;
                state   = 0;
                i       = 0;

                s = get_value(r, ident, 0);
                q = word;
                c = 0;
                while (*q) {
                    if (isalpha(*q))
                        c = *q;
                    q++;
                };

                if (s) {
                    if (strchr("diouxX", c))
                        sprintf(buf, word, atol(s));
                    else if (strchr("feEgG", c))
                        sprintf(buf, word, atof(s));
                    else if (strchr("s", c))
                        sprintf(buf, word, s);
                    else
                        return -9;

                    strcat(path, buf);
                }
                else {
                    /* ???? */
                    strcat(path, "x");
                }

                break;

            default:
                if (i >= sizeof(word))
                    return -9;
                word[i++] = *p;
                break;
        }
        p++;
    }
    word[i] = 0;
    strcat(path, word);
    return 0;
}

static err file_read(void* data, request* r, void* buffer, long* length) {
    filedata* file = (filedata*)data;
    char path[1024];
    FILE* f;
    err ret = 0;

    if (file->w == NULL)
        return EOF;


    if (r)
        reqcpy(r, file->w);

    if ((ret = findpath(file, file->w, path))) {
        marslog(LOG_EROR, "Cannot parse %s", file->tmplate);
        return ret;
    }

    file->w = file->w->next;


    f = fopen(path, "r");
    if (f == NULL) {
        marslog(LOG_DBUG | LOG_PERR, "Cannot open %s", path);
        return -2;
    }


    marslog(LOG_DBUG, "Open: %s", path);

    ret = _readany(f, buffer, length);

    fclose(f);
    if (ret == EOF) {
        marslog(LOG_EROR | LOG_PERR, "EOF in file %s", path);
        ret = -2;
    }

    return ret;
}

static err file_write(void* data, request* r, void* buffer, long* length) {
    filedata* file = (filedata*)data;
    err ret        = 0;
    char path[1024];
    const char* dir;
    long len;
    FILE* f;

    if ((ret = findpath(file, r, path))) {
        marslog(LOG_EROR, "Cannot parse %s", file->tmplate);
        return ret;
    }


    dir = mdirname(path);
    if (access(dir, F_OK) != 0)
        mkdirp(dir, 0777);

    f = fopen(path, "w");
    if (f == NULL) {
        marslog(LOG_EROR | LOG_PERR, "Cannot open %s", path);
        return -2;
    }


    len = *length;
    if ((*length = fwrite(buffer, 1, len, f)) != len) {
        ret = -2;
        marslog(LOG_EROR | LOG_PERR, "Error writing to %s", path);
    }

    fclose(f);
    return ret;
}

static boolean file_check(void* data, request* r) {
    return true;
}
