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

#if defined(ODBDIRECT)

//----------------------------------------------------------------------------------------------------------------------

#include "eckit/eckit.h"
#include "eckit/io/Buffer.h"
#include "eckit/io/DataHandle.h"
#include "eckit/log/Log.h"
#include "eckit/runtime/Main.h"

#include "marslib/MarsTask.h"

#include "odbsvr/ODBRetriever.h"

//----------------------------------------------------------------------------------------------------------------------

static void     odb_init(void);
static err      odb_open(void *data,request*,request*,int);
static err      odb_close(void *data);
static err      odb_read(void *data,request *r,void *buffer,long *length);
static err      odb_write(void *data,request *r,void *buffer,long *length);
static boolean  odb_check(void *data,request *r);

extern "C" void mars_odb_init(int *argc, char **argv) {
	eckit::Main::initialise(*argc, argv);
}

static void odb_init(void)
{
}

#if defined(ECKIT_HAVE_FMEMOPEN) || defined (EC_HAVE_FMEMOPEN)
#define _GNU_SOURCE
#endif


typedef struct odbdata {
	FILE* f;
	long64 total_read;
	timer* odb_timer;
	double start_time;
	size_t buffer_size;
	char *buffer;
} odbdata;

static option opts[] = {
    {(char*)"buffer-size", (char*)"ODB_DIRECT_BUFFER_SIZE", NULL, (char*)"67108864", t_int, sizeof(int),
	OFFSET(odbdata, buffer_size)},
};

base_class _odbdirect = {

	NULL,                      /* parent class */
    (char*)"odbdirect",        /* name         */

	false,                     /* inited       */

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

    odb_init,                  /* init         */

    odb_open,                  /* open         */
    odb_close,                 /* close        */

    odb_read,                  /* read         */
    odb_write,                 /* write        */

	NULL,                      /* control      */

    odb_check,                 /* check        */

};

base_class *odbdirect = &_odbdirect;


static err  odb_open(void *data,request *r,request *ev,int mode)
{
	odbdata *odb = (odbdata*)data;

    timer *index = get_timer("ODB index time",NULL,true);

	odb->odb_timer = get_timer("ODB read time",NULL,true);

	parameter *p = r->params;

	char name[80];

	MarsRequest req("retrieve");
	MarsRequest env("environ");

	while(p)
	{
		if(p->name[0] == '_') {
			p = p->next;
			continue;
		}

		strcpy(name, lowcase(p->name));
		std::string s = lowcase(p->values->name);

		std::vector<std::string> x;
		value* v = p->values;
		while(v) {
			x.push_back(lowcase(v->name));
			v = v->next;
		}

		req.setValues(name, x);

		// eckit::Log::info() << "key " << p->name << " " << p->values->name << std::endl;

		p = p->next;
	}

	// odb->buffer_size = 64 * 1024 * 1024;
	odb->buffer = (char*)malloc(odb->buffer_size);

	MarsTask task(req, env);

	try {
		timer_start(index);
        ODBRetriever retriever;
        odb->f = retriever.retrieve(task)->openf("r", true);
		setvbuf(odb->f,odb->buffer,_IOFBF,odb->buffer_size);

		timer_stop(index, 0);
	}
	catch(std::exception& e) {
		marslog(LOG_EROR,"ODB open error %s", e.what());
		return -2;
	}

	return 0;
}

static err  odb_close(void *data)
{
	odbdata *odb = (odbdata*)data;
	fclose(odb->f);

	timer_partial_rate(odb->odb_timer,odb->start_time, odb->total_read);

	// free(odb->buffer);
	return 0;
}


static err  odb_read(void *data, request *r, void *buffer, long *length)
{
	odbdata *odb = (odbdata*)data;
//	return timed_wind_next(odb->u_v, odb->f, (char *) buffer, length, odb->odb_timer);
}

static err  odb_write(void *data,request *r,void *buffer,long *length)
{
	marslog(LOG_EXIT,"ODB write not implemeted");
	return EOF;
}

static boolean  odb_check(void *data,request *r)
{
	if(track(r))
		return true;

	if(is_bufr(r) || image(r))
		return false;

	return true;
}


#else /* ODBDIRECT */

extern "C" {

	base_class *odbdirect = &_nullbase;

    void mars_odb_init(int *argc, char **argv) {
	}

} // extern "C"

#endif /* NOODB */
