/*
 * Copyright (c) 2003, 2004 Nokia
 * Author: Timo Savola <tsavola@movial.fi>
 *
 * This program is licensed under GPL (see COPYING for details)
 */

#define _GNU_SOURCE

#include "types.h"
#include "config.h"
#include "common.h"
#include "client.h"

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

/**
 * Compares the beginning of BUF to TGT. Comparison ends at first whitespace.
 */
static char *is_target(char *tgt, char *buf)
{
	int a, b;

	while (1) {
		a = *tgt++;
		b = *buf++;

		if (!a || !b || isspace(b))
			return (!a && isspace(b)) ? buf : NULL;

		if (a != b)
			return NULL;
	}
}

/**
 * Parses BUF into CFG.
 */
static bool_t read_params(char *buf, config_t *cfg)
{
	char *host, *port;

	host = skip_spaces(buf);
	if (!host) {
		errno = 0;
		error("Invalid parameter string");
		return FALSE;
	}

	port = strchr(host, ':');
	if (port) {
		*port++ = '\0';
		buf = port;
	} else {
		port = NULL;
		buf = host;
	}

	buf = find_space(buf);
	if (!buf) {
		errno = 0;
		error("Invalid parameter string");
		return FALSE;
	}
	*buf++ = '\0';

	cfg->host = strdup(host);
	if (!cfg->host) {
		oom_error();
		return FALSE;
	}

	if (port) {
		cfg->port = strdup(port);
		if (!cfg->port) {
			oom_error();
			return FALSE;
		}
	}

	return TRUE;
}

static bool_t read_opts(FILE *file, char *buf, size_t size, config_t *cfg)
{
	long pos;
	size_t cnt = 0;
	char **p;

	pos = ftell(file);

	while (1) {
		bool_t blank;
		ssize_t len;

		len = read_line_blank(file, buf, size, &blank);
		if (len < 0)
			break;
		if (blank)
			break;
		if (len > 0 && !isspace(buf[0]))
			break;

		buf = skip_spaces(buf);
		if (strlen(buf) == 0)
			continue;

		cnt++;
	}

	fseek(file, pos, SEEK_SET);

	cfg->opts = calloc(cnt + 1, sizeof (char *));
	if (!cfg->opts) {
		oom_error();
		return FALSE;
	}

	for (p = cfg->opts; cnt > 0; ) {
		if (read_line(file, buf, size) < 0)
			break;

		buf = skip_spaces(buf);
		if (strlen(buf) == 0)
			continue;

		*p++ = strdup(buf);
		cnt--;
	}

	return TRUE;
}

bool_t config_read(config_t *cfg, const char *filename, const char *target)
{
	FILE *file;
	char buf[1024], *bufp;
	ssize_t len;
	bool_t ok = FALSE;

	file = fopen(filename, "r");
	if (!file) {
		error("Can't open %s", filename);
		return FALSE;
	}

	while (1) {
		len = read_line(file, buf, sizeof (buf));
		if (len < 0) {
			errno = 0;
			error("Target %s is not listed in %s", target, filename);
			break;
		}

		if (len == 0 || isspace(buf[0]))
			continue;
		
		if (target) {
			/* Compare target name */

			bufp = is_target((char *) target, buf);
			if (!bufp)
				continue;

			cfg->target = strdup(target);

		} else {
			/* Take the first one (default) */

			bufp = find_space(buf);
			cfg->target = strndup(buf, bufp - buf);
		}

		ok = (read_params(bufp, cfg) && read_opts(file, buf, sizeof (buf), cfg));
		break;
	}

	fclose(file);

	return ok;
}

config_t *config_alloc(void)
{
	return calloc(1, sizeof (config_t));
}

void config_free(config_t *cfg)
{
	if (cfg->target)
		free(cfg->target);

	if (cfg->host)
		free(cfg->host);

	if (cfg->port)
		free(cfg->port);

	if (cfg->opts)
		free_vec((void **) cfg->opts, NULL);

	free(cfg);
}

char **get_targets(const char *filename)
{
	FILE *file;
	char buf[1024];
	ssize_t len;
	size_t cnt;
	char **targets, **p;

	file = fopen(filename, "r");
	if (!file)
		return NULL;

	for (cnt = 0; TRUE; ) {
		len = read_line(file, buf, sizeof (buf));
		if (len < 0)
			break;

		if (len > 0 && !isspace(*buf))
			++cnt;
	}

	rewind(file);

	targets = calloc(cnt + 1, sizeof (char *));
	if (!targets) {
		oom_error();
		return NULL;
	}

	for (p = targets; TRUE; ) {
		len = read_line(file, buf, sizeof (buf));
		if (len < 0)
			break;

		if (len > 0 && !isspace(*buf)) {
			*find_space(buf) = '\0';
			*p++ = strdup(buf);
		}
	}

	return targets;
}

