package PROP::Query::Link;

use strict;
use Carp;
use PROP::Constants;
use Hash::Util qw/lock_keys/;
use PROP::ResultSet::Link;
use PROP::Conditions::Local;
use PROP::Conditions::Foreign;
use PROP::Exception::IllegalArgument;

sub new {
    my ($invocant, $foreign_conditions, $orderings, $buffer_size, $limit) = @_;

    die new PROP::Exception::IllegalArgument("was expecting foreign conditions")
	unless ref($foreign_conditions) eq 'PROP::Conditions::Foreign';

    my $link = $foreign_conditions->get_link();
    my $relationship = $foreign_conditions->get_relationship();
    my $conditions = new PROP::Conditions::Local($foreign_conditions->get_expressions() || [],
						 $foreign_conditions->get_bindings() || []);

    my $class = ref($invocant) || $invocant;
    my $self = bless({}, $class);
    
    unless($relationship eq 'parents' or $relationship eq 'children') {
	my $msg = "was expecting either 'parents' or 'children', but got '$relationship'";
	die new PROP::Exception::IllegalArgument($msg);
    }


    $self->{-link} = $link;
    $self->{-relationship} = $relationship;
    $self->{-conditions} = $conditions || new PROP::Conditions::Local([], []);
    $self->{-orderings} = $orderings || [];
    $self->{-buffer_size} = $buffer_size || LIMIT;
    $self->{-limit} = $limit;

    unless(ref($self->{-conditions}) eq 'PROP::Conditions::Local') {
	my $msg = "invalid conditions argument: " . $conditions;
	die new PROP::Exception::IllegalArgument($msg);
    }

    lock_keys(%$self) if DEBUG;

    return $self;
}

sub push_conditional_expression {
    my ($self, $condition) = @_;
    $self->{-conditions}->push_expression($condition);
}

sub push_binding {
    my ($self, $binding) = @_;
    $self->{-conditions}->push_binding($binding);
}

sub get_link {
    my ($self) = @_;
    return $self->{-link};
}

sub get_relationship {
    my ($self) = @_;
    return $self->{-relationship};
}

sub get_expressions {
    my ($self) = @_;
    return $self->{-conditions}->get_expressions();
}

sub get_bindings {
    my ($self) = @_;
    return $self->{-conditions}->get_bindings();
}

sub get_orderings {
    my ($self) = @_;
    return unless $self->{-orderings};
    return @{$self->{-orderings}};
}

sub get_buffer_size {
    my ($self) = @_;
    return $self->{-buffer_size};
}

sub get_limit {
    my ($self) = @_;
    return $self->{-limit};
}

1;

=head1 Name

PROP::Query::Link

=head1 Description

Objects of this class are used to specify and execute queries on
collections of pairings for the given link, obtaining either the
parents or the children in the relationship.  Specifically, they are
used to specify whether parents or children are to be loaded, which of
those relatives are of interest, how the results should be ordered,
and how many results should be buffered in memory at a time.

=head1 Synopsis

 $ql  = new PROP::Query::Link($link, $relationship, $foreign_conditions,
			      $orderings, $buffer_size);

 $rsl = new PROP::ResultSet::Link($ql);

=head1 Methods

=over

=item new

 $ql = PROP::Query::Link->new($link, $relationship, $foreign_conditions,
			      $orderings, $buffer_size);

This method constructs a query as follows...

=over

=item $link

This argument is an instance of the class PROP::Link that specifies the
link upon which this query operation will be performed.

=item $relationship

This argument is a string value, either 'parents' or 'children',
specifying which part of the relationship will be loaded by the query

=item $foreign_conditions

This argument is an instance of the class PROP::Conditions::Foreign,
and specifies restrictions on which relatives will be loaded.

=item $orderings

This argument is an array reference that contains zero or more fields
by which to order the results of the query.

=item $buffer_size

This argument specifies the maximum number of rows that will be pulled
into memory at a time.  Programmatically speaking, this argument has
no ultimate effect on what the resulting result set will contain, but
can potentially affect performance dramatically, and as such it may be
desirable to tune it, taking into account the size of the things being
queried and the memory/cpu/disk of the machine.  Basically, you want
to load as many rows at a time as you can without choking the system's
resources.

=back

=item get_link

 $ql->get_link()

This method returns the link on which this query is to be performed.

=item get_relationship

 $ql->get_relationship()

This method returns the relationship of the objects to be obtained in
the resulting result set.

=item get_expressions

 $ql->get_expressions()

This method returns the reference to the array that holds the list of
conditional expressions for this query.

=item get_bindings

 $ql->get_bindings()

This method returns the reference to the array that holds the list of bindings for
the conditions for this query.

=item get_orderings

 $ql->get_orderings()

This method returns the reference to the array that holds the list of fields by
which to order the results.

=item get_buffer_size

 $ql->get_buffer_size()

This method returns the integer value that is the maximum number of fields that
will be requested from the underlying database at a time.

=back

=head1 Author

Andrew Gibbs (awgibbs@awgibbs.com,andrew.gibbs@nist.gov)

=head1 Legalese

This software was developed at the National Institute of Standards and
Technology by employees of the Federal Government in the course of
their official duties. Pursuant to title 17 Section 105 of the United
States Code this software is not subject to copyright protection and
is in the public domain. PROP is an experimental system. NIST
assumes no responsibility whatsoever for its use by other parties, and
makes no guarantees, expressed or implied, about its quality,
reliability, or any other characteristic. We would appreciate
acknowledgement if the software is used.  This software can be
redistributed and/or modified freely provided that any derivative
works bear some notice that they are derived from it, and any modified
versions bear some notice that they have been modified.
