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

/*
 *      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 "trx.h"

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

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

static char configfile[256] = "/etc/ax25/baycomusb.conf";
static xmlDocPtr doc;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

#define FALSE 0
#define TRUE 1

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

int config_load(const char *name)
{
	if (name)
		strncpy(configfile, name, sizeof(configfile));
	pthread_mutex_lock(&mutex);
	if (!(doc = xmlParseFile(configfile))) {
		if (!(doc = xmlNewDoc("1.0")))
			goto errmem;
		if (!(doc->root = xmlNewDocNode(doc, NULL, "baycomusb", NULL)))
			goto errmem;
		goto retok;
	}
	if (strcmp(doc->root->name, "baycomusb")) {
		lprintf(1, "Config file does not contain baycomusb data\n");
		pthread_mutex_unlock(&mutex);
		return -1;
	}
  retok:
	pthread_mutex_unlock(&mutex);
	return 0;

  errmem:
	lprintf(1, "Configfile: out of memory\n");
	pthread_mutex_unlock(&mutex);
	return -1;
}

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

int config_save(void)
{
	int r = 0;

	pthread_mutex_lock(&mutex);
	if (!xmlSaveFile(configfile, doc)) 
		r = -1;
	pthread_mutex_unlock(&mutex);
	return r;
}

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

static xmlNodePtr getserialnode(const char *serial)
{
	xmlNodePtr node;
	const char *cp;

	for (node = doc->root->childs; node; node = node->next) {
                if (!node->name || strcmp(node->name, "device"))
                        continue;
		cp = xmlGetProp(node, "serial");
		if (!cp)
			continue;
		if (strcmp(cp, serial))
			continue;
		return node;
	}
	node = xmlNewChild(doc->root, NULL, "device", NULL);
	if (node)
		xmlSetProp(node, "serial", serial);
	return node;
}

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

static xmlNodePtr getsectionnode(xmlNodePtr sernode, const char *secname)
{
	xmlNodePtr node;

	for (node = sernode->childs; node; node = node->next) {
                if (!node->name)
			continue;
		if (strcmp(node->name, secname))
			continue;
		return node;
	}
	node = xmlNewChild(sernode, NULL, secname, NULL);
	return node;
}

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

static void parseip(trxapi_ip_address_t addr, const char *cp)
{
        unsigned int r1, r2, r3, r4;
        
        if (!cp || sscanf(cp, "%u.%u.%u.%u", &r1, &r2, &r3, &r4) != 4) {
                addr[0] = addr[1] = addr[2] = addr[3] = 0;
                return;
        }
        addr[0] = r1;
        addr[1] = r2;
        addr[2] = r3;
        addr[3] = r4;
}

void config_loaddevice(struct trx_thread_state *state)
{
	xmlNodePtr sernode, node;
	const char *cp;

	/* set default */
	state->cfg.adapt.mode = trxapi_baycomusb_mode_fsk;
	state->cfg.adapt.fclk = 19666600;
	state->cfg.adapt.bitraterx = state->cfg.adapt.bitratetx = 9600;
	state->cfg.adapt.loopback = trxapi_baycomusb_loopback_off;
	state->cfg.adapt.pttmute = TRUE;
	state->cfg.adapt.filtmode = 0;
	state->cfg.adapt.samplerate = 8000;
	state->cfg.adapt.gain = 1;
	strncpy(state->cfg.adapt.audiodevin, "/dev/dsp", sizeof(state->cfg.adapt.audiodevin));
	strncpy(state->cfg.adapt.audiodevout, "/dev/dsp", sizeof(state->cfg.adapt.audiodevout));
	state->cfg.adapt.rfsquelch = RSSI_MIN;
	state->cfg.adapt.audiosquelch = FALSE;
	state->cfg.chacc.txdelay = 150;
	state->cfg.chacc.slottime = 100;
	state->cfg.chacc.ppersistence = 40;
	state->cfg.chacc.txtail = 10;
	state->cfg.chacc.fullduplex = FALSE;
	strncpy(state->cfg.intf.ifname, "bcu0", sizeof(state->cfg.intf.ifname));
	strncpy(state->cfg.intf.hwaddr, "N0CALL", sizeof(state->cfg.intf.hwaddr));
	state->cfg.intf.ipaddr[0] = 10;
	state->cfg.intf.ipaddr[1] = 0;
	state->cfg.intf.ipaddr[2] = 0;
	state->cfg.intf.ipaddr[3] = 1;
	state->cfg.intf.netmask[0] = 255;
	state->cfg.intf.netmask[1] = 255;
	state->cfg.intf.netmask[2] = 255;
	state->cfg.intf.netmask[3] = 0;
	state->cfg.intf.broadcast[0] = 10;
	state->cfg.intf.broadcast[1] = 0;
	state->cfg.intf.broadcast[2] = 0;
	state->cfg.intf.broadcast[3] = 255;
	state->cfg.intf.gwhwaddr[0] = 0;
	state->cfg.intf.gwipaddr[0] = 0;
	state->cfg.intf.gwipaddr[1] = 0;
	state->cfg.intf.gwipaddr[2] = 0;
	state->cfg.intf.gwipaddr[3] = 0;
	state->cfg.intf.gwnetmask[0] = 255;
	state->cfg.intf.gwnetmask[1] = 0;
	state->cfg.intf.gwnetmask[2] = 0;
	state->cfg.intf.gwnetmask[3] = 0;
	state->cfg.intf.gwipmode = ' ';
	state->cfg.mdisc.output = 0;
	state->cfg.mdisc.direction = 0xff;
	state->cfg.mdisc.rxc = trxapi_modem_disconnect_rxc_normal;
	state->cfg.mdisc.txc = trxapi_modem_disconnect_txc_normal;
	state->cfg.mdisc.txd = trxapi_modem_disconnect_txd_normal;
	pthread_mutex_lock(&mutex);
	if (!(sernode = getserialnode(state->serial))) {
		pthread_mutex_unlock(&mutex);
		return;
	}
	if ((node = getsectionnode(sernode, "adapter"))) {
		if ((cp = xmlGetProp(node, "mode"))) {
			if (!strcmp(cp, "fsk")) {
				state->cfg.adapt.mode = trxapi_baycomusb_mode_fsk;
			} else if (!strcmp(cp, "external")) {
				state->cfg.adapt.mode = trxapi_baycomusb_mode_external;
			} else if (!strcmp(cp, "afsk")) {
				state->cfg.adapt.mode = trxapi_baycomusb_mode_afsk;
			} else if (!strcmp(cp, "audio")) {
				state->cfg.adapt.mode = trxapi_baycomusb_mode_audio;
			}
		}
		if ((cp = xmlGetProp(node, "fclk")))
			state->cfg.adapt.fclk = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "bitrate")))
			state->cfg.adapt.bitraterx = state->cfg.adapt.bitratetx = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "bitraterx")))
			state->cfg.adapt.bitraterx = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "bitratetx")))
			state->cfg.adapt.bitratetx = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "loopback")))
			state->cfg.adapt.loopback = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "pttmute")))
			state->cfg.adapt.pttmute = (cp[0] == '0' || cp[0] == 'n' || cp[0] == 'N') ? FALSE : TRUE;
		if ((cp = xmlGetProp(node, "filtmode")))
			state->cfg.adapt.filtmode = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "samplerate")))
			state->cfg.adapt.samplerate = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "gain")))
			state->cfg.adapt.gain = strtod(cp, NULL);
		if ((cp = xmlGetProp(node, "audiodevin")))
			strncpy(state->cfg.adapt.audiodevin, cp, sizeof(state->cfg.adapt.audiodevin));
		if ((cp = xmlGetProp(node, "audiodevout")))
			strncpy(state->cfg.adapt.audiodevout, cp, sizeof(state->cfg.adapt.audiodevout));
		if ((cp = xmlGetProp(node, "rfsquelch")))
			state->cfg.adapt.rfsquelch = strtod(cp, NULL);
		if ((cp = xmlGetProp(node, "audiosquelch")))
			state->cfg.adapt.audiosquelch = (cp[0] == '0' || cp[0] == 'n' || cp[0] == 'N') ? FALSE : TRUE;
	}
	if ((node = getsectionnode(sernode, "chaccess"))) {
		if ((cp = xmlGetProp(node, "txdelay")))
			state->cfg.chacc.txdelay = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "slottime")))
			state->cfg.chacc.slottime = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "ppersistence")))
			state->cfg.chacc.ppersistence = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "txtail")))
			state->cfg.chacc.txtail = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "fullduplex")))
			state->cfg.chacc.fullduplex = (cp[0] == '0' || cp[0] == 'n' || cp[0] == 'N') ? FALSE : TRUE;
	}
	if ((node = getsectionnode(sernode, "interface"))) {
		if ((cp = xmlGetProp(node, "ifname")))
			strncpy(state->cfg.intf.ifname, cp, sizeof(state->cfg.intf.ifname));
		if ((cp = xmlGetProp(node, "hwaddr")))
			strncpy(state->cfg.intf.hwaddr, cp, sizeof(state->cfg.intf.hwaddr));
		if ((cp = xmlGetProp(node, "ipaddr")))
			parseip(state->cfg.intf.ipaddr, cp);
		if ((cp = xmlGetProp(node, "netmask")))
			parseip(state->cfg.intf.netmask, cp);
		if ((cp = xmlGetProp(node, "broadcast")))
			parseip(state->cfg.intf.broadcast, cp);
		if ((cp = xmlGetProp(node, "gwhwaddr")))
			strncpy(state->cfg.intf.gwhwaddr, cp, sizeof(state->cfg.intf.gwhwaddr));
		if ((cp = xmlGetProp(node, "gwipaddr")))
			parseip(state->cfg.intf.gwipaddr, cp);
		if ((cp = xmlGetProp(node, "gwnetmask")))
			parseip(state->cfg.intf.gwnetmask, cp);
		if ((cp = xmlGetProp(node, "gwipmode")))
			state->cfg.intf.gwipmode = *cp;
	}
	if ((node = getsectionnode(sernode, "modemdisc"))) {
		if ((cp = xmlGetProp(node, "output")))
			state->cfg.mdisc.output = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "direction")))
			state->cfg.mdisc.direction = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "rxc")))
			state->cfg.mdisc.rxc = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "txc")))
			state->cfg.mdisc.txc = strtoul(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "txd")))
			state->cfg.mdisc.txd = strtoul(cp, NULL, 0);
	}
	if ((node = getsectionnode(sernode, "frequency"))) {
		if ((cp = xmlGetProp(node, "rx")))
			state->cfg.freq.rx = strtoull(cp, NULL, 0);
		if ((cp = xmlGetProp(node, "tx")))
			state->cfg.freq.tx = strtoull(cp, NULL, 0);
	}
	pthread_mutex_unlock(&mutex);
}

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

