/*****************************************************************************/

/*
 *      cfgunix.c  --  UNIX configuration file IO.
 *
 *      Copyright (C) 1999-2001  Thomas Sailer (t.sailer@alumni.ethz.ch)
 *
 *      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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/*****************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "baycomusb.h"

/* libxml includes */
#include <tree.h>
#include <parser.h>

/* ---------------------------------------------------------------------- */

#define MAXCONFIGENTRIES  32

static char configfile[256] = "/etc/ax25/baycomusb.conf";

static struct configentry configs[MAXCONFIGENTRIES];

/* ---------------------------------------------------------------------- */

void config_setfile(const char *name)
{
	if (!name)
		name = "/etc/ax25/baycomusb.conf";
	strncpy(configfile, name, sizeof(configfile));
}

const char *config_getfile(void)
{
	return configfile;
}

struct configentry *config_entry(unsigned int index)
{
	struct configentry *cfg;
	unsigned int i;

	for (cfg = &configs[0], i = 0; i < MAXCONFIGENTRIES; i++, cfg++) {
		if (!cfg->serial[0])
			continue;
		if (!index)
			return cfg;
		index--;
	}
	return NULL;
}

struct configentry *config_findentry(const char *ser)
{
	struct configentry *cfg;
	unsigned int i;

	for (cfg = &configs[0], i = 0; i < MAXCONFIGENTRIES; i++, cfg++) {
		if (!cfg->serial[0])
			continue;
		if (!strcmp(cfg->serial, ser))
			return cfg;
	}
	return NULL;
}

struct configentry *config_lookup(const char *ser)
{
	struct configentry *cfg;
	unsigned int i;

	for (cfg = &configs[0], i = 0; i < MAXCONFIGENTRIES; i++, cfg++) {
		if (!cfg->serial[0])
			continue;
		if (!strcmp(cfg->serial, ser))
			return cfg;
	}
	return &configs[0];
}
struct configentry *config_newentry(void)
{
	struct configentry *cfg;
	unsigned int i;

	for (cfg = &configs[1], i = 1; i < MAXCONFIGENTRIES && cfg->serial[0]; i++, cfg++);
	if (i >= MAXCONFIGENTRIES)
		return NULL;
	config_default(&configs[i]);
	return &configs[i];
}

int config_deleteentry(struct configentry *cfg)
{
	if (!cfg)
		return -1;
	if (cfg == &configs[0])
		return -1;
	cfg->serial[0] = 0;
	return 0;
}

/* ---------------------------------------------------------------------- */

