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