/*
 * serverconfig.c
 * Configuration for the rdf server.
 *
 * Copyright (C) Leni Mayo 2002
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License, version 2, as
 *	published by the Free Software Foundation.
 *	
 *	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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdlib.h>
#include <string.h> /* for strchr */
#include <ctype.h>
#include <errno.h>
#include "common.h"
#include "log.h"
#include "serverconfig.h"

typedef enum
{
	eTypeString,     /* expect a string */
	eTypeBool        /* expect a yes/no */
} ConfigKeyType;

typedef struct
{
	ConfigKey     key;              /* the key */
	ConfigKeyType type;             /* it's type */
	const char    *pszName;         /* the key if settable via the config file, NULL otherwise */
	      char    *pszValue;        /* value provided via the config file  */
	const char    *pszDefaultValue; /* default value */
} Pent; /* so-called because it has five fields */

static       Pent *pent(ConfigKey key);
static const char *pentValue(ConfigKey key);
static int boolFromString(const char *psz);
static int isStringBool(const char *psz);
static ConfigKey keyFromString(const char *psz);
static void setPentValue(ConfigKey key, const char *pszValue);
static void splitStringIntoKeyValue(char *psz, char **ppszKey, char **ppszValue);
static char * stripLeadingAndTrailingWhitespace(char *pszInput);

Pent *pent(ConfigKey key)
{
	static Pent table[eConfigKeyLast] = 
	{
	{ eConfigKeyRdfDbIgnore,     eTypeBool,   "dbignore",     NULL,   "no"  },
	{ eConfigKeyRdfDbDirectory,  eTypeString, "dbdirectory",  NULL,   "/var/lib/serel/" }, /* trailing / is optional for librdf_new_storage() */
	{ eConfigKeyRdfDbFormat,     eTypeString, NULL,           NULL,   "bdb" },
	{ eConfigKeyRdfParser,       eTypeString, NULL,           NULL,   "repat" },
	{ eConfigKeyServiceUriSeq,   eTypeString, "rdfpath",      NULL,   "/etc/serel/local.rdf;/etc/serel/default.rdf" },
	{ eConfigKeyBaseUri,         eTypeString, NULL,           NULL,   "http://localhost" },
	{ eConfigKeyMangleDollar,    eTypeString, "mangledollar", NULL,   "strip" }
	};

	assert(key >= 0 && key < eConfigKeyLast);

	return &(table[key]);
}

/*
 * void configInitialise()
 *	This routine is basically:
 *	- read the file
 *	- parse it into key=value pairs
 *	- for the keys that match, set a value
 *	- generate warnings for unknown and badly typed keys.
 */
void configInitialise(const char *pszFilename)
{
	FILE *fp = NULL;
	int linenum = 0;
	int i;

	/* just in case someone edits the table and forgets to reset the enum values
	*/
	for (i = 0; i < eConfigKeyLast; i++)
	{
		assert(pent(i)->key == i);
	}

	if (pszFilename)
	{
		fp = fopen(pszFilename, "r");

		if (fp == NULL)
		{
			const char *pszFormat = "Error: unable to open config file for reading: %s - %s.  Relying on compile-time defaults...\n";

			fprintf(stderr, pszFormat, pszFilename, strerror(errno));

			printLog(eLogLevelError, pszFormat, pszFilename, strerror(errno));
		}
	}

	if (fp != NULL)
	{
		char buf[2000];

		while (!feof(fp))
		{
			buf[0] = '\0';

			fgets(buf, sizeof(buf), fp);
			linenum++;

			if (strlen(buf) > 0 && buf[strlen(buf) - 1] != '\n')
			{
				const char *pszFormat = "Warning at line %d, %s: line too long - ignoring.\n";

				fprintf(stderr, pszFormat, linenum, pszFilename);
			}
			else
			{
				ConfigKey key;
				char *pszKey;
				char *pszValue;
				
				splitStringIntoKeyValue(buf, &pszKey, &pszValue);

				if (pszKey == NULL || strlen(pszKey) == 0 || *pszKey == '#')
				{
					continue;
				}

				key = keyFromString(pszKey);

				if (key == eConfigKeyLast)
				{
					const char *pszFormat = "Warning at line %d, %s: ignoring unknown key: %s\n";
					fprintf(stderr, pszFormat, linenum, pszFilename, pszKey);
					continue;
				}

#ifdef TAKENOUT
				if (pent(key)->type == eTypeBool && g_slist_length(pList) != 2)
				{
					const char *pszFormat = "Warning at line %d, %s: expected a line of the form key=value.\n";
					fprintf(stderr, pszFormat, linenum, pszFilename);
					continue;
				}
#endif

				if (!pszValue)
					pszValue = "";

				if (pent(key)->type == eTypeBool && !isStringBool(pszValue))
				{
					const char *pszFormat = "Warning at line %d, %s: expected yes/no value with key: %s\n";
					fprintf(stderr, pszFormat, linenum, pszFilename, pszKey);
					continue;
				}

				setPentValue(key, pszValue);
			}
		}

		fclose(fp);
	}
}

void configCleanup()
{
	int i;

	for (i = 0; i < eConfigKeyLast; i++)
	{
		if (pent(i)->pszValue != NULL)
		{
			free(pent(i)->pszValue);
		}
	}
}

const char *pentValue(ConfigKey key)
{
	return pent(key)->pszValue ? pent(key)->pszValue : pent(key)->pszDefaultValue;
}

void setPentValue(ConfigKey key, const char *pszValue)
{
	if (pent(key)->pszValue)
	{
		free(pent(key)->pszValue);
	}

	pent(key)->pszValue = strdup(pszValue);
}

int boolFromString(const char *psz)
{
	assert(psz);

	return strcasecmp(psz, "yes") == 0;
}

int isStringBool(const char *psz)
{
	return strcasecmp(psz, "yes") == 0 || strcasecmp(psz, "no") == 0;
}

const char *configAsString(ConfigKey key)
{
	assert(pent(key)->type == eTypeString);

	return pentValue(key);
}

int configAsBool(ConfigKey key)
{
	assert(pent(key)->type == eTypeBool);

	return boolFromString(pentValue(key));
}

ConfigKey keyFromString(const char *psz)
{
	int i;
	ConfigKey ret = eConfigKeyLast;

	for (i = 0; i < eConfigKeyLast; i++)
	{
		if (pent(i)->pszName && strcasecmp(pent(i)->pszName, psz) == 0)
		{
			ret = i;
			break;
		}
	}

	return ret;
}

void splitStringIntoKeyValue(char *psz, char **ppszKey, char **ppszValue)
{
	char *p;

	assert(psz && ppszKey && ppszValue); 

	p = strchr(psz, '=');

	if (p == NULL)
	{
		*ppszKey = stripLeadingAndTrailingWhitespace(psz);
		*ppszValue = NULL;
	}
	else
	{
		*p = '\0';

		*ppszKey = stripLeadingAndTrailingWhitespace(psz);
		*ppszValue = stripLeadingAndTrailingWhitespace(p + 1);
	}
}

/* permutes the input string
*/
char * stripLeadingAndTrailingWhitespace(char *pszInput)
{
	char *pszStart = NULL;
	int i;
	int len = strlen(pszInput);

	for (i = 0; i < len; i++)
	{
		if (pszStart == NULL && ! isspace(pszInput[i]))
		{
			pszStart = &(pszInput[i]);
		}

		if (isspace(pszInput[i]))
		{
			pszInput[i] = '\0';
		}
	}

	return pszStart;
}
