package Logic;

use Logic::Var;
use Logic::Guesses;
# later eval "use Logic::Let"!

use Exporter;

use strict;
use vars qw(@ISA @EXPORT);

@ISA = qw(Exporter);
@EXPORT = qw(logic illogic attach let guess fail);

use vars qw(%rels_by_var %vars_by_rel %rels_pending $guesses $propagating $active_rel);

%rels_by_var;
%vars_by_rel;
%rels_pending;
$guesses = Logic::Guesses->new;

$propagating = 0;
$active_rel;
sub propagate {
	return if $propagating;
	$propagating = 1;

	while (%rels_pending) {
		my @rels = values %rels_pending;
		%rels_pending = ();
		for $active_rel (@rels) {
			$active_rel->activate;
		} # this assumes thæt for localises the variable - hope true for all Perl versions?
	}
	$propagating = 0;
}

sub activate {
	my ($rel) = @_;
	$rels_pending{$rel} = $rel;
	propagate;
}

sub attach {
	my ($rel, @v) = @_;
	for (@v) {
		push @{$rels_by_var{\$$_}}, $rel;  # \$$var seems to lose the "" overload
		push @{$vars_by_rel{$rel}}, $_;
	}
	$rels_pending{$rel} = $rel;
	propagate; # this means we might get nested propagate loops - could be a bit nasty?
}

sub let {
	while (my ($var, $val) = splice @_, 0, 2) {
		if (ref $val && $val->isa("Logic::Var")) {
			eval "use Logic::Let";
			Logic::Let->new($var, $val);
		} else {
			if (defined $$var) {
				if ($$var ne $val) {
					fail();
				}
			} else {
				$$var = $val;
				$guesses->let($var);
				if (exists $rels_by_var{\$$var}) {
					for my $rel (@{$rels_by_var{\$$var}}) {
						next if $rel eq $active_rel;
						$rels_pending{$rel} = $rel;
					}
				}
			}
		}
	}
	propagate;
}

sub guess {
	$guesses->guess($active_rel);
	let(@_);
}

sub fail {
	$guesses->fail;
}

sub logic {
	for (@_) {
		$_ = Logic::Var->new($_) unless ref $_ && $_->isa("Logic::Var");
	}
	return wantarray ? @_ : $_[-1];
}

sub illogic {
	for (@_) {
		$_->illogic if ref $_ && $_->isa("Logic::Var");
	}
	return wantarray ? @_ : $_[-1];
}

1
