package Markup::Processor;

use strict; use warnings;

use base 'Object';

use Alias;

use vars qw(@elements $i $e $type $tag $name @context);

sub new {
	my ($class, $markup) = @_;
	my $p = $class->SUPER::new({
		elements => $markup->elements,
		i => -1,
		e => undef,
		type => undef,
		tag => undef,
		name => undef,
		context => [],
	});
	return $p;
}

sub read {
	my $p = attr shift;
	undef $type; undef $tag; undef $name;
	++$i;
	$e = $elements[$i];
	if (!defined $e) { return 0; }  # this happens after ++$i so can insert at end
	$type = $e->type;
	if ($type eq "open") {
		$tag = $e->tag;
		$name = $e->attr_get("name");
		push @context, $tag unless $e->single;
		# XXX some non-xhtml single tags will get added to @context,
		# e.g. br meta img input link ...
	}
	if ($type eq "close") {
		$tag = $e->tag;
		# strip off @context until we get past $tag
		# We do this because we can't assume the html is
		# properly balanced xhtml.
		while (1) {
			if (@context == 0) {
				die "unbalanced html, close tag '$tag' has no corresponding open tag";
			}
			my $open_tag = pop @context;
			if ($open_tag eq $tag) { last }
		}
	}
	return 1;
}

sub insert {
	my $p = attr shift;
	splice @elements, $i, 0, @_;
	$i += @_;
}

sub append {
	my $p = attr shift;
	splice @elements, $i+1, 0, @_;
	$i += @_;
}

sub delete {
	my $p = attr shift;
	splice @elements, $i, 1;
	--$i;
}

sub replace_container_content {
	my $p = attr shift;
	# @_ are the new content elements
	my $open_element = $e;
	if ($e->single) {
		$e->set_single(0);
		my $space_after = $e->get_space_after();
		$e->set_space_after("");
		$p->append(@_);
		my $close = Markup::Close->new($open_element->tag);
		$close->set_space_after($space_after);
		$p->append($close);
	} else {
		my $stop_level = @context - 1;
		while (1) {
			if (! $p->read) {
				my $tag = $open_element->tag;
				my $name = $open_element->attr_get("name");
				die qq{did not find </$tag> for <$tag name="$name">};
			}
			if (@context == $stop_level) {
				# we just read the corresponding close tag
				last;
			}
			$p->delete;
		}
		$p->insert(@_);
	}
}

sub read_container_content {
	my $p = attr shift;
	my $open_element = $e;
	my @result;
	if (! $e->single) {
		my $stop_level = @context - 1;
		while (1) {
			if (! $p->read) {
				my $tag = $open_element->tag;
				my $name = $open_element->attr_get("name");
				die qq{did not find </$tag> for <$tag name="$name">};
			}
			if (@context == $stop_level) {
				# we just read the corresponding close tag
				last;
			}
			push @result, $e;
		}
	}
	return \@result;
}

1
