

use strict;

use Tangram::Type::Abstract::Set;

package Tangram::Type::Set::FromOne;

use vars qw(@ISA);
 @ISA = qw( Tangram::Type::Abstract::Set );

use Carp;

sub reschema
{
	my ($self, $members, $class, $schema) = @_;

	foreach my $member (keys %$members)
	{
		my $def = $members->{$member};

		unless (ref($def))
		{
			$def = { class => $def };
			$members->{$member} = $def;
		}

		$def->{coll} ||= $schema->{normalize}->($class) . "_$member";

		$schema->{classes}{$def->{class}}{stateless} = 0;

		if (exists $def->{back})
		{
			my $back = $def->{back} ||= $def->{coll};
			$schema->{classes}{ $def->{class} }{members}{backref}{$back} =
			  bless {
					 name => $back,
					 col => $def->{coll},
					 class => $class,
					 field => $member
					}, 'Tangram::Type::BackRef';
		}
	}
   
	return keys %$members;
}

sub defered_save
  {
	my ($self, $storage, $obj, $field, $def) = @_;

	return  if tied $obj->{$field};

	my $coll_id = $storage->export_object($obj);
	my $classes = $storage->{schema}{classes};
	my $item_classdef = $classes->{$def->{class}};
	my $table = $item_classdef->{table};
	my $item_col = $def->{coll};

	$self->update
	    ($storage, $obj, $field,
	     sub
	     {
		 if ( $storage->can("t2_insert_hook") ) {
		     $storage->t2_insert_hook( ref($obj), $coll_id, $field, $_[1] );
		 }

		 my $sql = ("UPDATE\n    $table\nSET\n    "
			    ."$item_col = $coll_id\nWHERE\n    "
			    ."$storage->{schema}{sql}{id_col} = $_[0]");
		 $storage->sql_do($sql);
	     },

	     sub
	     {
		 if ( $storage->can("t2_remove_hook") ) {
		     $storage->t2_remove_hook( ref($obj), $coll_id, $field, $_[1] );
		 }

		 if ($def->{aggreg}) {
		     my $id = shift;
		     my $oid = shift;
		     print $Tangram::TRACE "Tangram::Type::Set::FromOne: removing oid $oid\n"
			 if $Tangram::TRACE;
		     # FIXME - use dummy object
		     $storage->erase( $storage->load( $oid ));
		 } else {
		     my $sql = ("UPDATE\n    $table\nSET\n    "
				."$item_col = NULL\nWHERE\n    "
				."$storage->{schema}{sql}{id_col} = "
				."$_[0]    AND\n    $item_col = $coll_id");
		     $storage->sql_do($sql);
		 }
	     }
	    );
  }

sub demand
  {
	my ($self, $def, $storage, $obj, $member, $class) = @_;

	my $set = Set::Object->new();

	if (my $prefetch = $storage->{PREFETCH}{$class}{$member}{$storage->export_object($obj)})
	{
	    print $Tangram::TRACE "demanding ".$storage->id($obj)
		.".$member from prefetch\n" if $Tangram::TRACE;
		$set->insert(@$prefetch);
	}
	else
	{
	    print $Tangram::TRACE "demanding ".$storage->id($obj)
		.".$member from storage\n" if $Tangram::TRACE;

		my $cursor = Tangram::Cursor::Coll->new($storage, $def->{class}, $storage->{db});

		my $coll_id = $storage->export_object($obj);
		my $tid = $cursor->{TARGET}->object->{table_hash}{$def->{class}}; # leaf_table;
		$cursor->{-coll_where} = "t$tid.$def->{coll} = $coll_id";
   
		$set->insert($cursor->select);
	}

	$self->remember_state($def, $storage, $obj, $member, $set);

	return $set;
}

sub erase
{
	my ($self, $storage, $obj, $members, $coll_id) = @_;

	$coll_id = $storage->{export_id}->($coll_id);

	foreach my $member (keys %$members)
	{
		my $def = $members->{$member};

		if ( $storage->can("t2_remove_hook") ) {
		    $storage->t2_remove_hook
			(
			 ref($obj),
			 $coll_id,
			 $member,
			 (map { $storage->export_object($_) }
			  $obj->{$member}->members),
			);
		}

		if ($def->{aggreg})
		{
			$storage->erase( $obj->{$member}->members );
		}
		else
		{
		    my $item_classdef = $storage->{schema}{classes}{$def->{class}};
		    my $table = $item_classdef->{table} || $def->{class};
		    my $item_col = $def->{coll};
		    $storage->sql_do("UPDATE\n    $table\nSET\n    $item_col = NULL\nWHERE\n    $item_col = $coll_id");
		}
	}
}

sub query_expr
{
	my ($self, $obj, $members, $tid) = @_;
	map { Tangram::Expr::Coll::FromOne->new($obj, $_); } values %$members;
}

sub remote_expr
{
	my ($self, $obj, $tid) = @_;
	Tangram::Expr::Coll::FromOne->new($obj, $self);
}

sub prefetch
{
	my ($self, $storage, $def, $coll, $class, $member, $filter) = @_;

	my $ritem = $storage->remote($def->{class});

	my $prefetch = $storage->{PREFETCH}{$class}{$member} ||= {}; # weakref

	my $includes = $coll->{$member}->includes($ritem);
	$includes &= $filter if $filter;

	my $cursor = $storage->my_cursor( $ritem, filter => $includes, retrieve => [ $coll->{id} ] );
   
	while (my $item = $cursor->current)
	{
		my ($coll_id) = $cursor->residue;
		push @{ $prefetch->{$coll_id}||=[] }, $item;
		$cursor->next;
	}

	return $prefetch;
}

sub get_intrusions {
  my ($self, $context) = @_;
  return [ $self->{class}, $context->{mapping}->get_home_table($self->{class}) ];
}

$Tangram::Schema::TYPES{iset} = Tangram::Type::Set::FromOne->new;

#---------------------------------------------------------------------
#  Tangram::Type::Set::FromOne->coldefs($cols, $members, $schema, $class,
#                            $tables)
#
#  Setup column mappings for one to many unordered mappings (foreign
#  key)
#---------------------------------------------------------------------
sub coldefs
{
    my ($self, $cols, $members, $schema, $class, $tables) = @_;

    foreach my $member (values %$members)
    {
	my $table =
	    $tables->{ $schema->{classes}{$member->{class}}{table} }
		||= {};
	$table->{COLS}{$member->{coll}}
	    = "$schema->{sql}{id} $schema->{sql}{default_null}";
    }
}

1;
