#!/usr/bin/perl

use strict;
use warnings;
use Test::More tests => 22;
use Test::Exception;
use Aspect;
use Aspect::Library::Listenable;

# Set up the Listenable relationship type
aspect Listenable => (
	Erase => call "Foo::set_erased"
);
aspect Listenable => (
	Color => call "Foo::set_color",
	color => 'get_color',
);





######################################################################
# Main Tests

SCOPE: {
	my $has_been_called = 0;
	my $point = Foo->new;
	add_listener( $point, Erase => my $listener = sub { $has_been_called = 1 } );
	$point->set_erased;
	ok( $point->get_erased, 'has been erased' );
	ok( $has_been_called, 'has been called' );
	remove_listener( $point, Erase => $listener );
}

SCOPE: {
	my $has_been_called1 = 0;
	my $has_been_called2 = 0;
	my $point = Foo->new;
	add_listener( $point, Erase => my $listener1 = sub { $has_been_called1 = 1 } );
	add_listener( $point, Erase => my $listener2 = sub { $has_been_called2 = 1 } );
	$point->set_erased;
	ok( $has_been_called1, 'listener 1' );
	ok( $has_been_called2, 'listener 2' );
	remove_listener( $point, Erase => $listener1 );
	remove_listener( $point, Erase => $listener2 );
}

SCOPE: {
	my $has_been_called1 = 0;
	my $has_been_called2 = 0;
	my $point1 = Foo->new;
	my $point2 = Foo->new;
	add_listener( $point1, Erase => my $listener1 = sub { $has_been_called1 = 1 } );
	add_listener( $point2, Erase => my $listener2 = sub { $has_been_called2 = 1 } );
	$point2->set_erased;
	ok( !$point1->get_erased, 'point 1'  );
	ok( $point2->get_erased, 'point 2'   );
	ok( !$has_been_called1, 'listener 1' );
	ok( $has_been_called2, 'listener 2'  );
	remove_listener( $point1, Erase => $listener1 );
	remove_listener( $point2, Erase => $listener2 );
}

SCOPE: {
	my $has_been_called1 = 0;
	my $has_been_called2 = 0;
	my $point = Foo->new;
	add_listener( $point, Erase => my $listener1 = sub { $has_been_called1 = 1 } );
	add_listener( $point, Erase => my $listener2 = sub { $has_been_called2 = 1 } );
	remove_listener( $point, Erase => $listener1 );
	$point->set_erased;
	ok( !$has_been_called1, 'listener 1' );
	ok( $has_been_called2, 'listener 2'  );
	remove_listener( $point, Erase => $listener2 );
}

SCOPE: {
	my $event = 0;
	my $point = Foo->new;
	add_listener( $point, Color => my $listener = sub { $event = shift } );
	$point->set_color('red');
	is( $point->get_color, 'red', 'point color' );
	ok( $event, 'event fired' );
	is( ref $event, 'Aspect::Library::Listenable::Event', 'event class' );
	is( $event->name, 'Color', 'name' );
	is( $event->source, $point, 'source' );
	is( $event->color, 'red', 'color' );
	is( $event->old_color, 'blue', 'old_color' );
	is_deeply( $event->params, ['red'], 'params' );
	remove_listener( $point, Color => $listener );
}

SCOPE: {
	my $hasnt_been_called = 1;
	my $point = Foo->new;
	add_listener( $point, Color => my $listener = sub { $hasnt_been_called = 0 } );
	$point->set_color('blue');
	ok( $hasnt_been_called );
	remove_listener( $point, Color => $listener );
}

SCOPE: {
	my $listenable = bless [], 'SomePackage';
	throws_ok(
		sub { add_listener( $listenable, Event => sub {} ) },
		qr/not a hash based object/,
	);
}

SCOPE: {
	my $point = Foo->new;
	add_listener( $point, Erase => [do_call =>
		my $listener = Bar->new,
		[qw(source)],
	] );
	ok( !$listener->has_been_called, 'before erased' );
	$point->set_erased;
	is( $listener->has_been_called, $point, 'after erased' );
	remove_listener( $point, Erase => $listener );
}





######################################################################
# Support Classes

package Foo;

sub new {
	bless {
		color  => 'blue',
		erased => 0,
	}, shift;
}

sub get_erased { shift->{erased} }
sub set_erased { shift->{erased} = 1 }
sub get_color  { shift->{color} }
sub set_color  { shift->{color} = pop }

package Bar;

sub new {
	bless {
		has_been_called => 0,
	}, shift;
}
sub do_call         { shift->{has_been_called} = pop }
sub has_been_called { shift->{has_been_called} }
