package ATR::Dataset::Rowmap;

use strict;
use warnings;
use base 'ATR::Dataset';


=head1 NAME

ATR::Dataset::Rowmap - lookup by field value

=head1 DESCRIPTION

Generate a static hash from values in a column to rowId.  Instantiate
with L<ATR::Dataset>-E<gt>map_colhash or map_rowlist .

=cut

# $Id: Rowmap.pm,v 1.5 2005/05/23 16:04:37 mca Exp $

#----------------------------------------------------------------------------

sub _init_hash {
    my ($self, $src, $srcColId) = @_;
    my $at = "creating rowmap '".$self->name."' from '".$src->name."'";
    $self->_init_cols($src, $at);

    # Generate row map
    my %rowmap; # key = value in $srcColId, value = $srcRowId
    foreach my $srcRowId ($src->rowList) {
	my $key = $src->get($srcColId, $srcRowId);
	die "Cannot rowmap NULL value, found at ('$srcColId', '$srcRowId') $at (colmap it to '' instead?)"
	  unless defined $key;
	die "Key conflict on '$key' in column '$srcColId' $at"
	  if exists $rowmap{$key};
	$rowmap{$key} = $srcRowId;
    }

    $self->{rowmap} = \%rowmap;
    $self->{src} = $src;
}

sub _init_list {
    my ($self, $src, $rowlist) = @_;
    my $at = "creating rowmap '".$self->name."' from '".$src->name."'";
    $self->_init_cols($src, $at);

    # Check the ids - have our surprise now instead of later
    my %src_rows;
    @src_rows{($src->rowList)} = ();
    for (my $i=0; $i<@$rowlist; $i++) {
	my $srcRowId = $rowlist->[$i];
#	next unless defined $srcRowId; # allow missing rows
	die "Found undef source row at #$i when $at" unless defined $srcRowId;
	die "Found source row id '$srcRowId' at #$i, not in source rowList when $at"
	  unless exists $src_rows{$srcRowId};
    }

    $self->{rowlist} = $rowlist;
    $self->{src} = $src;
}

sub _init_cols {
    my ($self, $src, $at) = @_;
    my @cols = $src->colnames;
    my %col2num;
    @col2num{@cols} = (0 .. $#cols);
    $self->{cols} = \@cols;
    $self->{col2num} = \%col2num;
}


sub rowCount {
    my $self = shift;
    return scalar ($self->{rowmap}
		   ? keys %{ $self->{rowmap}  }
		   :      @{ $self->{rowlist} } );
}

sub rowList {
    my $self = shift;
    return keys %{ $self->{rowmap} } if $self->{rowmap};
    my @rows = (0 .. $#{ $self->{rowlist} }); # avoid using .. in scalar context
    return @rows;
}


sub get {
    my ($self, $colId, $rowId) = @_;
    die "Object method needs 2 args" unless @_ == 3;
    my $srcRowId = $self->_mapFatal($rowId);
    return $self->{src}->get($colId, $srcRowId);
}

sub canSet {
    my ($self, $colId) = @_;
    return $self->{src}->canSet($colId);
}

sub set {
    my ($self, $colId, $rowId, $value) = @_;
    die "Object method needs 3 args" unless @_ == 4;
    my $srcRowId = $self->_mapFatal($rowId);
    return $self->{src}->set($colId, $srcRowId, $value);
}

sub _mapFatal {
    my ($self, $rowId) = @_;
    my $rowmap = $self->{rowmap};
    if ($rowmap) {
	die "Row id '$rowId' not found in '".$self->name."'"
	  unless exists $rowmap->{$rowId};
	return $rowmap->{$rowId};
    } else {
	my $rowlist = $self->{rowlist};
	die "Row id '$rowId' not found in '".$self->name."'"
	  unless exists $rowlist->[$rowId]; # yes it works on ARRAY too!
	return $rowlist->[$rowId];
    }
}

1;
