#!/usr/bin/perl -w

use strict;
use XML::DOM;

sub write_c_header(@);
sub write_sql_dump(@);
sub get_data($);

my $dump_to_sql = 0;

# initialize parser and read the file
my $input = "./config.xml";
my $parser = new XML::DOM::Parser;
my $tree = $parser->parsefile($input) || die "Unable to parse XML file.";

# Get data
my $nodes = $tree->getElementsByTagName("option");
my @options = ();
for (my $i = 0; $i < $nodes->getLength; $i++)
{
	my @data = get_data($nodes->item($i));
	push @options, \@data;
}

write_c_header(@options);
write_sql_dump(@options) if ($dump_to_sql);

my $config_defaults = "void config_defaults(struct hub_config* config)\n{\n";
my $config_apply = "static int apply_config(struct hub_config* config, char* key, char* data, int line_count)\n{\n\tint max = 0;\n\tint min = 0;\n\n";
my $config_free = "void free_config(struct hub_config* config)\n{\n";
my $config_dump = "void dump_config(struct hub_config* config, int ignore_defaults)\n{\n";

foreach my $option (@options)
{
	my ($type, $name, $default, $advanced, $short, $desc, $since, $example, $check, $ifdef) = @$option;
	my $string = ($type =~ /(string|file|message)/);
	my $min = undef;
	my $max = undef;
	my $regexp = undef;

	if (defined $check)
	{
		$min = $check->getAttribute("min");
		$max = $check->getAttribute("max");
		$regexp = $check->getAttribute("regexp");

		$max = undef if ($max eq "");
		$min = undef if ($min eq "");
		$regexp = undef if ($regexp eq "");
	}

	$config_defaults .= "#ifdef $ifdef\n" if ($ifdef ne "");
	$config_defaults .= "\tconfig->$name = ";
	$config_defaults .= "hub_strdup(\"" if ($string);
	$config_defaults .= $default;
	$config_defaults .= "\")" if ($string);
	$config_defaults .= ";\n";
	$config_defaults .= "#endif /* $ifdef */\n" if ($ifdef ne "");

	$config_apply .= "#ifdef $ifdef\n" if ($ifdef ne "");
	$config_apply .= "\tif (!strcmp(key, \"" . $name . "\"))\n\t{\n";

	if ($type eq "int")
	{
		$config_apply .= "\t\tmin = $min;\n" if (defined $min);
		$config_apply .= "\t\tmax = $max;\n" if (defined $max);
		$config_apply .= "\t\tif (!apply_integer(key, data, &config->$name, ";
		if (defined $min) { $config_apply .= "&min"; } else { $config_apply .= "0"; }
		$config_apply .= ", ";
		if (defined $max) { $config_apply .= "&max"; } else { $config_apply .= "0"; }
		$config_apply .= "))\n";
	}
	elsif ($type eq "boolean")
	{
		$config_apply .= "\t\tif (!apply_boolean(key, data, &config->$name))\n";
	}
	elsif ($string)
	{
		$config_apply .="\t\tif (!apply_string(key, data, &config->$name, (char*) \"\"))\n";
	}

	$config_apply .= "\t\t{\n" .
				  "\t\t\tLOG_ERROR(\"Configuration parse error on line %d\", line_count);\n" .
				  "\t\t\treturn -1;\n" .
				  "\t\t}\n" .
				  "\t\treturn 0;\n" .
				  "\t}\n";
	$config_apply .= "#endif /* $ifdef */\n" if ($ifdef ne "");
	$config_apply .= "\n";

	if ($string)
	{
		$config_free .= "#ifdef $ifdef\n" if ($ifdef ne "");
		$config_free .= "\thub_free(config->" . $name . ");\n";
		$config_free .= "#endif /* $ifdef */\n" if ($ifdef ne "");
		$config_free .= "\n";
	}

	my $out = "%s";
	my $val = "config->$name";
	my $test = "config->$name != $default";

	$out = "%d" if ($type eq "int");
	$val = "config->$name ? \"yes\" : \"no\"" if ($type eq "boolean");

	if ($string)
	{
		$out = "\\\"%s\\\"";
		$test = "strcmp(config->$name, \"$default\") != 0";
	}

	$config_dump .= "#ifdef $ifdef\n" if ($ifdef ne "");
	$config_dump .= "\tif (!ignore_defaults || $test)\n";
	$config_dump .= "\t\tfprintf(stdout, \"$name = $out\\n\", $val);\n";
	$config_dump .= "#endif /* $ifdef */\n" if ($ifdef ne "");
	$config_dump .= "\n";
}

$config_apply .= "\t/* Still here -- unknown directive */\n";
$config_apply .= "\tLOG_ERROR(\"Unknown configuration directive: '%s'\", key);\n";
$config_apply .= "\treturn -1;\n";
$config_apply .= "}\n\n";
$config_defaults .= "}\n\n";
$config_free .= "}\n\n";
$config_dump .= "}\n\n";

