/*
 * Portions Copyright (c) 1999 GMRS Software GmbH
 * Carl-von-Linde-Str. 38, D-85716 Unterschleissheim, http://www.gmrs.de
 * All rights reserved.
 *
 * Author: Arno Unkrig <arno@unkrig.de>
 *
 * 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 in the file COPYING for more details.
 */

#include <ctype.h>
#include <iostream>

#include "Properties.h"

const char *
Properties::getProperty(const char *key, const char *dflt) const
{
	map<string, string>::const_iterator i;
	i = property_map.find(key);
	return i == property_map.end() ? dflt : (*i).second.c_str();
}

const char *
Properties::getProperty(const char *key) const
{
	map<string, string>::const_iterator i;
	i = property_map.find(key);
	return i == property_map.end() ? NULL : (*i).second.c_str();
}

void
Properties::load(istream &is)
{
	string key, value;
	while (readProperty(is, &key, &value))
		setProperty(key, value);
}

/*
 * Expand the escape sequence at "line[pos]". Backslash-Newline reads another
 * line from "is".
 */
static void
expandEscape(string *line_in_out, string::size_type *pos_in_out, istream &is)
{
	for (;;) {
		if (line_in_out->at(*pos_in_out) != '\\') {
			++*pos_in_out;
			return;
		}
		if (*pos_in_out != line_in_out->size() - 1)
			break;

		string tmp;
		if (!getline(is, tmp)) {
			++*pos_in_out;
			return;
		}
		size_t j;
		for (j = 0; j < (size_t)tmp.size() && isspace(tmp[j]); ++j)
			;
		line_in_out->replace(
				*pos_in_out, string::npos,
				tmp, j, string::npos
				);
	}

	char c = line_in_out->at(*pos_in_out + 1);
	switch (c) {
	case 't': c = '\t';
		break;
	case 'n': c = '\n';
		break;
	case 'r': c = '\r';
		break;
	}
	line_in_out->replace(*pos_in_out, 2, &c, 1);
	++*pos_in_out;
}

/*static*/ bool
Properties::readProperty(istream &is, string *key_return, string *value_return)
{
	string line;
	string::size_type i, l;

	// Skip empty and comment lines.
	for (;;) {
		if (!getline(is, line))
			return false;
		l = line.size();

		// Skip leading white-space.
		for (i = 0; i < l && isspace(line[i]); ++i)
			;
		if (i == l)
			continue;                    // Line contains only white-space.

		// Ignore comment lines.
		if (line[i] == '#' || line[i] == '!')
			continue;                               // Comment line.

		break;
	}

	// Parse key.
	string::size_type bok = i;
	while (i < line.size()) {
		char c = line[i];
		if (isspace(c) || c == '=' || c == ':')
			break;
		expandEscape(&line, &i, is);
	}
	string::size_type eok = i;

	// Skip key terminator.
	while (i < l && isspace(line[i]))
		++i;
	if (i < l && (line[i] == '=' || line[i] == ':')) {
		for (++i; i < l && isspace(line[i]); ++i)
			;
	}

	// Substitute escape sequences in value.
	string::size_type bov = i;
	while (i < line.size())
		expandEscape(&line, &i, is);

	// Return key and value.
	key_return->assign(line, bok, eok - bok);
	value_return->assign(line, bov, string::npos);
	return true;
}
