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