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

typedef struct private {
    char* fname;
    FILE* file;
    boolean obs;
    boolean list;
    int bufsize;
}
private
;

static void msbase_init(void);
static err msbase_open(void* data, request*, request*, int);
static err msbase_close(void* data);
static err msbase_read(void* data, request* r, void* buffer, long* length);
static err msbase_write(void* data, request* r, void* buffer, long* length);

static option opts[] = {
    {
        "bufsize",
        "MARS_READDISK_BUFFER",
        "-bufsize",
        "0",
        t_int,
        sizeof(int),
        OFFSET(private, bufsize),
    },
};


static base_class _msbase = {

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

    false, /* inited       */

    sizeof(private), /* private size */
    NUMBER(opts),    /* options_count */
    opts,            /* options       */


    msbase_init, /* init         */

    msbase_open,  /* open         */
    msbase_close, /* close        */

    msbase_read,  /* read         */
    msbase_write, /* write        */

    NULL, /* control       */
    NULL, /* check         */
    NULL, /* query         */
    NULL, /* archive       */
    NULL, /* admin         */
    NULL, /* validate      */
};

/* the only 'public' variable ... */

base_class* msbase = &_msbase;

static err readmany(FILE* f, char* buff, long* len) {
    int count = 0;
    long a    = *len;
    long b    = *len;
    long n;
    int pad = sizeof(long);
    int rnd;
    off_t pos = ftell(f);

    err e = 0;
    *len  = 0;


    n = a - pad;
    while (a > 10240 && (e = _readbufr(f, buff, &n)) == 0) {
        a   = n;
        rnd = ((a + pad - 1) / pad) * pad - a;
        while (rnd-- > 0)
            buff[a++] = 0;

        buff += a; /* should round to 8 ... */
        b -= a;
        *len += a;
        a = b;
        count++;
        n   = a - pad;
        pos = ftell(f);
    }

    if (count == 0 && (e == -3 || e == -4))
        *len = a;

    if (count && (e == -1 || e == -3 || e == -4)) {
        fseek(f, pos, SEEK_SET);
        return 0;
    }

    return e;
}

static void msbase_init(void) {
}

static err msbase_open(void* data, request* r, request* env, int mode) {
private
    *g = (private*)data;
    err e;
    const char* user = get_value(env, "user", 0);
    request* s       = clone_all_requests(r);
    long version     = atol(get_value(r, "_MARS_VERSION", 0));
    long my_version  = marsversion();

    if (user)
        new_user(user);

    if (version < my_version) {
        if (version)
            marslog(LOG_INFO, "Current MARS Client : %ld", version);
        marslog(LOG_INFO, "New MARS Client available: %ld", my_version);
    }
    else if (version > my_version) {
        marslog(LOG_WARN, "Client (%ld) is ahead of server (%ld)", version, my_version);
    }

    g->fname = strcache(marstmp());
    g->file  = NULL;
    g->obs   = is_bufr(s);
    g->list  = EQ(r->name, "LIST");

    /* Each forked task will print it's pid */
    mars.show_pid = getpid();

    /* Force target name */

    set_value(s, "TARGET", "%s", g->fname);
    unset_value(s, "FIELDSET");
    unset_value(s, "DATABASE");

    /* Get again language defaults and rules */
    /* because it can come from an old mars client */
    s = build_mars_request(s);

    e = handle_request(s, NULL);

    free_all_requests(s);

    return e;
}

static err msbase_close(void* data) {
private
    *g = (private*)data;
    unlink(g->fname);
    strfree(g->fname);
    if (g->file)
        fclose(g->file);
    return 0;
}

static err msbase_read(void* data, request* r, void* buffer, long* length) {
private
    *g = (private*)data;
    err e;

    if (g->file == NULL) {
        g->file = fopen(g->fname, "r");
        if (g->file == NULL) {
            marslog(LOG_EROR | LOG_PERR, "Cannot open %s", g->fname);
            return -2;
        }
        else {
            /* Modify disk I/O buffer, if application buffer allows for that */
            if (g->bufsize) {
                if (g->bufsize > *length)
                    marslog(LOG_WARN, "Cannot use %d bytes for setvbuf, maximum size is %ld", g->bufsize, *length);
                else if (setvbuf(g->file, buffer, _IOFBF, *length))
                    marslog(LOG_WARN | LOG_PERR, "setvbuf failed");
            }
        }
    }

    if (g->list) {
        long n = 0;
        if ((n = fread(buffer, 1, *length, g->file)) > 0) {
            *length = n;
            return NOERR;
        }
        else
            return EOF;
    }
    else {
        if (g->obs)
            e = readmany(g->file, buffer, length);
        else
            e = _readany(g->file, buffer, length);

        if (e == 0 && r) {
            if (g->obs) {
                bufr_to_request(r, buffer, *length);
            }
            else {
                grib_to_request(r, buffer, *length);
            }
        }
    }

    return e;
}

static err msbase_write(void* data, request* r, void* buffer, long* length) {
    marslog(LOG_WARN, "now in msbase_write. Sorry not implemeted");
    return EOF;
}
