The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
use strict;
use warnings FATAL => 'all';

package HTML::Tested::List;
use HTML::Tested::List::Renderer;
use HTML::Tested::List::Table;
use Carp;

sub new {
	my ($class, $parent, $name, $c, %args) = @_;
	$args{name} ||= $name;

	my @renderers = ('HTML::Tested::List::Renderer');
	push @renderers, HTML::Tested::List::Table->new if $args{render_table};

	$args{renderers} ||= \@renderers;
	{
		no strict 'refs';
		if (!$c) {
			$c = "$parent\::Item$name";
			push @{ "$c\::ISA" }, ($args{cbase} || "HTML::Tested");
		}
		*{ "$parent\::$name\_containee" } = sub { return $c; };
		*{ "$parent\::$name\_containee_do" } = sub {
			my ($self, $func, @args) = @_;
			return $self->$name($c->$func(@args));
		};
	};
	
	$args{containee} ||= $c;
	my $self = bless(\%args, $class);
	for my $r (@{ $self->renderers }) {
		$r->init($self, $parent);
	}
	return $self;
}

sub name { return shift()->{name}; }
sub renderers { return shift()->{renderers}; }
sub options { return {}; }
sub _get_option { return undef; }

sub render {
	my ($self, $caller, $stash, $id) = @_;
	for my $r (@{ $self->renderers }) {
		$r->render($self, $caller, $stash, $id);
	}
}

sub containee {
	my $res = shift()->{containee} or confess "No containee argument given";
	return $res;
}

sub bless_from_tree {
	my ($self, $p) = @_;
	my $target = $self->containee;
	return [ map { $target->ht_bless_from_tree($_) } @$p ];
}

sub merge_one_value { shift()->_do_one_value("merge_one_value", @_); }
sub absorb_one_value { shift()->_do_one_value("absorb_one_value", @_); }

sub _do_one_value {
	my ($self, $func, $root, $val, @path) = @_;
	my $arr = $root->{ $self->name } || [];
	my $id = shift(@path) or return;
	$arr->[--$id] ||= bless({}, $self->containee);
	$arr->[$id]->_ht_set_one($func, $val, @path);
	$root->{ $self->name } = $arr;
}

sub finish_load {
	my ($self, $root) = @_;
	my @arr = grep { $_ } @{ $root->{ $self->name } };
	$root->{ $self->name } = \@arr unless $self->{keep_holes};
	$_->_call_finish_load for @arr;
}

sub validate {
	my ($self, $caller) = @_;
	my $n = $self->name;
	my $arr = $caller->$n || [];
	my @res;
	for (my $i = 0; $i < @$arr; $i++) {
		push @res, map { [
			sprintf('%s__%d__%s', $n, $i + 1, shift @$_), @$_
		] } $arr->[$i]->ht_validate;
	}
	return @res;
}

1;