package Oryx::DBI::Association::Array;

use Oryx::DBI::Association::Reference;

use base qw(Oryx::Association::Array);

sub create {
    my ($self, $query, $proto) = @_;
}

sub retrieve {
    my ($self, $query, $id) = @_;
}

sub update {
    my ($self, $query, $obj) = @_;
    my $accessor = $self->role;
    my $value = $obj->$accessor;
    my $sql = SQL::Abstract->new;

    my $lt_name = $self->link_table;
    my @lt_flds = $self->link_fields;

    my (@bind, %lt_fieldvals, %lt_where, $stmnt, $sth);
    if (%{tied(@$value)->deleted}) {
	%lt_where = ($lt_flds[0] => $obj->id, $lt_flds[2] => '');

	$stmnt = $sql->delete($lt_name, \%lt_where);
	$sth   = $obj->dbh->prepare($stmnt);

	while (my ($index, $thing) = each %{tied(@$value)->deleted}) {
	    $lt_where{$lt_flds[2]} = $index;
	    @bind = $sql->values(\%lt_where);
	    $sth->execute(@bind);
	}

	$sth->finish;
	tied(@$value)->deleted({});
    }

    if (%{tied(@$value)->created}) {
	@lt_fieldvals{@lt_flds} = ($obj->id, '', '');

	$stmnt = $sql->insert($lt_name, \%lt_fieldvals);
	$sth   = $obj->dbh->prepare($stmnt);

	while (my ($index, $thing) = each %{tied(@$value)->created}) {
	    $lt_fieldvals{$lt_flds[1]} = defined $thing ? $thing->id : undef;
	    $lt_fieldvals{$lt_flds[2]} = $index;
	    @bind = $sql->values(\%lt_fieldvals);
	    $sth->execute(@bind);
	}

	$sth->finish;
	tied(@$value)->created({});
    }

    if (%{tied(@$value)->updated}) {
	%lt_where = ( $lt_flds[0] => $obj->id, $lt_flds[2] => '' );
	%lt_fieldvals = ( $lt_flds[1] => '' );

	$stmnt = $sql->update($lt_name, \%lt_fieldvals, \%lt_where);
	$sth   = $obj->dbh->prepare($stmnt);

	while (my ($index, $thing) = each %{tied(@$value)->updated}) {
	    $lt_fieldvals{$lt_flds[1]} = defined $thing ? $thing->id : undef;
	    $lt_where{$lt_flds[2]} = $index;
	    @bind = $sql->values(\%lt_fieldvals);
	    push @bind, $sql->values(\%lt_where);
	    $sth->execute(@bind);
	}

	$sth->finish;
	tied(@$value)->updated({});
    }

    $self->update_backrefs($obj, @$value);

    $obj->dbh->commit;
}

sub delete {
    my $self = shift;
    my ($query, $obj) = @_;
    my $accessor = $self->role;
    my $value = $obj->$accessor;

    if ($self->constraint eq 'Composition') {
	# cascade the delete
	while (my $thing = pop @$value) {
	    $thing->delete;
	}
    } elsif ($self->constraint eq 'Aggregation') {
	# just clear the Array
	@$value = ();
    }

    $self->update(@_);
}

sub search {
    my ($self, $query) = @_;
}

sub construct {
    my ($self, $obj) = @_;
    my $assoc_name = $self->role;
    my @args = ($self, $obj);
    tie my @value, __PACKAGE__, @args;
    $obj->{$assoc_name} = \@value;
}

# Fill an array ref with ids from the link table and order by 'meta'.
# The ids are each tied to a Reference value type which will retrieve
# the referenced object (lazy loading)
sub load {
    my ($self, $owner) = @_;

    my $lt_name = $self->link_table;
    my ($s_id_field, $t_id_field, $meta_field) = $self->link_fields;

    my (@fields, %where, @order);
    @fields = ($t_id_field, '_seq');
    $where{$s_id_field} = $owner->id;
    @order = ('_seq');

    my $sql = SQL::Abstract->new;
    my ($stmnt, @bind) = $sql->select($lt_name, \@fields, \%where, \@order);

    my $sth = $owner->dbh->prepare_cached($stmnt);
    $sth->execute(@bind);

    my $Array = [ ]; my @args;
    my @ids_seq = $sth->fetchall;
    for (my $x = 0; $x < @ids_seq; $x++) {
	@args = ($self, $ids_seq[$x]->[0]);
	# this probably looks a bit weird, but the reason we're not
	# tie'ing here is because certain Array operations need to
	# access each member of the array (such as shift and unshift)
	# and when it does, we just want to move the Reference objects
	# about without fetching each referree from storage... hence
	# the explicit call to $thing->FETCH below in 'sub fetch'
	$Array->[$ids_seq[$x]->[1]]
	  = Oryx::DBI::Association::Reference->TIESCALAR(@args);
    }
    $sth->finish;

    return $Array;
}

sub fetch {
    my ($self, $thing, $owner) = @_;
    if (ref $thing eq 'Oryx::DBI::Association::Reference') {
	return $thing->FETCH();
    }
    return $thing;
}

sub store {
    my ($self, $thing, $owner) = @_;
    return $thing;
}

sub link_fields {
    my $self = shift;
    return ("source_id", "target_id", '_seq');
}

1;
