#!perl -wT

use lib 't';
use Test::More tests =>
	+1  # use
	+14 # tie to obj
	+15 # is_tied
	+14 # weak_tie
	+7  # is_tied again (stale ties)
	+16 # weaken_tie
	+28 # is_weak_tie
	+1  # $@
	+1  # weak_tie retval
	+10 # tie to non-obj
	+1  # tied
	+1  # fix_tie
;
use Symbol 'geniosym';

BEGIN { use_ok 'Tie::Util' };

no warnings 'once';

{ package overloaded;
	use overload fallback => 1,
	'${}' => sub { \my $v },
	'@{}' => sub { [] },
	'%{}' => sub { +{} },
	'&{}' => sub { my $v; sub { $v } },
	'*{}' => sub { \*oeutnhnoetunhnt },
	 bool => sub{0};
	*TIESCALAR = *TIEHASH = *TIEARRAY = *TIEHANDLE =
	sub { bless $_[1] };

#	# This makes overload::Method return false:
#	bless overload::Method(__PACKAGE__, "$_\{}") for qw/ $ @ % & * /;
# But it also makes perl crash. So I suppose I don’t need to be paranoid
# about a case that can’t happen anyway.
}

$} = geniosym; $% = geniosym;
bless $_, 'overloaded'
	for \($~,@~,%~,*~,$%,@%,%%,*%,$`[0],$`{0},*${}},*{$%});
# makes it harder for the functions to get at the tied variable

*TIESCALAR = *TIEHASH = *TIEARRAY = *TIEHANDLE =
	sub { bless $_[1] };

sub UNTIE { ++$untied };

my $obj = bless[];
is tie($~, to => $obj), $obj, 'return value of tie$to';
is tie(@~, to => $obj), $obj, 'return value of tie@to';
is tie(%~, to => $obj), $obj, 'return value of tie%to';
is tie(*~, to => $obj), $obj, 'return value of tie*to';
is tie($`{0}, to => $obj), $obj, 'return value of tie${}to';
is tie($`[0], to => $obj), $obj, 'return value of tie$[]to';
is tie(*${}}, to => $obj), $obj, 'return value of tie*{IO}to';
is tied($~), $obj, 'tie$to works';
is tied(@~), $obj, 'tie@to works';
is tied(%~), $obj, 'tie%to works';
is tied(*~), $obj, 'tie*to works';
is tied($`{0}), $obj, 'tie${}to works';
is tied($`[0]), $obj, 'tie$[]to works';
is tied(*${}}), $obj, 'tie*{IO}to works';

# These lines were making is_tied return true for @% and %%, until
# I fixed it:
tie $%[0], to => $obj;
tie $%{0}, to => $obj;

is is_tied($~), 1, 'is_tied$';
is is_tied(@~), 1, 'is_tied@';
is is_tied(%~), 1, 'is_tied%';
is is_tied(*~), 1, 'is_tied*';
is is_tied($`[0]), 1, 'is_tied$[]';
is is_tied($`{0}), 1, 'is_tied${}';
is is_tied(*${}}), 1, 'is_tied*{IO}';
is is_tied($%), '', '!is_tied$';
is is_tied(@%), '', '!is_tied@';
is is_tied(%%), '', '!is_tied%';
is is_tied(*%), '', '!is_tied*';
is is_tied($@[0]), '', '!is_tied$[]';
is is_tied($@{0}), '', '!is_tied${}';
is is_tied(*{$%}), '', '!is_tied*{IO}';
tie @%, overloaded, bless[],overloaded;
ok is_tied(@%), 'is_tied @tied_to_bool_false_obj';

{	my $foo = bless[0];
	untie$~,untie@~,untie(%~),untie(*~),untie$`[0],untie$`{0},untie*${}};
	weak_tie $~, '', $foo;
	weak_tie @~, '', $foo;
	weak_tie %~, '', $foo;
	weak_tie *~, '', $foo;
	weak_tie $`[0], '', $foo;
	weak_tie $`{0}, '', $foo;
	weak_tie *${}}, '', $foo;
	is tied($~), $foo, 'weak_tie$';
	is tied(@~), $foo, 'weak_tie@';
	is tied(%~), $foo, 'weak_tie%';
	is tied(*~), $foo, 'weak_tie*';
	is tied($`[0]), $foo, 'weak_tie$[]';
	is tied($`{0}), $foo, 'weak_tie${}';
	is tied(*${}}), $foo, 'weak_tie*{IO}'; }
