use strict;
use XML::Rules;

my $parser = XML::Rules->new( rules => [ _default => 'raw extended array']);


my %plugins;
%plugins = (
	'_parser' => $parser,
	set => sub {
		die "The <set> plugin requires a name attribute!\n" unless exists($_[0]->{name});

		my $content = $_[0]->{_content};
		$plugins{$_[0]->{name}} = sub {$content};
		return;
	},
	print => sub {
		print $parser->ToXML( '', process($_[0]->{_content}));
		return;
	},
	select => sub {
		die "The <select> plugin requires a switch attribute!\n" unless exists($_[0]->{switch});

		my $switch = $_[0]->{switch};
		if (exists $_[0]->{':' . $switch}) { # exists a subtag with that name
			return process($_[0]->{':' . $switch}[0]{_content})
		} elsif (exists $_[0]->{':case'}) { # maybe there is a <case id="that name">
			foreach my $case (@{$_[0]->{':case'}}) {
				return process($case->{_content}) if $case->{id} eq $switch;
			}
			return;
		} else {
			return; # the case was not found
		}
	},
);

my $data = $parser->parse( \*DATA);
$data = $data->{_content};

use Data::Dumper;
#print Dumper($data);
#print Dumper(process($data));
my $result = $parser->ToXML(@{(process($data))->[0]});

print "\n-----------------------------\n\n$result";

sub process {
	my ($what) = @_;
	if (! ref $what) {
		return $what
	} elsif (ref($what) eq 'ARRAY') {
		return [ map {
			if (!ref $_) {
				$_
			} elsif (ref($_) eq 'ARRAY' and @$_ == 2) {
				processAttr($_->[1]);
				if (exists $plugins{$_->[0]}) {
					$plugins{$_->[0]}->($_->[1])
				} else {
					$_->[1]{_content} = process($_->[1]{_content});
					[ $_->[0] => $_->[1]]
				}
			}
		} @$what]
	} else {
		die "process() only accepts strings and array refs.\n";
	}
}

sub processAttr {
	my ($attrs) = @_;
	foreach my $attr (keys %$attrs) {
		next if $attr =~ /^:/;
		next unless $attrs->{$attr} =~ /\{/;
#print "$attrs->{$attr} -> ";
		$attrs->{$attr} =~ tr/{}/<>/;
		my $data = $parser->parse( '<root>' . $attrs->{$attr} . '</root>');
		$data = $data->{_content}[0][1]{_content};
#print "(" . Dumper($data) . ")";
		$attrs->{$attr} = $parser->ToXML('', process($data));
#print "$attrs->{$attr}\n";
	}
}


__END__
<html>
 <set name="foo">blah blah</set>
 <set name="bar">two</set>
 <set name="x">3</set>
 <h1>I say <foo/>, you know.</h1>
<print>This is printed always!</print>
 <p>
 <select switch="{bar/}">
   <one>This is one.</one>
   <two>This is the other.</two>
   <three>And this is yet another.</three>
   <print>This is NOT printed!</print>
 </select>
 </p>

 <p>
 <select switch="{x/}0">
   <case id="10">This is ten.</case>
   <case id="20">This is twenty.</case>
   <case id="30">This is thirty.</case>
   <case id="41">This is fourty one.<print>This is not printed neither!</print></case>
 </select>
 </p>
</html>