package PROP::Schema;

use strict;
use Hash::Util qw/lock_keys/;
use PROP::Constants;
use PROP::Schema::MySQL;
use PROP::Conf;
use PROP::DBH;
use PROP::Exception::Configuration;

sub new {
    my ($invocant, $table_name) = @_;
    my $self = bless({}, 'PROP::Schema::' . get_rdbms());

    $self->{-table_name} = $table_name;
    $self->{-pk_name} = undef;
    $self->{-fields} = [];
    $self->{-field_hash} = {};

    lock_keys(%$self) if DEBUG;

    eval {
	$self->_query_schema();
    };

    die if $@;


    return $self;
}

sub _query_schema {
    my ($self) = @_;
    my $class = ref($self);
    
    my $msg = "class '$class' did not define a _query_schema method";
    die new PROP::Exception::Configuration($msg);
}

sub _add_field {
    my ($self, $field) = @_;

    push(@{$self->{-fields}}, $field);
    $self->{-field_hash}{$field->get_name()} = $field;

    $self->_add_pk($field->get_name()) if $field->is_pk();
}

sub _add_pk {
    my ($self, $name) = @_;
    my $pk = $self->{-pk_name};

    if($pk) {
	if(ref($pk)) {
	    push(@$pk, $name);
	}
	else {
	    $self->{-pk_name} = [$pk, $name];
	}
    }
    else {
	$self->{-pk_name} = $name;
    }
}

sub get_table_name {
    my ($self) = @_;
    return $self->{-table_name};
}

sub get_pk_name {
    my ($self) = @_;

    # make a defensive copy if the pk is an array
    return ref($self->{-pk_name}) ? [@{$self->{-pk_name}}] : $self->{-pk_name};
}

sub get_field_names {
    my ($self) = @_;
    return map { $_->get_name() } @{$self->{-fields}};
}

sub has_field {
    my ($self, $field_name) = @_;
    return $self->{-field_hash}{$field_name} ? 1 : 0;
}

package PROP::Schema::Field;

use strict;
use PROP::Constants;
use Hash::Util qw/lock_keys/;

sub new {
    my ($invocant, $name, $is_pk) = @_;
    my $self = bless({}, ref($invocant) || $invocant);

    $self->{-name} = $name;
    $self->{-is_pk} = $is_pk;

    lock_keys(%$self) if DEBUG;

    return $self;
}

sub get_name {
    my ($self) = @_;
    return $self->{-name};
}

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

1;

=head1 Name

PROP::Schema

=head1 Description

This class houses information about the schema of a database table.
Given the name of a table, passed to its constructor, it automatically
populates itself with this information.

=head1 Synopsis

 $schema = new PROP::Schema('MyTable');
 print "MyTable's primary key field is ", $schema->get_pk_name(), "\n";

=head1 Methods

=over

=item new

 $schema = new PROP::Schema('MyTable');

This method constructs and returns a PROP::Schema object.  The actual
table structure is pulled automatically from the underlying database.

=item get_table_name

 $schema->get_table_name();

This method returns the name of the table.

=item get_pk_name

 $schema->get_pk_name()

This method returns a scalar value that is either a string that
contains the name of the primary key, or an array reference that holds
two or more string values, each of which contains a name of a field
that makes up a multi-field primary key.

=item get_field_names

 $schema->get_field_names()

This method returns an array that holds the names of the fields of
this table, in the order in which they appear in the table.

=item has_field

 $schema->has_field($field_name)

This method returns a boolean value, indicating whether the table in
question has a field by the name of $field_name.

=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.
