package MooseX::JobControl::Engine::POEScheduled;

use strict;
use Moose;
use MooseX::AttributeHelpers;
use Time::HiRes;
use POE;

extends qw(MooseX::JobControl::Engine);

has session => (
    isa      => 'POE::Session',
    is       => 'ro',
    required => 1,
    default  => sub {
        POE::Session->create(
            object_states => [
                $_[0] => [qw(
                    _start
                    _schedule
                    _schedule_all
                    _run_job
                )],
            ],
        );
    },
);

has alarm_ids => (
    metaclass => 'Collection::Hash',
    is        => 'ro',
    isa       => 'HashRef',
    default   => sub { {} },
    provides  => {
        'set'    => 'set_alarm',
        'exists' => 'alarm_exists',
    },
);

sub yield {
    my $self = shift;
    $poe_kernel->post( $self->session => @_ );
}

sub call {
    my $self = shift;
    return $poe_kernel->call( $self->session => @_ );
}

sub run {
    my $self = shift;
    POE::Kernel->run;
}

after 'add_job' => sub {
    my ($self, $job) = @_;
    $self->set_alarm( $self->call( _schedule => $job ) );
};

# POE Events
sub _start {
    $_[KERNEL]->yield( '_schedule_all' );
}

sub _schedule_all {
    my ($self, $kernel) = @_[OBJECT, KERNEL];
    for my $job ($self->jobs){
        $self->set_alarm( $self->call( _schedule => $job ) )
            unless $self->alarm_exists( $job->uuid );
    }
}

sub _schedule {
    my ($self, $kernel, $job) = @_[OBJECT, KERNEL, ARG0];

    my $alarm_id = $kernel->alarm_set(
        '_run_job'
        => $job->next( DateTime->now )->epoch
        => $job
    );

    return ($job->uuid, $alarm_id);
}

sub _run_job {
    my ($self, $kernel, $job) = @_[OBJECT, KERNEL, ARG0];

    $self->add_process( $self->spawn( $job->command, $job->args ) );

    $self->yield( _schedule => $job );
}

1;
