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

/*
 *      cfgwin32.c  --  Windows (registry) configuration IO.
 *
 *      Copyright (C) 2000-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"

#include <windows.h>

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

#define MAXCONFIGENTRIES  32

#define REGISTRYPATH "SOFTWARE\\FlexNet\\BaycomUSB"
#define REGHKEY      HKEY_LOCAL_MACHINE

static char configfile[256] = REGISTRYPATH;

static struct configentry configs[MAXCONFIGENTRIES];

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

void config_setfile(const char *name)
{
	if (!name)
		name = REGISTRYPATH;
	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 int deletekeyx(HKEY key)
{
        char name[128];
        LONG err;
        DWORD len;
        HKEY key2;
        int ret = 0;
        
        for (;;) {
                len = sizeof(name);
                if ((RegEnumKeyEx(key, 0, name, &len, NULL, NULL, NULL, NULL)) != ERROR_SUCCESS)
                        return ret;
                if ((err = RegOpenKeyEx(key, name, 0, KEY_ALL_ACCESS, &key2)) != ERROR_SUCCESS) {
                        lprintf(10, "RegOpenKeyEx(%s) returned 0x%lx\n", name, err);
                        return -1;
                }
                ret |= deletekeyx(key2);
                RegCloseKey(key2);
                RegDeleteKey(key, name);
        }
        return ret;
}

static int deletekey(const char *name)
{
        HKEY key;
        int ret;
        DWORD err;
        
        if ((err = RegOpenKeyEx(REGHKEY, name, 0, KEY_ALL_ACCESS, &key)) != ERROR_SUCCESS) {
                lprintf(10, "RegOpenKeyEx(%s) returned 0x%lx\n", name, err);
                return -1;
        }
        ret = deletekeyx(key);
        RegCloseKey(key);
        RegDeleteKey(REGHKEY, name);
        return ret;
}

static int setprop(const char *serial, const char *section, const char *propname, const char *value)
{
	char name[256];
        HKEY key;
        DWORD err;
        
	snprintf(name, sizeof(name), "%s\\%s\\%s", configfile, serial, section);
	if ((err = RegCreateKeyEx(REGHKEY, name, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL)) != ERROR_SUCCESS) {
		lprintf(10, "RegCreateKeyEx(%s,%s) returned 0x%lx\n", serial, section, err);
                return -1;
        }
        err = RegSetValueEx(key, propname, 0, REG_SZ, value, strlen(value)+1);
        RegCloseKey(key);
        if (err != ERROR_SUCCESS) {
                lprintf(10, "RegSetValueEx(%s,%s,%s,%s) returned 0x%lx\n", serial, section, propname, value, err);
                return -1;
        }
        return 0;
}

static int getprop(const char *serial, const char *section, const char *propname, char *value, size_t vsize)
{
	char name[256];
        HKEY key;
        DWORD err, vtype, len;
        
	snprintf(name, sizeof(name), "%s\\%s\\%s", configfile, serial, section);
        if ((err = RegCreateKeyEx(REGHKEY, name, 0, "", REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &key, NULL)) != ERROR_SUCCESS) {
		lprintf(10, "RegCreateKeyEx(%s,%s) returned 0x%lx\n", serial, section, err);
                return -1;
        }
        len = vsize;
        err = RegQueryValueEx(key, propname, NULL, &vtype, value, &len);
        RegCloseKey(key);
        if (err != ERROR_SUCCESS) {
                lprintf(10, "RegQueryValueEx(%s,%s,%s) returned 0x%lx\n", serial, section, propname, err);
		value[0] = 0;
                return -1;
        }
        if (vtype != REG_SZ) {
		value[0] = 0;
                return -1;
	}
        if (len >= vsize)
                len = vsize;
        value[len] = 0;
	return 0;
}

int config_setvalue(const char *propname, const char *value)
{
	char name[256];
        HKEY key;
        DWORD err;
        
	snprintf(name, sizeof(name), "%s\\misc", configfile);
	if ((err = RegCreateKeyEx(REGHKEY, name, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL)) != ERROR_SUCCESS) {
		lprintf(10, "RegCreateKeyEx(misc) returned 0x%lx\n", err);
                return -1;
        }
        err = RegSetValueEx(key, propname, 0, REG_SZ, value, strlen(value)+1);
        RegCloseKey(key);
        if (err != ERROR_SUCCESS) {
                lprintf(10, "RegSetValueEx(misc,%s,%s) returned 0x%lx\n", propname, value, err);
                return -1;
        }
        return 0;
}

int config_getvalue(const char *propname, char *value, size_t vsize)
{
	char name[256];
        HKEY key;
        DWORD err, vtype, len;

	snprintf(name, sizeof(name), "%s\\misc", configfile);
        if ((err = RegCreateKeyEx(REGHKEY, name, 0, "", REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &key, NULL)) != ERROR_SUCCESS) {
		lprintf(10, "RegCreateKeyEx(misc) returned 0x%lx\n", err);
                return -1;
        }
        len = vsize;
        err = RegQueryValueEx(key, propname, NULL, &vtype, value, &len);
        RegCloseKey(key);
        if (err != ERROR_SUCCESS) {
                lprintf(10, "RegQueryValueEx(misc,%s) returned 0x%lx\n", propname, err);
		value[0] = 0;
                return -1;
        }
        if (vtype != REG_SZ) {
		value[0] = 0;
                return -1;
	}
        if (len >= vsize)
                len = vsize;
        value[len] = 0;
	return 0;
}

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

static void parseone(struct configentry *cfg)
{
	char buf[128];

	if (!getprop(cfg->serial, "adapter", "mode", buf, sizeof(buf))) {
		if (!strcmp(buf, "fsk")) {
			cfg->adapter.mode = MODE_FSK;
		} else if (!strcmp(buf, "external")) {
			cfg->adapter.mode = MODE_EXTERNAL;
		} else if (!strcmp(buf, "afsk")) {
			cfg->adapter.mode = MODE_AFSK;
		} else if (!strcmp(buf, "audio")) {
			cfg->adapter.mode = MODE_AUDIO;
		} else if (!strcmp(buf, "bscan")) {
			cfg->adapter.mode = MODE_BSCAN;
		}
	}
	if (!getprop(cfg->serial, "adapter", "fclk", buf, sizeof(buf)))
		cfg->adapter.fclk = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "adapter", "bitrate", buf, sizeof(buf)))
		cfg->adapter.bitraterx = cfg->adapter.bitratetx = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "adapter", "bitraterx", buf, sizeof(buf)))
		cfg->adapter.bitraterx = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "adapter", "bitratetx", buf, sizeof(buf)))
		cfg->adapter.bitratetx = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "adapter", "samplerate", buf, sizeof(buf)))
		cfg->adapter.samplerate = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "adapter", "loopback", buf, sizeof(buf)))
		cfg->adapter.loopback = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "adapter", "pttmute", buf, sizeof(buf)))
		cfg->adapter.pttmute = !(buf[0] == '0' || buf[0] == 'n' || buf[0] == 'N');
	if (!getprop(cfg->serial, "adapter", "filtmode", buf, sizeof(buf)))
		cfg->adapter.filtmode = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "adapter", "gain", buf, sizeof(buf)))
		cfg->adapter.gain = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "chaccess", "txdelay", buf, sizeof(buf)))
		cfg->chaccess.txdelay = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "chaccess", "slottime", buf, sizeof(buf)))
		cfg->chaccess.slottime = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "chaccess", "ppersistence", buf, sizeof(buf)))
		cfg->chaccess.ppersistence = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "chaccess", "txtail", buf, sizeof(buf)))
		cfg->chaccess.txtail = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "chaccess", "fullduplex", buf, sizeof(buf)))
		cfg->chaccess.fullduplex = !(buf[0] == '0' || buf[0] == 'n' || buf[0] == 'N');
	if (!getprop(cfg->serial, "interface", "ifname", buf, sizeof(buf)))
		strncpy(cfg->ifcfg.ifname, buf, sizeof(cfg->ifcfg.ifname));
	if (!getprop(cfg->serial, "interface", "hwaddr", buf, sizeof(buf)))
		strncpy(cfg->ifcfg.hwaddr, buf, sizeof(cfg->ifcfg.hwaddr));
	if (!getprop(cfg->serial, "interface", "ipaddr", buf, sizeof(buf)))
		strncpy(cfg->ifcfg.ipaddr, buf, sizeof(cfg->ifcfg.ipaddr));
	if (!getprop(cfg->serial, "interface", "netmask", buf, sizeof(buf)))
		strncpy(cfg->ifcfg.netmask, buf, sizeof(cfg->ifcfg.netmask));
	if (!getprop(cfg->serial, "interface", "broadcast", buf, sizeof(buf)))
		strncpy(cfg->ifcfg.broadcast, buf, sizeof(cfg->ifcfg.broadcast));
	if (!getprop(cfg->serial, "modemdisc", "output", buf, sizeof(buf)))
		cfg->mdisc.output = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "modemdisc", "direction", buf, sizeof(buf)))
		cfg->mdisc.direction = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "modemdisc", "rxc", buf, sizeof(buf)))
		cfg->mdisc.rxc = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "modemdisc", "txc", buf, sizeof(buf)))
		cfg->mdisc.txc = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "modemdisc", "txd", buf, sizeof(buf)))
		cfg->mdisc.txd = strtoul(buf, NULL, 0);
	if (!getprop(cfg->serial, "t7f", "ctrl", buf, sizeof(buf)))
		cfg->t7f.ctrl =strtoul(buf, NULL, 0) ;
}

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)
{
        HKEY regkey;
        LONG err;
        DWORD len;
        DWORD index = 0;
        char buf[256];
	struct configentry *cfg;
	unsigned int i;

	/* default configuration */
	for (i = 0; i < MAXCONFIGENTRIES; i++) {
		cfg = &configs[i];
		config_default(cfg);
	}
	strncpy(configs[0].serial, "default", sizeof(configs[0].serial));
	/* enumerate registry keys */
        if ((err = RegOpenKeyEx(REGHKEY, configfile, 0, KEY_READ, &regkey)) != ERROR_SUCCESS) {
                lprintf(10, "RegOpenKeyEx(%s) returned 0x%lx\n", configfile, err);
                return -1;
        }
	for (;;) {
                len = sizeof(buf);
                if ((RegEnumKeyEx(regkey, index, buf, &len, NULL, NULL, NULL, NULL)) != ERROR_SUCCESS)
                        break;
                index++;
		if (!strcmp(buf, "default"))
			cfg = &configs[0];
		else if (buf[0] >= '0' && buf[0] <= '9') {
			cfg = config_findentry(buf);
			if (!cfg) {
				cfg = config_newentry();
				if (!cfg) {
					lprintf(10, "Too many configurations, ignoring serial %s\n", buf);
					continue;
				}
				strncpy(cfg->serial, buf, sizeof(cfg->serial));
			}
		}
		parseone(cfg);
#if 0
                lprintf(10, "Read Configuration: Serial %s, Mode %u\n", cfg->serial, cfg->adapter.mode);
#endif
        }
        RegCloseKey(regkey);
	return 0;
}

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

