package A2B::Index;

use strict;

sub new {
	my $package = shift;
	my $table = shift;
	my @field_names = @_;
	my @fields = @{$table->{fields}}{@field_names};
	my $self = bless {
		field_names => \@field_names,
		fields => \@fields,
		data => {},
	}, $package;
	$table->_add_index($self);
	return $self;
}

sub name {
	my $self = shift;
	my $name = join '-', @{$self->{field_names}};
	return $name;
}

sub add_row {
	my $self = shift;
	my $row = shift;
	my @key = @$row[@{$self->{fields}}];
	my $place = $self->{data};
	while (@key > 1) {
		$place = $place->{shift @key} ||= {};
	}
	push @{$place->{$key[0]}}, $row;
}

sub get {
	my $self = shift;
	my @key = @_;
	my $place = $self->{data};
	while (@key && defined $place) {
		$place = $place->{shift @key};
	}
	my $depth = @{$self->{fields}} - @_;
	if ($depth > 0 && defined $place) {
		# we need to delve inside the rest of the index
		# this is a little ugly, & perhaps slow?
		my @list = values %$place;
		while (--$depth > 0) {
			@list = map { values %$_ } @list;
		}
		@list = map { @$_ } @list;
		return \@list;
	}
	return defined $place ? $place : [];
}

# warning - if you call query with undefined values in the template_row, it may
# go into an infinite loop

sub query {
	my $self = shift;
	my $template_row = shift;
	my @key = @$template_row[@{$self->{fields}}];
	pop @key until defined $key[-1];
	for (@key) { ! defined $_ and die "invalid query" }
	return $self->get(@key);
}

sub keys {
	my $self = shift;
	my $depth = @_ ? shift : @{$self->{fields}};
	die "bad depth" if $depth == 0 || $depth > @{$self->{fields}};

	return _collect_keys($self->{data}, $depth);
}

sub _collect_keys {
	my ($hash, $depth) = @_;
	if ($depth == 1) {
		return [CORE::keys(%$hash)];
	}
	return map {[$_ , _collect_keys($hash->{$_}, $depth-1)]} CORE::keys(%$hash);
}

sub data {
	my $self = shift;
	return $self->{data};
}

1