open GENIMPL, ">gen_config.c" || die "Unable to write source file";
print GENIMPL "/* THIS FILE IS AUTOGENERATED - DO NOT CHANGE IT! */\n\n";
print GENIMPL $config_defaults;
print GENIMPL $config_apply;
print GENIMPL $config_free;
print GENIMPL $config_dump;


sub get_data($)
{
	my $p = shift;

	my $short = "";
	my $example = "";
	my $description = "";
	my $since = "";
	my $ifdef = "";

	$short = $p->getElementsByTagName("short")->item(0)->getFirstChild()->getData() if ($p->getElementsByTagName("short")->getLength());
	$since = $p->getElementsByTagName("since")->item(0)->getFirstChild()->getData() if ($p->getElementsByTagName("since")->getLength());
	$example = $p->getElementsByTagName("example")->item(0)->getFirstChild()->getData() if ($p->getElementsByTagName("example")->getLength());
	$description = $p->getElementsByTagName("description")->item(0)->getFirstChild()->getData() if ($p->getElementsByTagName("description")->getLength());
	my $check = $p->getElementsByTagName("check")->item(0);
	$ifdef = $p->getElementsByTagName("ifdef")->item(0)->getFirstChild()->getData() if ($p->getElementsByTagName("ifdef")->getLength());

	my @data = (
			$p->getAttribute("type"),
			$p->getAttribute("name"),
			$p->getAttribute("default"),
			$p->getAttribute("advanced"),
			$short,
			$description,
			$since,
			$example,
			$check,
			$ifdef
		);
	return @data;
}

# Write header file
sub write_c_header(@)
{
	my @data = @_;

	open GENHEAD, ">gen_config.h" || die "Unable to write header file";
	print GENHEAD "/* THIS FILE IS AUTOGENERATED - DO NOT CHANGE IT! */\n\n";
	print GENHEAD "struct hub_config\n{\n";

	foreach my $option (@data)
	{
		my ($type, $name, $default, $advanced, $short, $desc, $since, $example, $check, $ifdef) = @$option;

		my $string = ($type =~ /(string|file|message)/);

		print GENHEAD "#ifdef $ifdef\n" if ($ifdef ne "");

		print GENHEAD "\t";
		print GENHEAD "int  " if ($type eq "int");
		print GENHEAD "int  " if ($type eq "boolean");
		print GENHEAD "char*" if ($string);
		print GENHEAD " " . $name . ";";

		my $comment = "";
		if ($type eq "message")
		{
			$comment = "\"" . $default . "\"";
		}
		elsif (defined $short && length $short > 0)
		{
			$comment = $short;
			if (defined $default)
			{
				$comment .= " (default: ";
				$comment .= "\"" if ($string);
				$comment .= $default;
				$comment .= "\"" if ($string);
				$comment .= ")";
			}
		}

		if (length $comment > 0)
		{
			my $pad = "";
			for (my $i = length $name; $i < 32; $i++)
			{
				$pad .= " ";
			}
			$comment = $pad . "/*<<< " . $comment . " */";
		}
		print GENHEAD $comment . "\n";
		print GENHEAD "#endif /* $ifdef */\n" if ($ifdef ne "");
	}

	print GENHEAD "};\n\n";
}


sub write_sql_dump(@)
{
	my @data = @_;

	# Write SQL dump code
	open GENSQL, ">gen_config.sql" || die "Unable to write SQL dump";
	print GENSQL "START TRANSACTION;\n\n
		DROP TABLE uhub_config IF EXISTS;\n\n
		CREATE TABLE uhub_config (
			name VARCHAR(32) UNIQUE NOT NULL,
			defaultValue TINYTEXT NOT NULL,
			description LONGTEXT NOT NULL,
			type TINYTEXT NOT NULL,
			advanced BOOLEAN,
			example LONGTEXT,
			since TINYTEXT
		);\n\n";

	foreach my $option (@data)
	{
		my ($type, $name, $default, $advanced, $short, $desc, $since, $example, $check, $ifdef) = @$option;

		if ($type =~ /(string|file|message)/ )
		{
			$default = "\\\"$default\\\"";
		}

		$desc =~ s/\"/\\\"/g;
		$type =~ s/^int$/integer/;

		my $stmt = "INSERT INTO uhub_config VALUES(";
		$stmt .= "\"$name\", ";
		$stmt .= "\"$default\", ";
		$stmt .= "\"$desc\", ";
		$stmt .= "\"$type\", ";

		if (defined $example)
		{
			my $example_str = $example;
			$example_str =~ s/\\/\\\\/g;
			$example_str =~ s/\"/\\\"/g;
			$stmt .= "\"$example_str\", ";
		} else {
			$stmt .= "NULL, ";
		}

		if (defined $since) {
			$stmt .= "\"$since\", ";
		} else {
			$stmt .= "NULL, ";
		}

		if (defined $advanced) {
			$stmt .= "\"$advanced\"";
		} else {
			$stmt .= "NULL";
		}

		$stmt .= ");\n";

		print GENSQL $stmt;
	}
	print GENSQL "\n\nCOMMIT;\n\n";
}
