#!/usr/bin/perl -I../lib -I..

BEGIN {
	$OOPS::SelfFilter::defeat = 1
		unless defined $OOPS::SelfFilter::defeat;
}
BEGIN {
	unless ( eval { require 5.008003 } ) {
		print "1..0 # Skipped: perl 5.8.3 required for scalar(%tied_hash) to work\n";
		exit;
	}
}
BEGIN {
	for my $m (qw(Data::Dumper Clone::PP Data::Compare)) {
		unless ( eval " require $m " ) {
			print "1..0 # Skipped: this test requires the $m module\n";
			exit;
		}
		$m->import();
	}
}

import Clone::PP qw(clone);
import Data::Compare;

use OOPS;
use Carp qw(confess);
use Scalar::Util qw(reftype);
use strict;
use warnings;
use diagnostics;

use OOPS::TestCommon;

modern_data_compare();
print "1..1691\n";

resetall; # --------------------------------------------------
{
	my $number = 0;
	my $FAIL = <<'END';

END
	sub selector {
		return 1 if 1 || $number == 1;
		return 0;
	}
	my $tests = <<'END';
		my $x = exists $root->{h2}{b};
		%{$root->{h2}} = ();

		my $x = $root->{h2}{b};
		%{$root->{h2}} = ();

		$root->{h2}{b} = '22';
		%{$root->{h2}} = ();

		my $x = $root->{h2}{b};
		delete @{$root->{h2}}{'a','b'};

		$root->{h2}{b} = '22';
		delete @{$root->{h2}}{'a','b'};

		%{$root->{h2}} = ();
		---
		$root->{h2}{z} = 'q';
		---
		delete $root->{h2}{z};

		delete @{$root->{h2}}{'a','b'};

		my $x = exists $root->{h2}{b};
		delete @{$root->{h2}}{'a','b'};

		$root->{h1}{x} = 2;
		---
		delete $root->{h1}{x};

		%{$root->{h2}} = ();
END
	
	for my $test (split(/^\s*$/m, $tests)) {
		#
		# commit after each test?
		# samesame after each not-final test?
		# samesame after final
		#
		$number++;
		next unless &selector($number);
		my %conf;
		$test =~ s/\A[\n\s]+//;
		$conf{$1} = [ split(' ', $2) ]
			while $test =~ s/(V|S|C)=(.*)\n\s*//;
		my (@tests) = split(/\n\s+---\s*\n/, $test);
		my (@func);
		for my $t (@tests) {
			eval "push(\@func, sub { my \$root = shift; $t })";
			die "eval <<$t>>of<$test>: $@" if $@;
		}
		my (@virt) = $conf{V}
			? @{$conf{V}}
			: (qw(0 virtual));
		my (@commits) = $conf{C}
			? @{$conf{C}}
			: (0..2**(@tests));
		my (@ss) = $conf{S}
			? @{$conf{S}}
			: (0..2**(@tests -1));

		my $mroot;
		my $proot;
		for my $vobj (@virt) {
			for my $docommit (@commits) {
				for my $dosamesame (@ss) {
					resetall;
					my $x = 'rval';
					$mroot = {
						h1 => { },
						h2 => { a => 1, b => [] },
					};

					$r1->{named_objects}{root} = clone($mroot);
					$r1->virtual_object($r1->{named_objects}{root}{h1}, $vobj) if $vobj;
					$r1->virtual_object($r1->{named_objects}{root}{h2}, $vobj) if $vobj;
					$r1->commit;
					rcon;

					my $sig = "N=$number.V=$vobj.C=$docommit.S=$dosamesame-$test";

					for my $tn (0..$#func) {
						my $tf = $func[$tn];
						$proot = $r1->{named_objects}{root};

						print "# $sig\n" if $debug;
						print "# EXECUTING $tests[$tn]\n" if $debug;
						&$tf($mroot);
						&$tf($proot);

						$r1->commit
							if $docommit & 2**$tn;
						print "# COMPARING\n" 
							if $dosamesame & 2**$tn && $debug;
						test(sdocompare($mroot, $proot), "<$tn>$sig")
							if $dosamesame & 2**$tn;
						rcon
							if $tn < $#func && $docommit & 2**$tn;
					}
					print "# FINAL COMPARE\n" if $debug;
					test(sdocompare($mroot, $proot), "<END>$sig")
				}
			}
		}

		rcon;

		if (exists $r1->{named_objects}{root}{acircular}) {
			@{$r1->{named_objects}{root}{acircular}} = ();
			delete $r1->{named_objects}{root}{acircular};
		}
		delete $r1->{named_objects}{root};
		$r1->commit;
		rcon;
		notied;
	}
}

sub sdocompare
{
	my ($x, $y) = @_;

	for my $k (keys %$x) {
		next unless reftype($x->{$k}) eq 'HASH';
		next if !!scalar(%{$x->{$k}}) == !!scalar(%{$y->{$k}});
		print "# scalar(%{\$x->{$k}}) = ".scalar(%{$x->{$k}}).", scalar(%{\$y->{$k}}) = ".scalar(%{$y->{$k}})."\n";
		print "# keys %{\$x->{$k}} = ".join(' ', keys %{$x->{$k}})."\n";
		print "# keys %{\$y->{$k}} = ".join(' ', keys %{$y->{$k}})."\n";
		return 0;
	}

	my $r = Compare($x, $y);
	return $r if $r;

	my $c1 = ref2string($x);
	my $c2 = ref2string($y);
	return 1 if $c1 eq $c2;
	# print "c1=$c1\nc2=$c2\n";

	return 0;
}

print "# ---------------------------- done ---------------------------\n" if $debug;
$okay--;
print "# tests: $okay\n" if $debug;

exit 0; # ----------------------------------------------------

1;