is tied($~), undef, 'weak_tie$ gone stale';
is tied(@~), undef, 'weak_tie@ gone stale';
is tied(%~), undef, 'weak_tie% gone stale';
is tied(*~), undef, 'weak_tie* gone stale';
is tied($`[0]), undef, 'weak_tie$[] gone stale';
is tied($`{0}), undef, 'weak_tie${} gone stale';
is tied(*${}}), undef, 'weak_tie*{IO} gone stale';

is is_tied($~), 1, 'is_tied $stale_tie';
is is_tied(@~), 1, 'is_tied @stale_tie';
is is_tied(%~), 1, 'is_tied %stale_tie';
is is_tied(*~), 1, 'is_tied *stale_tie';
is is_tied($`[0]), 1, 'is_tied $stale[tie]';
is is_tied($`{0}), 1, 'is_tied $stale{tie}';
is is_tied(*${}}), 1, 'is_tied *stale_tie{IO}';

untie($~), untie(@~), untie(%~), untie(*~), untie($`[0]), untie($`{0}), 
untie(*${}});
$untied = 0; # weaken_tie has to clobber the UNTIE method temporarily in
             # the package into which the object to which the variable is
             # tied is blessed.
{	my $foo = bless[0];
	tie $~, '', $foo;
	tie @~, '', $foo; # strong
	tie %~, '', $foo; # ties
	tie *~, '', $foo;
	tie $`[0], '', $foo;
	tie $`{0}, '', $foo;
	tie *${}}, '', $foo;
	weaken_tie $~;    # not
	weaken_tie @~;    # any
	weaken_tie %~;    # more
	weaken_tie *~;
	weaken_tie $`[0];
	weaken_tie $`{0};
	weaken_tie *${}};
	is tied($~), $foo, 'weaken_tie$ before staleness';
	is tied(@~), $foo, 'weaken_tie@ before staleness';
	is tied(%~), $foo, 'weaken_tie% before staleness';
	is tied(*~), $foo, 'weaken_tie* before staleness';
	is tied($`[0]), $foo, 'weaken_tie$[] before staleness';
	is tied($`{0}), $foo, 'weaken_tie${} before staleness';
	is tied(*${}}), $foo, 'weaken_tie*{IO} before staleness'; }
is tied($~), undef, 'weaken_tie$ gone stale and mouldy';
is tied(@~), undef, 'weaken_tie@ gone stale';
is tied(%~), undef, 'weaken_tie% gone stale';
is tied(*~), undef, 'weaken_tie* stalemate';
is tied($`[0]), undef, 'weaken_tie$[] stalemate';
is tied($`{0}), undef, 'weaken_tie${} stalemate';
is tied(*${}}), undef, 'weaken_tie*{IO} stalemate';
is $untied, 0, 'UNTIE is not called inadvertently';
ok defined &UNTIE, 'UNTIE was not inadvertently deleted';

{	my $foo = bless[0];
	untie($~), untie(@~), untie(%~), untie(*~), untie($`[0]), 
	untie($`{0}), untie(*${}});
	tie $~, '', $foo;
	tie @~, '', $foo; # strong
	tie %~, '', $foo; # ties
	tie *~, '', $foo;
	tie $`[0], '', $foo;
	tie $`{0}, '', $foo;
	tie *${}}, '', $foo;
	is is_weak_tie($~), '', 'is_weak_tie$ with strong tie';
	is is_weak_tie(@~), '', 'is_weak_tie@ with strong tie';
	is is_weak_tie(%~), '', 'is_weak_tie% with strong tie';
	is is_weak_tie(*~), '', 'is_weak_tie* with strong tie';
	is is_weak_tie($`[0]), '', 'is_weak_tie$[] with strong tie';
	is is_weak_tie($`{0}), '', 'is_weak_tie${} with strong tie';
	is is_weak_tie(*${}}), '', 'is_weak_tie*{IO} with strong tie';
	weaken_tie $~;    # not
	weaken_tie @~;    # any
	weaken_tie %~;    # more
	weaken_tie *~;
	weaken_tie $`[0];
	weaken_tie $`{0};
	weaken_tie *${}};
	is is_weak_tie($~), 1, 'is_weak_tie$ with weak tie';
	is is_weak_tie(@~), 1, 'is_weak_tie@ with weak tie';
	is is_weak_tie(%~), 1, 'is_weak_tie% with weak tie';
	is is_weak_tie(*~), 1, 'is_weak_tie* with weak tie';
	is is_weak_tie($`[0]), 1, 'is_weak_tie$[] with weak tie';
	is is_weak_tie($`{0}), 1, 'is_weak_tie${} with weak tie';
	is is_weak_tie(*${}}), 1, 'is_weak_tie*{IO} with weak tie'; }