int config_save(void)
{
	struct configentry *cfg;
        HKEY regkey;
        LONG err;
        DWORD len, index;
        char buf[32];
	char buf2[256];
	unsigned int i;

	/* first delete all nonexisting serials */
  delloop:
	/* enumerate registry keys */
        if ((err = RegOpenKeyEx(REGHKEY, configfile, 0, KEY_READ, &regkey)) != ERROR_SUCCESS) {
                lprintf(10, "RegOpenKeyEx(%s) returned 0x%lx\n", configfile, err);
                goto dosave;
        }
	index = 0;
	for (;;) {
                len = sizeof(buf);
                if ((RegEnumKeyEx(regkey, index, buf, &len, NULL, NULL, NULL, NULL)) != ERROR_SUCCESS)
                        break;
                index++;
                if (buf[0] < '0' || buf[0] > '9')
                        continue;
		for (i = 0; i < MAXCONFIGENTRIES && (!configs[i].serial[0] || strcmp(buf, configs[i].serial)); i++);
		if (i >= MAXCONFIGENTRIES) {
			RegCloseKey(regkey);
			snprintf(buf2, sizeof(buf2), "%s\\%s", configfile, buf);
			deletekey(buf2);
			lprintf(10, "Deleting Key %s\n", buf2);
			goto delloop;
		}
	}
	RegCloseKey(regkey);
	/* save existing serials */
  dosave:
	for (i = 0, cfg = configs; i < MAXCONFIGENTRIES; i++, cfg++) {
		if (!cfg->serial[0])
			continue;
		setprop(cfg->serial, "adapter", "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);
		setprop(cfg->serial, "adapter", "fclk", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->adapter.bitraterx);
		setprop(cfg->serial, "adapter", "bitraterx", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->adapter.bitratetx);
		setprop(cfg->serial, "adapter", "bitratetx", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->adapter.samplerate);
		setprop(cfg->serial, "adapter", "samplerate", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->adapter.loopback);
		setprop(cfg->serial, "adapter", "loopback", buf);
		snprintf(buf, sizeof(buf), "%d", !!cfg->adapter.pttmute);
		setprop(cfg->serial, "adapter", "pttmute", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->adapter.filtmode);
		setprop(cfg->serial, "adapter", "filtmode", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->adapter.gain);
		setprop(cfg->serial, "adapter", "gain", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->chaccess.txdelay);
		setprop(cfg->serial, "chaccess", "txdelay", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->chaccess.slottime);
		setprop(cfg->serial, "chaccess", "slottime", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->chaccess.ppersistence);
		setprop(cfg->serial, "chaccess", "ppersistence", buf);
		snprintf(buf, sizeof(buf), "%d", cfg->chaccess.txtail);
		setprop(cfg->serial, "chaccess", "txtail", buf);
		snprintf(buf, sizeof(buf), "%d", !!cfg->chaccess.fullduplex);
		setprop(cfg->serial, "chaccess", "fullduplex", buf);
		setprop(cfg->serial, "interface", "ifname", cfg->ifcfg.ifname);
		setprop(cfg->serial, "interface", "hwaddr", cfg->ifcfg.hwaddr);
		setprop(cfg->serial, "interface", "ipaddr", cfg->ifcfg.ipaddr);
		setprop(cfg->serial, "interface", "netmask", cfg->ifcfg.netmask);
		setprop(cfg->serial, "interface", "broadcast", cfg->ifcfg.broadcast);
		snprintf(buf, sizeof(buf), "0x%02x", cfg->mdisc.output & 0xff);
		setprop(cfg->serial, "modemdisc", "output", buf);
		snprintf(buf, sizeof(buf), "0x%02x", cfg->mdisc.direction & 0xff);
		setprop(cfg->serial, "modemdisc", "direction", buf);
		snprintf(buf, sizeof(buf), "%u", cfg->mdisc.rxc);
		setprop(cfg->serial, "modemdisc", "rxc", buf);
		snprintf(buf, sizeof(buf), "%u", cfg->mdisc.txc);
		setprop(cfg->serial, "modemdisc", "txc", buf);
		snprintf(buf, sizeof(buf), "%u", cfg->mdisc.txd);
		setprop(cfg->serial, "modemdisc", "txd", buf);
		snprintf(buf, sizeof(buf), "0x%02x", cfg->t7f.ctrl & 0x1f);
		setprop(cfg->serial, "t7f", "ctrl", buf);
	}
	return 0;
}

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