static void parseone(xmlNodePtr node, struct configentry *cfg)
{
	const char *cp;

	for (; node; node = node->next) {
                if (!node->name) {
			lprintf(10, "Node has no name\n");
			continue;
		}
		if (!strcmp(node->name, "adapter")) {
			if ((cp = xmlGetProp(node, "mode"))) {
				if (!strcmp(cp, "fsk")) {
					cfg->adapter.mode = MODE_FSK;
				} else if (!strcmp(cp, "external")) {
					cfg->adapter.mode = MODE_EXTERNAL;
				} else if (!strcmp(cp, "afsk")) {
					cfg->adapter.mode = MODE_AFSK;
				} else if (!strcmp(cp, "audio")) {
					cfg->adapter.mode = MODE_AUDIO;
				} else if (!strcmp(cp, "bscan")) {
					cfg->adapter.mode = MODE_BSCAN;
				}
			}
			if ((cp = xmlGetProp(node, "fclk")))
				cfg->adapter.fclk = strtoul(cp, NULL, 0);
			/* legacy */
			if ((cp = xmlGetProp(node, "bitrate")))
				cfg->adapter.bitraterx = cfg->adapter.bitratetx = strtoul(cp, NULL, 0);
			if ((cp = xmlGetProp(node, "bitraterx")))
				cfg->adapter.bitraterx = strtoul(cp, NULL, 0);
			if ((cp = xmlGetProp(node, "bitratetx")))
				cfg->adapter.bitratetx = strtoul(cp, NULL, 0);
			if ((cp = xmlGetProp(node, "samplerate")))
				cfg->adapter.samplerate = strtoul(cp, NULL, 0);
			if ((cp = xmlGetProp(node, "loopback")))
				cfg->adapter.loopback = strtoul(cp, NULL, 0);
			if ((cp = xmlGetProp(node, "pttmute")))
				cfg->adapter.pttmute = !(cp[0] == '0' || cp[0] == 'n' || cp[0] == 'N');
			if ((cp = xmlGetProp(node, "filtmode")))
				cfg->adapter.filtmode = strtoul(cp, NULL, 0);
			if ((cp = xmlGetProp(node, "gain")))
				cfg->adapter.gain = strtoul(cp, NULL, 0);
		} else if (!strcmp(node->name, "chaccess")) {
			if ((cp = xmlGetProp(node, "txdelay")))
				cfg->chaccess.txdelay = strtoul(cp, NULL, 0);
			if ((cp = xmlGetProp(node, "slottime")))
				cfg->chaccess.slottime = strtoul(cp, NULL, 0);
			if ((cp = xmlGetProp(node, "ppersistence")))
				cfg->chaccess.ppersistence = strtoul(cp, NULL, 0);
			if ((cp = xmlGetProp(node, "txtail")))
				cfg->chaccess.txtail = strtoul(cp, NULL, 0);
			if ((cp = xmlGetProp(node, "fullduplex")))
				cfg->chaccess.fullduplex = !(cp[0] == '0' || cp[0] == 'n' || cp[0] == 'N');
		} else if (!strcmp(node->name, "interface")) {
			if ((cp = xmlGetProp(node, "ifname")))
				strncpy(cfg->ifcfg.ifname, cp, sizeof(cfg->ifcfg.ifname));
			if ((cp = xmlGetProp(node, "hwaddr")))
				strncpy(cfg->ifcfg.hwaddr, cp, sizeof(cfg->ifcfg.hwaddr));
			if ((cp = xmlGetProp(node, "ipaddr")))
				strncpy(cfg->ifcfg.ipaddr, cp, sizeof(cfg->ifcfg.ipaddr));
			if ((cp = xmlGetProp(node, "netmask")))
				strncpy(cfg->ifcfg.netmask, cp, sizeof(cfg->ifcfg.netmask));
			if ((cp = xmlGetProp(node, "broadcast")))
				strncpy(cfg->ifcfg.broadcast, cp, sizeof(cfg->ifcfg.broadcast));
		} else if (!strcmp(node->name, "modemdisc")) {
			if ((cp = xmlGetProp(node, "output")))
				cfg->mdisc.output = strtoul(cp, NULL, 0);
			if ((cp = xmlGetProp(node, "direction")))
				cfg->mdisc.direction = strtoul(cp, NULL, 0);
			if ((cp = xmlGetProp(node, "rxc")))
				cfg->mdisc.rxc = strtoul(cp, NULL, 0);
			if ((cp = xmlGetProp(node, "txc")))
				cfg->mdisc.txc = strtoul(cp, NULL, 0);
			if ((cp = xmlGetProp(node, "txd")))
				cfg->mdisc.txd = strtoul(cp, NULL, 0);
		} else if (!strcmp(node->name, "t7f")) {
			if ((cp = xmlGetProp(node, "ctrl")))
				cfg->t7f.ctrl =strtoul(cp, NULL, 0) ;
		} else {
			lprintf(10, "Unknown node %s\n", node->name);
			continue;
		}
	}
}

void config_default(struct configentry *cfg)
{
	cfg->serial[0] = 0;
	cfg->adapter.mode = MODE_FSK;
	cfg->adapter.fclk = 19666600;
	cfg->adapter.bitraterx = cfg->adapter.bitratetx = 9600;
	cfg->adapter.samplerate = 8000;
	cfg->adapter.loopback = 0;
	cfg->adapter.pttmute = 1;
	cfg->adapter.filtmode = 0;
	cfg->adapter.gain = 0;
	cfg->chaccess.txdelay = 150;
	cfg->chaccess.slottime = 100;
	cfg->chaccess.ppersistence = 40;
	cfg->chaccess.txtail = 10;
	cfg->chaccess.fullduplex = 0;
	strncpy(cfg->ifcfg.ifname, "bcu0", sizeof(cfg->ifcfg.ifname));
	strncpy(cfg->ifcfg.hwaddr, "N0CALL", sizeof(cfg->ifcfg.hwaddr));
	strncpy(cfg->ifcfg.ipaddr, "10.0.0.1", sizeof(cfg->ifcfg.ipaddr));
	strncpy(cfg->ifcfg.netmask, "255.255.255.0", sizeof(cfg->ifcfg.netmask));
	strncpy(cfg->ifcfg.broadcast, "10.0.0.255", sizeof(cfg->ifcfg.broadcast));
	cfg->mdisc.output = 0;
	cfg->mdisc.direction = 0xff;
	cfg->mdisc.rxc = 0;
	cfg->mdisc.txc = 0;
	cfg->mdisc.txd = 0;
	cfg->t7f.ctrl = 0;
}

int config_parse(void)
{
	xmlDocPtr doc;
	xmlNodePtr node;
	struct configentry *cfg;
	unsigned int i;
	const char *cp;

	/* default configuration */
	for (i = 0; i < MAXCONFIGENTRIES; i++) {
		cfg = &configs[i];
		config_default(cfg);
	}
	strncpy(configs[0].serial, "default", sizeof(configs[0].serial));
	/* parse config file */
	doc = xmlParseFile(configfile);
	if (!doc)
		goto err;
	if (strcmp(doc->root->name, "baycomusb")) {
		lprintf(10, "Config file does not contain baycomusb data\n");
		goto err2;
	}
	for (node = doc->root->childs; node; node = node->next) {
                if (!node->name || strcmp(node->name, "device"))
                        continue;
		cp = xmlGetProp(node, "serial");
		if (!cp || !strcmp(cp, "default"))
			cfg = &configs[0];
		else {
			cfg = config_findentry(cp);
			if (!cfg) {
				cfg = config_newentry();
				if (!cfg) {
					lprintf(10, "Too many configurations, ignoring serial %s\n", cp);
					continue;
				}
				strncpy(cfg->serial, cp, sizeof(cfg->serial));
			}
		}
		parseone(node->childs, cfg);
	}
	xmlFreeDoc(doc);
	return 0;

 err2:
	xmlFreeDoc(doc);
 err:
	return -1;
}

