package Notification::Center;

use Moose;
use Scalar::Util qw(refaddr);
use Set::Object;

our $AUTHORITY = 'CPAN:RLB';
our $VERSION   = '0.0.5';

has observers => (
    is      => 'ro',
    isa     => 'HashRef[Set::Object]',
    default => sub {
        { DEFAULT => Set::Object->new };
    }
);

has method_calls => (
    is      => 'ro',
    isa     => 'HashRef',
    default => sub { {} }
);

my $instance;
sub default {
    return $instance ||= Notification::Center->new;
};

sub add {
    my ( $self, $args ) = @_;

    my $event    = $args->{'event'} || 'DEFAULT';
    my $observer = $args->{'observer'};
    my $method   = $args->{'method'} || 'update';

    $self->observers->{$event} ||= Set::Object->new;

    $self->observers->{$event}->insert($observer);
    $self->method_calls->{ refaddr $observer }->{$event} = $method;
}

sub remove {
    my ( $self, $args ) = @_;

    my $event = $args->{'event'} || 'DEFAULT';
    my $observer = $args->{'observer'};

    delete $self->method_calls->{ refaddr $observer }->{$event};
    $self->observers->{$event}->remove($observer);
}

sub notify {
    my ( $self, $args ) = @_;

    my $event = $args->{'event'};

    return if !exists $self->observers->{$event};

    my $observers;
    if ( $event ne 'DEFAULT' ) {
        $observers = Set::Object->new( $self->observers->{$event}->members, $self->observers->{'DEFAULT'}->members );
    }
    else {
        $observers = $self->observers->{$event};
    }

    foreach my $observer ( $observers->members ) {
        my $method = $self->method_calls->{ refaddr $observer }->{$event} ||
            $self->method_calls->{ refaddr $observer }->{'DEFAULT'};
        $observer->$method($args->{'args'}) if $observer->can($method);
    }
}

1;

__END__

=pod

=head1 NAME

Notification::Center - An observer/notification for perl

=head1 SYNOPSIS

    {

        package Counter;

        use Moose;
        use Notification::Center;

        has count => ( is => 'rw', isa => 'Int', default => 0 );

        sub inc {
            my ($self) = @_;
            $self->count( $self->count + 1 );
            my $nc = Notification::Center->default;
            $nc->notify( { event => 'count', args => $self->count } );
        }

        sub dec {
            my ($self) = @_;
            $self->count( $self->count - 1 );
            my $nc = Notification::Center->default;
            $nc->notify( { event => 'count', args => $self->count } );
        }

        no Moose;

        package TrackCount;

        use Moose;

        has count => ( is => 'rw', isa => 'Int', default => 0 );

        sub print {
            my ($self) = @_;
            print $self->count;
        }

        sub get_count {
            my ( $self, $count ) = @_;
            $self->count($count);
        }

        no Moose;
    }

    my $count = Counter->new;

    my $mn = Notification::Center->default;
    my $tc = TrackCount->new;
    $mn->add(
        {
            observer => $tc,
            event    => 'count',
            method   => 'get_count',
        }
    );

    for ( 1 .. 10 ) {
        $count->inc;
    }
    for ( 1 .. 5 ) {
        $count->dec;
    }

    $tc->print;    # 5


    or use IOC using Bread::Board

    use Bread::Board;

    my $c = container 'TestApp' => as {

        service 'fname' => 'Larry';
        service 'lname' => 'Wall';

        service 'notification_center' => (
            class     => 'Notification::Center',
            lifecycle => 'Singleton',
        );

        service 'person' => (
            class        => 'Person',
            dependencies => {
                notification => depends_on('notification_center'),
                fname        => depends_on('fname'),
                lname        => depends_on('lname'),
            },
        );

        service 'upn' => (
            class        => 'UCPrintName',
            dependencies => { notification => depends_on('notification_center') },
        );

        service 'pn' => (
            class        => 'PrintName',
            dependencies => { notification => depends_on('notification_center'), },
        );
    };

    my $pn     = $c->fetch('pn')->get;
    my $upn    = $c->fetch('upn')->get;
    my $person = $c->fetch('person')->get;
    my $nc     = $c->fetch('notification_center')->get;

    $person->print_name;
    $nc->remove( { observer => $upn } );
    $person->print_name;


=head1 DESCRIPTION

An observer/notification center based on the objective-c NSNotificationCenter Class

=over

=item new

The method creates a new instance of Notification::Center object

=item default

This method creates a singleton of the Notification::Center object

=item add

args keys: observer, event, method

observer: the object that will observer events

event: the name of the event that you are assigning the observer. Defaults to DEFAULT

method: the method you want called on the observer when the event is called. Defaults to update

=item remove

args keys: observer, event

observer: the object that you want to remove

event: the name of the event that you are removing the observer. Defaults to DEFAULT

=item notify

args keys : event, args

event: the event you want to trigger

args: data you want to pass into observers

=back

=head1 GIT Repository

http://github.com/rlb3/notification-center/tree/master

=cut