void config_savedevice(struct trx_thread_state *state)
{
	xmlNodePtr sernode, node;
	char buf[128];

        if (!state->serial[0])
                return;
	pthread_mutex_lock(&mutex);
	if (!(sernode = getserialnode(state->serial))) {
		lprintf(1, "Config %s out of memory\n", state->serial);
		pthread_mutex_unlock(&mutex);
		return;
	}
	if ((node = getsectionnode(sernode, "adapter"))) {
		xmlSetProp(node, "mode", 
			   (state->cfg.adapt.mode == trxapi_baycomusb_mode_external) ? "external" :
			   (state->cfg.adapt.mode == trxapi_baycomusb_mode_afsk) ? "afsk" :
			   (state->cfg.adapt.mode == trxapi_baycomusb_mode_audio) ? "audio" : "fsk");
		snprintf(buf, sizeof(buf), "%ld", state->cfg.adapt.fclk);
		xmlSetProp(node, "fclk", buf);
		snprintf(buf, sizeof(buf), "%ld", state->cfg.adapt.bitraterx);
		xmlSetProp(node, "bitraterx", buf);
		snprintf(buf, sizeof(buf), "%ld", state->cfg.adapt.bitratetx);
		xmlSetProp(node, "bitratetx", buf);
		snprintf(buf, sizeof(buf), "%d", state->cfg.adapt.loopback);
		xmlSetProp(node, "loopback", buf);
		snprintf(buf, sizeof(buf), "%d", !!state->cfg.adapt.pttmute);
		xmlSetProp(node, "pttmute", buf);
		snprintf(buf, sizeof(buf), "%ld", state->cfg.adapt.filtmode);
		xmlSetProp(node, "filtmode", buf);
		snprintf(buf, sizeof(buf), "%ld", state->cfg.adapt.samplerate);
		xmlSetProp(node, "samplerate", buf);
		snprintf(buf, sizeof(buf), "%f", state->cfg.adapt.gain);
		xmlSetProp(node, "gain", buf);
		xmlSetProp(node, "audiodevin", state->cfg.adapt.audiodevin);
		xmlSetProp(node, "audiodevout", state->cfg.adapt.audiodevout);
		snprintf(buf, sizeof(buf), "%f", state->cfg.adapt.rfsquelch);
		xmlSetProp(node, "rfsquelch", buf);
		snprintf(buf, sizeof(buf), "%d", !!state->cfg.adapt.audiosquelch);
		xmlSetProp(node, "audiosquelch", buf);
	} else
		lprintf(1, "Config %s node adapter out of memory\n", state->serial);
	if ((node = getsectionnode(sernode, "chaccess"))) {
		snprintf(buf, sizeof(buf), "%ld", state->cfg.chacc.txdelay);
		xmlSetProp(node, "txdelay", buf);
		snprintf(buf, sizeof(buf), "%ld", state->cfg.chacc.slottime);
		xmlSetProp(node, "slottime", buf);
		snprintf(buf, sizeof(buf), "%ld", state->cfg.chacc.ppersistence);
		xmlSetProp(node, "ppersistence", buf);
		snprintf(buf, sizeof(buf), "%ld", state->cfg.chacc.txtail);
		xmlSetProp(node, "txtail", buf);
		snprintf(buf, sizeof(buf), "%d", !!state->cfg.chacc.fullduplex);
		xmlSetProp(node, "fullduplex", buf);
	} else
		lprintf(1, "Config %s node chaccess out of memory\n", state->serial);
	if ((node = getsectionnode(sernode, "interface"))) {
		xmlSetProp(node, "ifname", state->cfg.intf.ifname);
		xmlSetProp(node, "hwaddr", state->cfg.intf.hwaddr);
		snprintf(buf, sizeof(buf), "%u.%u.%u.%u", state->cfg.intf.ipaddr[0], state->cfg.intf.ipaddr[1],
			 state->cfg.intf.ipaddr[2], state->cfg.intf.ipaddr[3]);
		xmlSetProp(node, "ipaddr", buf);
		snprintf(buf, sizeof(buf), "%u.%u.%u.%u", state->cfg.intf.netmask[0], state->cfg.intf.netmask[1],
			 state->cfg.intf.netmask[2], state->cfg.intf.netmask[3]);
		xmlSetProp(node, "netmask", buf);
		snprintf(buf, sizeof(buf), "%u.%u.%u.%u", state->cfg.intf.broadcast[0], state->cfg.intf.broadcast[1],
			 state->cfg.intf.broadcast[2], state->cfg.intf.broadcast[3]);
		xmlSetProp(node, "broadcast", buf);
		xmlSetProp(node, "gwhwaddr", state->cfg.intf.gwhwaddr);
		snprintf(buf, sizeof(buf), "%u.%u.%u.%u", state->cfg.intf.gwipaddr[0], state->cfg.intf.gwipaddr[1],
			 state->cfg.intf.gwipaddr[2], state->cfg.intf.gwipaddr[3]);
		xmlSetProp(node, "gwipaddr", buf);
		snprintf(buf, sizeof(buf), "%u.%u.%u.%u", state->cfg.intf.gwnetmask[0], state->cfg.intf.gwnetmask[1],
			 state->cfg.intf.gwnetmask[2], state->cfg.intf.gwnetmask[3]);
		xmlSetProp(node, "gwnetmask", buf);
		snprintf(buf, sizeof(buf), "%c", state->cfg.intf.gwipmode);
		xmlSetProp(node, "gwipmode", buf);
	} else
		lprintf(1, "Config %s node interface out of memory\n", state->serial);
	if ((node = getsectionnode(sernode, "modemdisc"))) {
		snprintf(buf, sizeof(buf), "0x%02lx", state->cfg.mdisc.output & 0xff);
		xmlSetProp(node, "output", buf);
		snprintf(buf, sizeof(buf), "0x%02lx", state->cfg.mdisc.direction & 0xff);
		xmlSetProp(node, "direction", buf);
		snprintf(buf, sizeof(buf), "%u", state->cfg.mdisc.rxc);
		xmlSetProp(node, "rxc", buf);
		snprintf(buf, sizeof(buf), "%u", state->cfg.mdisc.txc);
		xmlSetProp(node, "txc", buf);
		snprintf(buf, sizeof(buf), "%u", state->cfg.mdisc.txd);
		xmlSetProp(node, "txd", buf);
	} else
		lprintf(1, "Config %s node modemdisc out of memory\n", state->serial);	
	if ((node = getsectionnode(sernode, "frequency"))) {
		snprintf(buf, sizeof(buf), "%llu", state->cfg.freq.rx);
		xmlSetProp(node, "rx", buf);
		snprintf(buf, sizeof(buf), "%llu", state->cfg.freq.tx);
		xmlSetProp(node, "tx", buf);
	} else
		lprintf(1, "Config %s node frequency out of memory\n", state->serial);
	pthread_mutex_unlock(&mutex);
}