/* ---------------------------------------------------------------------- */

int config_save(void)
{
	xmlDocPtr doc;
	xmlNodePtr node, node2;
	struct configentry *cfg;
	unsigned int i;
	char buf[128];

	if (!(doc = xmlNewDoc("1.0")))
		return -1;
	doc->root = xmlNewDocNode(doc, NULL, "baycomusb", NULL);
	for (i = 0, cfg = configs; i < MAXCONFIGENTRIES; i++, cfg++) {
		if (!cfg->serial[0])
			continue;
		node = xmlNewChild(doc->root, NULL, "device", NULL);
		xmlSetProp(node, "serial", cfg->serial);
		node2 = xmlNewChild(node, NULL, "adapter", NULL);
		xmlSetProp(node2, "mode", 
			   (cfg->adapter.mode == MODE_EXTERNAL) ? "external" :
			   (cfg->adapter.mode == MODE_AFSK) ? "afsk" :
			   (cfg->adapter.mode == MODE_AUDIO) ? "audio" : 
			   (cfg->adapter.mode == MODE_BSCAN) ? "bscan" : "fsk");
		snprintf(buf, sizeof(buf), "%d", cfg->adapter.fclk);
		xmlSetProp(node2, "fclk", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->adapter.bitraterx);
		xmlSetProp(node2, "bitraterx", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->adapter.bitratetx);
		xmlSetProp(node2, "bitratetx", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->adapter.samplerate);
		xmlSetProp(node2, "samplerate", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->adapter.loopback);
		xmlSetProp(node2, "loopback", buf);
		snprintf(buf, sizeof(buf), "%d", !!cfg->adapter.pttmute);
		xmlSetProp(node2, "pttmute", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->adapter.filtmode);
		xmlSetProp(node2, "filtmode", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->adapter.gain);
		xmlSetProp(node2, "gain", buf);
		node2 = xmlNewChild(node, NULL, "chaccess", NULL);
		snprintf(buf, sizeof(buf), "%d", cfg->chaccess.txdelay);
		xmlSetProp(node2, "txdelay", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->chaccess.slottime);
		xmlSetProp(node2, "slottime", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->chaccess.ppersistence);
		xmlSetProp(node2, "ppersistence", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->chaccess.txtail);
		xmlSetProp(node2, "txtail", buf);
		snprintf(buf, sizeof(buf), "%d", !!cfg->chaccess.fullduplex);
		xmlSetProp(node2, "fullduplex", buf);
		node2 = xmlNewChild(node, NULL, "interface", NULL);
		xmlSetProp(node2, "ifname", cfg->ifcfg.ifname);
		xmlSetProp(node2, "hwaddr", cfg->ifcfg.hwaddr);
		xmlSetProp(node2, "ipaddr", cfg->ifcfg.ipaddr);
		xmlSetProp(node2, "netmask", cfg->ifcfg.netmask);
		xmlSetProp(node2, "broadcast", cfg->ifcfg.broadcast);
		node2 = xmlNewChild(node, NULL, "modemdisc", NULL);
		snprintf(buf, sizeof(buf), "0x%02x", cfg->mdisc.output & 0xff);
		xmlSetProp(node2, "output", buf);
		snprintf(buf, sizeof(buf), "0x%02x", cfg->mdisc.direction & 0xff);
		xmlSetProp(node2, "direction", buf);
		snprintf(buf, sizeof(buf), "%u", cfg->mdisc.rxc);
		xmlSetProp(node2, "rxc", buf);
		snprintf(buf, sizeof(buf), "%u", cfg->mdisc.txc);
		xmlSetProp(node2, "txc", buf);
		snprintf(buf, sizeof(buf), "%u", cfg->mdisc.txd);
		xmlSetProp(node2, "txd", buf);
		node2 = xmlNewChild(node, NULL, "t7f", NULL);
		snprintf(buf, sizeof(buf), "0x%02x", cfg->t7f.ctrl & 0x1f);
		xmlSetProp(node2, "ctrl", buf);
	}
	if (!xmlSaveFile(configfile, doc)) 
		goto err;
	xmlFreeDoc(doc);
	return 0;

 err:
	xmlFreeDoc(doc);
	return -1;
}

/* ---------------------------------------------------------------------- */
