###########################################
# POE Timed Process Launcher Component
# Mike Schilli, 2010 (m@perlmeister.com)
###########################################
package PoCoTimedProcess;
use strict;
use warnings;
use POE;
use POE::Wheel::Run;
use Log::Log4perl qw(:easy);

###########################################
sub new {
###########################################
  my($class, %options) = @_;

  my $self = { %options };
  bless $self, $class;
}

###########################################
sub launch {
###########################################
  my($self) = @_;

  $poe_kernel->post($self->{session},'up');
}

###########################################
sub spawn {
###########################################
  my($self) = @_;

  $self->{session} =
  POE::Session->create(
    inline_states => {
      _start => sub {
        my($h,$kernel) = @_[HEAP, KERNEL];

        $h->{is_up} = 0;
        $h->{command} = $self->{command};
        $h->{timeout} = $self->{timeout};
        $h->{heartbeat} = 
                       $self->{heartbeat};
        $kernel->yield('keep_alive');
        $kernel->yield('heartbeat');
      },
      sig_child => sub {
          delete $_[HEAP]->{wheel};
      },
      heartbeat => \&heartbeat,
      up   => \&up,
      down => \&down,
      keep_alive => sub {
        $_[HEAP]->{countdown} = 
          $_[HEAP]->{timeout};
      },
      closing => sub {
        $_[HEAP]->{is_up} = 0;
      },
   })->ID();
}

###########################################
sub heartbeat {
###########################################
  my($kernel, $heap) = @_[KERNEL, HEAP];

  $kernel->delay( "heartbeat", 
                  $heap->{heartbeat});

  if( $heap->{is_up} ) {
    INFO "Process is up for another ", 
      $heap->{countdown}, " seconds";

    $heap->{countdown} -= 
      $heap->{heartbeat};

    if($heap->{countdown} <= 0) {
        INFO "Time's up. Shutting down";
        $kernel->yield("down");
        return;
    }
  }
}

###########################################
sub up {
###########################################
  my ($heap, $kernel) = @_[ HEAP, KERNEL ];

  if($heap->{is_up}) {
      INFO "Is already up";
      $_[KERNEL]->yield('keep_alive');
      return 1;
  }

  my($prog, @args) = @{ $heap->{command} };

  $heap->{wheel} =
    POE::Wheel::Run->new(
      Program     => $prog,
      ProgramArgs => [@args],
      CloseEvent  => "closing",
      ErrorEvent  => "closing",
      StderrEvent => "ignore",
  ); 

  my $pid = $heap->{wheel}->PID();
  INFO "Started process $pid";

  $kernel->sig_child($pid, "sig_child");
  $kernel->sig( "INT"  => "down" );
  $kernel->sig( "TERM" => "down" );

  $_[KERNEL]->yield('keep_alive');
  $heap->{is_up} = 1;
}

###########################################
sub down {
###########################################
  my ($heap, $kernel) = @_[ HEAP, KERNEL ];

  if(! $heap->{is_up}) {
      INFO "Process already down";
      return 1;
  }

  INFO "Killing pid ", $heap->{wheel}->PID;
  $heap->{wheel}->kill();
  $heap->{is_up} = 0;
  $kernel->sig_handled();
}

1;
