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

	char	*source;
	boolean	obs;
	wind    *u_v;
	hypercube *h;

	FILE	*s;
	void    *buffer;

}flatfiledata;

static void flatfile_init(void);
static err  flatfile_open(void *data,request*,request*,int);
static err  flatfile_close(void *data);
static err  flatfile_read(void *data,request *r,void *buffer,long *length);
static err  flatfile_write(void *data,request *r,void *buffer,long *length);


static base_class _flatfilebase = {

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

	false,                      /* inited       */

	sizeof(flatfiledata),       /* private size */
	0,                          /* option count */
	NULL,                       /* options      */



	flatfile_init,              /* init         */

	flatfile_open,              /* open         */
	flatfile_close,             /* close        */

	flatfile_read,              /* read         */
	flatfile_write,             /* write        */

};

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

base_class *flatfilebase = &_flatfilebase;


static void flatfile_init(void)
{
}

static err flatfile_open(void *data,request *r,request *e,int mode)
{
	flatfiledata *g = (flatfiledata*)data;

	g->obs = observation(r) || track(r);

	if(g->source == NULL && r != NULL )
		g->source = strcache(no_quotes(get_value(r,"SOURCE",0)));

	g->h = new_hypercube_from_mars_request(r);

	if(g->source != NULL)
	{
		marslog(LOG_DBUG,"Trying to open flatfile %s",g->source);

		if( (g->s = fopen(g->source,"r")) )
		{
			marslog(LOG_INFO,"Opening file '%s'",g->source);
			/* Modify disk I/O buffer, if application buffer allows for that */
			if(mars.readdisk_buffer > 0)
			{
				if(g->buffer == NULL)
					g->buffer = reserve_mem(mars.readdisk_buffer);
				marslog(LOG_DBUG,"Setting I/O read buffer to %d bytes",mars.readdisk_buffer);
				if(setvbuf(g->s,g->buffer,_IOFBF,mars.readdisk_buffer))
					marslog(LOG_WARN|LOG_PERR,"setvbuf failed");
			}
		}
		else
			marslog(LOG_EROR|LOG_PERR,"Error opening file '%s'",g->source);
	}

	if(!g->obs)
		g->u_v = wind_new(r,0,1);

	return g->s == NULL ? -1 : 0;
}

static err  flatfile_close(void *data)
{
	flatfiledata *g = (flatfiledata*)data;
	if(g->s) fclose(g->s);
	strfree(g->source);

	if(g->u_v)
		wind_free(g->u_v);
	if(g->h)
		free_hypercube(g->h);

	if(mars.readdisk_buffer)
		if(g->buffer != NULL)
		{
			release_mem(g->buffer);
			g->buffer = NULL;
		}

	return 0;
}

static err readmany(struct flatfiledata *data,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(data->s);

	err e = 0;
	*len = 0;

	n = a - pad;
	while(a > 10240 && (e = _readbufr(data->s,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(data->s);
	}

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

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

	return e;
}

/* Reads next field contained in request 'r' */
static err next_field(flatfiledata *g, request *r,void *buffer,long *length)
{
	err  ret   = 0;
	boolean match = false;
	long len   = *length;
	timer *fileread_time  = get_timer("Read from file","readfiletime",true);

	while((ret == 0) && !match)
	{
		*length = len;
		ret = wind_next(g->u_v,g->s,buffer,length,fileread_time);
		if(ret == NOERR)
		{
			request *u = empty_request(0);

			grib_to_request(u,buffer,*length);

			if(cube_order(g->h,u) != -1)
				match = true;
			free_all_requests(u);
		}
	}

	return ret;
}


static err  flatfile_read(void *data,request *r,void *buffer,long *length)
{
	flatfiledata *g = (flatfiledata*)data;
	err ret;

	if(g->obs)
		ret = _readany(g->s,buffer,length);
	else
		ret = next_field(g,r,buffer,length);

	if(ret != 0 && ret != -3)
		*length = 0;

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

static err  flatfile_write(void *data,request *r,void *buffer,long *length)
{
	return -1;
}
