#!/usr/bin/perl

package Class::Workflow::State::AutoApply;
use Moose::Role;

use Carp qw/croak/;
use Scalar::Util qw/refaddr/;

has auto_transition => (
	does      => "Class::Workflow::Transition",
	accessor  => "auto_transition",
	required  => 0,
);

around transitions => sub {
	my $next = shift;
	my ( $self, @transitions ) = @_;

	my @ret = $self->$next( @transitions );

	# if the auto transition was not in ->transitions( @set ) then delete it
	if ( @transitions and my $auto = $self->auto_transition ) {
		$self->auto_transition(undef) unless grep { $_ == $auto } @transitions;
	}

	if ( my $auto = $self->auto_transition ) {
		return $auto, @ret;
	} else {
		return @ret;
	}
};

around has_transition => sub {
	my $next = shift;
	my ( $self, $transition ) = @_;

	no warnings 'uninitialized';
	return ( $self->$next($transition) || ( refaddr($self->auto_transition) == refaddr($transition) ) );
};

around has_transitions => sub {
	my $next = shift;
	my ( $self, @transitions ) = @_;

	if ( my $auto = $self->auto_transition ) {
		@transitions = grep { $_ != $auto } @transitions;
	}

	return $self->$next( @transitions );
};

around accept_instance => sub {
	my $next = shift;
	my ( $self, $orig_instance, @args ) = @_;
	my $instance = $self->$next( $orig_instance, @args );

	return $self->apply_auto_transition( $instance, @args ) || $instance;
};

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

	if ( my $auto_transition = $self->auto_transition ) {
		return $auto_transition->apply( $instance, @args );
	}

	return;
}

__PACKAGE__;

__END__

=pod

=head1 NAME

Class::Workflow::State::AutoApply - Automatically apply a transition upon
arriving into a state.

=head1 SYNOPSIS

	package MyState;
	use Moose;

	with qw/Class::Workflow::State::AutoApply/;
	
	my $state = Mystate->new( auto_transition => $t );

	my $i2 = $state->accept_instance( $i, @args ); # automatically calls $t->apply( $i, @args )

=head1 DESCRIPTION

This state role is used to automatically apply a transition

=head1 PARTIAL TRANSITIONS

If an auto-application may fail validation or something of the sort you can do
something like:

	around apply_auto_transition => sub {
		my $next = shift;
		my ( $self, $instance, @args ) = @_;

		eval { $self->$next( $instance, @args ) }

		die $@ unless $@->isa("SoftError");
	}

If apply_auto_transition returns a false value then the original instance will
be returned automatically, at which point the intermediate state is the current
state.

=cut


