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

/*

B.Raoult
ECMWF Oct-93

*/

#include "mars.h"
#include "base.h"
#ifdef sgi
#include <bstring.h>
#endif

/* extern base_class *cachebase; */
extern base_class *netbase;
extern base_class *nfdbbase;
extern base_class *filebase;
extern base_class *fdb5base;
extern base_class *forwardbase;
extern base_class *targetbase;
extern base_class *nullbase;
extern base_class *dhsbase;
extern base_class *flatfilebase;
extern base_class *multibase;
extern base_class *apibase;
extern base_class *odbbase;
extern base_class *odbdirect;

static base_class **bases[] = {
	&nullbase,
	&targetbase,
	&filebase,
#if defined(ECMWF) && !defined(NOFDB)
	&nfdbbase,
#endif
#if defined(ECMWF) && !defined(NOFDB5)
    &fdb5base,
#endif
#if defined(ECMWF) && defined(ODBDIRECT)
    &odbdirect,
#endif
    &odbbase,
	&netbase,
	/* &cachebase, */
	&dhsbase,
	&flatfilebase,
    &multibase,
    &forwardbase,
    &apibase,
};

static database* open_db = 0;

base_class *base_class_by_name(const char *name)
{
	int i;

	for(i=0;i<NUMBER(bases);i++) {
		if(strcasecmp(name,(*bases[i])->name)==0) {
			return *bases[i];
			}
	}

	marslog(LOG_EROR,"Cannot find base class named '%s'",name);
	/* return NULL; */
	return nullbase; /* return nullbase, so request can progress */
}

static database* new_database(base_class *driver,const char *name)
{
	database   *b;

	if(driver == NULL) return NULL;

	b = NEW_CLEAR(database);

	if(!driver->inited)
	{
		if(driver->init) driver->init();
		driver->inited = true;
	}

	b->next   = open_db;
	b->driver = driver;
	b->name   = strcache(name);

	if(driver->private_size)
	{
		b->data   = MALLOC(driver->private_size);
		bzero(b->data,(driver->private_size));

		get_options(driver->name,name,
		    b->data,driver->options_count,driver->options);
	}

	open_db = b;

	return b;
}

static void free_database(database *b)
{
	database *p = open_db;
	database *q = 0;
	while(p)
	{
		if(p == b)
		{
			if(q) q->next = b->next;
			else  open_db = b->next;
			break;
		}
		q = p;
		p = p->next;
	}

	if(b->data) FREE(b->data);
	strfree(b->name);
	FREE(b);
}

database *database_of(void *data)
{
	database* d = open_db;
	while(d)
	{
		if(d->data == data)
			return d;
		d = d->next;
	}
	return 0;
}

const char* database_name(void *data)
{
	database* d = database_of(data);
	return d?d->name:"<unknown>";
}


err database_validate(base_class *driver,const char *name,
	request *r,request *e,int mode)
{
	int ret = 0;


	if(ret == 0)
	{
		database  *b = new_database(driver,name);
		if(!b) return -1;
		if(driver->validate != NULL && 
				driver->validate(b->data,r,e,mode) != NOERR)
					ret = -1;
		 free_database(b);
	}
	 return ret;
}

typedef struct validate_data {
    char* file;
    char* only;
} validate_data;

static option validate_opts[] = {
	{"rules",NULL,NULL,NULL, t_str,sizeof(char*),OFFSET(validate_data,file),},
	{"only",    NULL,NULL,NULL, t_str,sizeof(char*),OFFSET(validate_data,only),},
};

database *database_open(base_class *driver,const char *name,
request *r,request *e,int mode)
{
	database  *b;
	validate_data data;	

	if(name)
	{
		get_options(driver->name,name,&data,NUMBER(validate_opts),
			validate_opts);

		if(data.only)
		{
			char h[80];
			gethostname(h,sizeof(h));
			if(strncmp(data.only,h,strlen(data.only)) != 0)
				return NULL;
		}

		if(data.file)
		{
			rule *rules = read_check_file(config_file(data.file));
			int e = check_one_request(rules,r)?NOERR:-1;
			free_rule(rules);
			if(e) return NULL;
		}
	}

	b = new_database(driver,name);
	if(!b) return NULL;

	if(driver->validate != NULL && driver->validate(b->data,r,e,mode) != NOERR)
	{
		free_database(b);
		return NULL;
	}

	if(driver->open(b->data,r,e,mode) != NOERR)
	{
		database_close(b);
		return NULL;
	}

	return b;
}

err database_read(database *b,request *r,void *buffer,long *length)
{
	return b->driver->read(b->data,r,buffer,length);
}

err database_write(database *b,request *r,void *buffer,long *length)
{
	return b->driver->write(b->data,r,buffer,length);
}

err database_control(database *b,int code,void *param,int size)
{
	if( b->driver->control)
		return b->driver->control(b->data,code,param,size);
	else
		return -1;
}

err database_archive(database *b,request* r)
{
	if( b->driver->archive)
		return b->driver->archive(b->data,r);
	else
		return feed(b,r);
}


boolean database_check(database *b,request *r)
{
	if(b->driver->check == NULL) return true;
	return b->driver->check(b->data,r);
}

err database_close(database *b)
{
	err ret =  b->driver->close(b->data);
	free_database(b);
	return ret;
}

void database_admin(base_class *driver)
{
	if(driver->admin != NULL)
	{
		database *b = database_open(driver,NULL,NULL,NULL,ADMIN_MODE);
		if(b != 0)
		{
			driver->admin(b->data);
			database_close(b);
		}
	}
}
