The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

package Xmldoom::Definition::Property::Simple;
use base qw(Xmldoom::Definition::Property);

use strict;

use Data::Dumper;

sub new
{
	my $class = shift;
	my $args  = shift;

	my $parent;
	my $prop_name;
	my $attr_name;
	my $trans_to;
	my $trans_from;
	my $set_name;
	my $get_name;
	my $options;
	my $options_table;
	my $options_column;
	my $options_criteria;
	my $inclusive;

	if ( ref($args) eq 'HASH' )
	{
		$parent           = $args->{parent};
		$prop_name        = $args->{name};
		$attr_name        = $args->{attribute};
		$trans_to         = $args->{trans_to};
		$trans_from       = $args->{trans_from};
		$set_name         = $args->{set_name};
		$get_name         = $args->{get_name};
		$options          = $args->{options};
		$options_table    = $args->{options_table};
		$options_column   = $args->{options_column};
		$options_criteria = $args->{options_criteria};
		$inclusive        = $args->{inclusive};
	}
	else
	{
		$parent    = $args;
		$prop_name = shift;
		$attr_name = shift;

		# if you don't know, then there is no need to know.
		$args = {
			parent => $parent,
			name   => $prop_name,
		};
	}

	# allow a short form when we want the property name
	# to be the same as the attribute name.
	if ( not defined $attr_name )
	{
		$attr_name = $prop_name;
	}

	# TODO: use the $parent variable to check if we are creating a valid 
	# property with a valid attribute.

	# the default get/set names
	if ( not defined $set_name )
	{
		$set_name = "set_$prop_name";
	}
	if ( not defined $get_name )
	{
		$get_name = "get_$prop_name";
	}

	# init our members
	my $self = $class->SUPER::new( $args );
	$self->{attr_name}        = $attr_name;
	$self->{trans_to}         = $trans_to;
	$self->{trans_from}       = $trans_from;
	$self->{set_name}         = $set_name;
	$self->{get_name}         = $get_name;
	$self->{options}          = $options   || [ ];
	$self->{options_table}    = $options_table;
	$self->{options_column}   = $options_column;
	$self->{options_criteria} = $options_criteria;
	$self->{inclusive}        = $inclusive || 0;

	bless  $self, $class;
	return $self;
}

sub get_type       { return "inherent"; }
sub get_attribute  { return shift->{attr_name}; }
sub get_trans_to   { return shift->{trans_to}; }
sub get_trans_from { return shift->{trans_from}; }

sub get_autoload_get_list
{
	return [ shift->{get_name} ];
}

sub get_autoload_set_list
{
	return [ shift->{set_name} ];
}

sub get_data_type
{
	my $self = shift;
	my $args = shift;

	my $include_options = 0;

	if ( ref($args) eq 'HASH' )
	{
		$include_options = $args->{include_options};
	}

	my $table  = $self->get_parent()->get_table();
	my $column = $table->get_column( $self->{attr_name} );
	my $value  = $table->get_column_type( $self->{attr_name} );

	if ( not defined $value )
	{
		die "Unable to determine datatype of column $self->{attr_name} of type $column->{type}";
	}

	# pass these along
	$value->{hints}   = $self->{hints};
	$value->{default} = $self->{default};

	# if inclusive options are set we want to return a list of those...
	if ( $self->{inclusive} and $include_options )
	{
		# copy the manual ones in
		$value->{options} = [ @{$self->{options}} ];

		# check to see if we need to pull anything from the database
		if ( defined $self->{options_table} and defined $self->{options_column} )
		{
			my $foreign_key = $self->get_parent()->get_table()->get_foreign_key( $self->{attr_name} );
			my $value_attr  = join '/', $foreign_key->{foreign_table}, $foreign_key->{foreign_column};
			my $desc_attr   = join '/', $self->{options_table}, $self->{options_column};
			my $database    = $self->get_parent()->get_database();

			my $criteria;

			if ( $self->{options_criteria} )
			{
				$criteria = $self->{options_criteria}->clone();
			}
			else
			{
				$criteria = Xmldoom::Criteria->new();
			}

			my $rs = $database->SearchRS( $criteria, $value_attr, $desc_attr );

			$value->{options} = [ ];
			while ( $rs->next() )
			{
				my $row = $rs->get_row();

				my $val   = $row->{$foreign_key->{foreign_column}};
				my $desc  = $row->{$self->{options_column}};

				my $set = 0;

				# check to see if we manually set this one and then use that one
				foreach my $manual_opt ( @{$self->{options}} )
				{
					if ( $manual_opt->{value} eq $value )
					{
						$set = 1;
					}
				}

				# add this guy from the database
				if ( not $set )
				{
					push @{$value->{options}}, { value => $val, description => $desc };
				}
			}
		}
	}

	return $value;
}

sub get_value_description
{
	my ($self, $value) = @_;

	# first try the options that we have manually added to property
	foreach my $opt ( @{$self->{options}} )
	{
		if ( $opt->{value} eq $value )
		{
			return $opt->{description};
		}
	}

	# second, attempt to pull from a translation table if information to do so
	# is available.
	if ( defined $self->{options_table} and defined $self->{options_column} )
	{
		my $database   = $self->get_parent()->get_database();
		my $table_name = $self->get_parent()->get_table_name();
		my $self_attr  = join '/', $table_name, $self->{attr_name};
		my $desc_attr  = join '/', $self->{options_table}, $self->{options_column};

		# TODO: should we here use the $self->{options_criteria}.
		# our criteria is that this properties attribute must equal the given value!
		my $criteria = Xmldoom::Criteria->new();
		$criteria->add_attr( $self_attr, $value );

		# grab the value
		my $rs = $database->SearchRS( $criteria, $desc_attr );
		if ( $rs->next() )
		{
			my $row = $rs->get_row();
			return $row->{$self->{options_column}};
		}
	}

	return undef;
}

sub trans_to
{
	my ($self, $value) = @_;

	if ( $self->{trans_to} )
	{
		if ( not exists $self->{trans_to}->{$value} )
		{
			die sprintf "Trying to use invalid value '$value' for the '%s' property", $self->get_name();
		}

		$value = $self->{trans_to}->{$value};
	}

	return $value;
}

sub trans_from
{
	my ($self, $value) = @_;

	if ( $self->{trans_from} )
	{
		$value = $self->{trans_from}->{$value};
	}

	return $value;
}

sub get
{
	my ($self, $object) = @_;

	return $self->trans_from( $object->_get_attr($self->{attr_name}) );
}

sub set
{
	my ($self, $object, $value) = @_;

	$object->_set_attr( $self->{attr_name}, $self->trans_to($value) );
}

sub get_query_lval
{
	my $self = shift;

	my $table_name  = $self->{parent}->get_table_name();
	my $column_name = $self->{attr_name};

	return [ DBIx::Romani::Query::SQL::Column->new( $table_name, $column_name ) ];
}

sub get_query_rval
{
	my ($self, $value) = @_;

	return [ DBIx::Romani::Query::SQL::Literal->new( $self->trans_to($value) ) ];
}

sub autoload
{
	my ($self, $object, $func_name, $value) = @_;

	if ( $func_name eq $self->{set_name} )
	{
		$self->set($object, $value);
	}
	elsif ( $func_name eq $self->{get_name} )
	{
		return $self->get($object);
	}
	else
	{
		die "$func_name is not defined by this property";
	}
}

1;