is is_weak_tie($~), '', 'is_weak_tie$ with stale tie';
is is_weak_tie(@~), '', 'is_weak_tie@ with stale tie';
is is_weak_tie(%~), '', 'is_weak_tie% with stale tie';
is is_weak_tie(*~), '', 'is_weak_tie* with stale tie';
is is_weak_tie($`[0]), '', 'is_weak_tie$[] with stale tie';
is is_weak_tie($`{0}), '', 'is_weak_tie${} with stale tie';
is is_weak_tie(*${}}), '', 'is_weak_tie*{IO} with stale tie';
is is_weak_tie($^), undef, 'is_weak_tie$ with no tie';
is is_weak_tie(@^), undef, 'is_weak_tie@ with no tie';
is is_weak_tie(%^), undef, 'is_weak_tie% with no tie';
is is_weak_tie(*^), undef, 'is_weak_tie* with no tie';
is is_weak_tie($@[0]), undef, 'is_weak_tie$[] with no tie';
is is_weak_tie($@{0}), undef, 'is_weak_tie${} with no tie';
is is_weak_tie(*{$%}), undef, 'is_weak_tie*{IO} with no tie';

{
	local *@;
	ok eval{
		weak_tie $@, to => bless[];
		'' eq is_weak_tie $@;
	}, 'tying of $@';
}

{
	my $ref = \weak_tie my $bar, to => my $baz = bless[];
	$$ref = 27;
	is tied $bar, 27, 'retval of weak_tie';
}

untie($~), untie(@~), untie(%~), untie(*~), untie($`[0]), 
untie($`{0}), untie(*${}});
Tie::Util::tie($~, to => 37);
Tie::Util::tie(@~, to => 37);
Tie::Util::tie(%~, to => 37);
Tie::Util::tie(*~, to => 37);
Tie::Util::tie($`{0}, to => 37);
Tie::Util::tie($`[0], to => 37);
Tie::Util::tie(*${}}, to => 37);
is tied($~), 37, 'tie$ to non-obj';
is tied(@~), 37, 'tie@ to non-obj';
is tied(%~), 37, 'tie% to non-obj';
is tied(*~), 37, 'tie* to non-obj';
is tied($`{0}), 37, 'tie${} to non-obj';
is tied($`[0]), 37, 'tie$[] to non-obj';
is tied(*${}}), 37, 'tie*{IO} to non-obj';
{
	my $ref =\ Tie::Util::tie $., to => \1;
	is_deeply tied $., \1, 'tie to unblessed ref';
	$$ref = Foo;
	is tied $., Foo, 'tie retval';
	sub Foo::FETCH{ 42 }
	is $., 42, 'package tie'
}

{
	my $ref = \tie my $foo, to => bless[];
	is \Tie::Util::tied($foo), $ref, 'tied'
}

{ # based on [perl #68192]
 package dwin;
 sub TIESCALAR { bless {}, __PACKAGE__ };
 sub STORE {};
 sub FETCH { 123456 };

 my $foo;
 tie $foo, __PACKAGE__;

 my $a = [1234567];

 my $x = 0 + $foo;
 use Tie'Util 'fix_tie';
 fix_tie($foo = $a);
 my $y = 0 + $foo;
 
 ::is($x, $y, 'fix_tie')
}
