/* ARISA - generic settings structure manipulation functions
 * Copyright (C) 2004 Carl Ritson
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

#include "arisa.h"

#define SET_FUNC(x) \
	static int set_##x(setting_t *setting, void *data, value_t *var)

#define GET_PTR(type,set,ptr) ((type)(((char*)ptr) + set->offset)) 
	
SET_FUNC(int) {
	int *i = GET_PTR(int*,setting,data);
	int min = 0, max = 0;
	int flags = VALUE_USE_DEFAULT | setting->flags;
	
	if(setting->min != NULL) {
		min = atoi(setting->min);
		flags |= VALUE_USE_MIN;
	}
	if(setting->max != NULL) {
		max = atoi(setting->max);
		flags |= VALUE_USE_MAX;
	}
	*i = value_as_int32(var,flags,min,max,*i);
	return 0;
}

SET_FUNC(uint) {
	unsigned int *i = GET_PTR(unsigned int*,setting,data);
	unsigned int min = 0, max = 0;
	int flags = VALUE_USE_DEFAULT | setting->flags;
	
	if(setting->min != NULL) {
		min = (unsigned int)strtoul(setting->min,NULL,10);
		flags |= VALUE_USE_MIN;
	}
	if(setting->max != NULL) {
		max = (unsigned int)strtoul(setting->max,NULL,10);
		flags |= VALUE_USE_MAX;
	}
	*i = value_as_uint32(var,flags,min,max,*i);
	return 0;
}

SET_FUNC(float) {
	float *f = GET_PTR(float*,setting,data);
	float min = 0, max = 0;
	int flags = VALUE_USE_DEFAULT | setting->flags;
	
	if(setting->min != NULL) {
		min = strtof(setting->min,NULL);
		flags |= VALUE_USE_MIN;
	}
	if(setting->max != NULL) {
		max = strtof(setting->max,NULL);
		flags |= VALUE_USE_MAX;
	}
	*f = value_as_float(var,flags,min,max,*f);
	return 0;
}

SET_FUNC(double) {
	double *d = GET_PTR(double*,setting,data);
	double min = 0, max = 0;
	int flags = VALUE_USE_DEFAULT | setting->flags;
	
	if(setting->min != NULL) {
		min = strtod(setting->min,NULL);
		flags |= VALUE_USE_MIN;
	}
	if(setting->max != NULL) {
		max = strtod(setting->max,NULL);
		flags |= VALUE_USE_MAX;
	}
	*d = value_as_double(var,flags,min,max,*d);
	return 0;
}

SET_FUNC(str) {
	char **s = GET_PTR(char**,setting,data);
	*s = value_as_string(var,VALUE_FREE_DEFAULT | setting->flags,NULL,0,*s);
	return 0;
}

SET_FUNC(nstr) {
	char buffer[16];
	char **s = GET_PTR(char**,setting,data);
	if(strcmp(value_as_string(var,0,buffer,sizeof(buffer),NULL),"-") == 0) {
		if(*s != NULL)
			xfree(*s);
		*s = NULL;
	} else
		*s = value_as_string(var,VALUE_FREE_DEFAULT | VALUE_USE_DEFAULT
				| VALUE_ALLOW_NULL | setting->flags,NULL,0,*s);
	return 0;
}

SET_FUNC(bool) {
	int *i = GET_PTR(int*,setting,data);
	*i = value_as_bool(var,VALUE_USE_DEFAULT | setting->flags,*i);
	return 0;
}

SET_FUNC(enum) {
	int *i = GET_PTR(int*,setting,data);
	*i = value_as_enum(var,VALUE_USE_DEFAULT | setting->flags,*i);
	return 0;
}

SET_FUNC(off) {
	off_t *o = GET_PTR(off_t*,setting,data);
	off_t min = 0, max = 0;
	int flags = VALUE_USE_DEFAULT | setting->flags;
	
	if(setting->min != NULL) {
		min = (off_t)strtoull(setting->min,NULL,10);
		flags |= VALUE_USE_MIN;
	}
	if(setting->max != NULL) {
		max = (off_t)strtoull(setting->max,NULL,10);
		flags |= VALUE_USE_MAX;
	}
	*o = (off_t)value_as_uint64(var,flags,min,max,(uint64_t)*o);
	return 0;
}
	

static setting_t *find_setting(setting_t *settings, value_t *var) {
	setting_t *ret = NULL;
	size_t namelen = strlen(var->name);
	int i,match;
	
	for(i = 0, match = 0; settings[i].name != NULL; ++i) {
		if(strncasecmp(settings[i].name,var->name,namelen) == 0) {
			ret = &(settings[i]);
			match++;
			if(strlen(settings[i].name) == namelen) {
				match = 1;
				break;
			}
		}
	}

	if(match == 1)
		return ret;
	else
		return NULL;
}

int apply_setting(setting_t *settings, void *data, value_t *var) {
	setting_t *setting = find_setting(settings,var);
	int ret = -1;
	
	if(setting == NULL)
		return -1;
	
	switch(setting->type) {
		case ST_INT:	ret = set_int(setting,data,var);	break;
		case ST_UINT:	ret = set_uint(setting,data,var);	break;
		case ST_FLOAT:	ret = set_float(setting,data,var);	break;
		case ST_DOUBLE:	ret = set_double(setting,data,var);	break;
		case ST_STR:	ret = set_str(setting,data,var);	break;
		case ST_NSTR:	ret = set_nstr(setting,data,var);	break;
		case ST_BOOL:	ret = set_bool(setting,data,var);	break;
		case ST_ENUM:	ret = value_set_enumeration(var,
						setting->str_to_enum,
						setting->enum_to_str);
				if(ret != -1)
					ret = set_enum(setting,data,var);	
				break;
		case ST_OFF:	ret = set_off(setting,data,var);	break;
		default:
				assert(0); break;
	}
	
	return ret;
}

int read_settings(setting_t *settings, void *data, pqueue_t *out) {
	int i;

	for(i = 0; settings[i].name != NULL; ++i) {
		setting_t *s = &(settings[i]);
		value_t *v;
		
		switch(s->type) {
			case ST_INT: 
				v = value_int32(s->name,*GET_PTR(int*,s,data));
				break;
			case ST_UINT:
				v = value_uint32(s->name,
					*GET_PTR(unsigned int*,s,data));
				break;
			case ST_FLOAT:
				v = value_float(s->name,
					*GET_PTR(float*,s,data));
				break;
			case ST_DOUBLE:
				v = value_double(s->name,
					*GET_PTR(double*,s,data));
				break;
			case ST_STR:
			case ST_NSTR:
				v = value_string(s->name,
					*GET_PTR(char**,s,data));
				break;
			case ST_BOOL:
				v = value_bool(s->name,s->flags,
					*GET_PTR(int*,s,data));
				break;
			case ST_ENUM:
				v = value_enum_val(s->name,
					*GET_PTR(int*,s,data),s->enum_to_str);
				break;
			case ST_OFF:
				v = value_uint64(s->name,(uint64_t)
					*GET_PTR(off_t*,s,data));
				break;
			default:
				assert(0);
		}
		pqueue_push_back(out,v);
	}

	return i;
}

/** Settings Data for settings_t (global settings structure) **/
static setting_t global_settings_data[] = {
	{"admin-socket",	ST_NSTR,OFFSETOF(settings_t,admin_socket),
		0,NULL,NULL,NULL,NULL},
	{"autosave-interval",	ST_INT,	OFFSETOF(settings_t,autosave_interval),
		0,"30",NULL,NULL,NULL},
	{"config",		ST_STR,	OFFSETOF(settings_t,config),
		0,NULL,NULL,NULL,NULL},
	{"hash-packs",		ST_BOOL,OFFSETOF(settings_t,hash_packs),
		VALUE_BOOL_YESNO,NULL,NULL,NULL,NULL},
	{"hashing-io-usage",	ST_INT,	OFFSETOF(settings_t,hashing_io_usage),
		0,"1","100",NULL,NULL},
	{"list-frequency",	ST_INT,	OFFSETOF(settings_t,list_frequency),
		0,"0",NULL,NULL,NULL},
	{"log",			ST_NSTR,OFFSETOF(settings_t,log),
		0,NULL,NULL,NULL,NULL},
	{"log-interval",	ST_INT,	OFFSETOF(settings_t,log_interval),
		0,"1",NULL,NULL,NULL},
	{"pid",			ST_NSTR,OFFSETOF(settings_t,pid),
		0,NULL,NULL,NULL,NULL},
	{"tick-interval",	ST_INT,	OFFSETOF(settings_t,tick_interval),
		0,"0",NULL,NULL,NULL},
	{NULL}
};

int global_apply_setting(const char *name, const char *value) {
	value_t *v = value_string(name,value);
	int ret;
	LOCK(global->settings);
	ret = apply_setting(global_settings_data,global->settings,v);
	UNLOCK(global->settings);
	xfree(v);
	return ret;
}

int global_read_settings(pqueue_t *out) {
	int ret;
	LOCK(global->settings);
	ret = read_settings(global_settings_data,global->settings,out);
	UNLOCK(global->settings);
	return ret;
}
