use strict;
package Event::process;
use Carp;
use Event qw(time);
use base 'Event::Watcher';
use vars qw($DefaultPriority);
$DefaultPriority = Event::PRIO_HIGH();

'Event::Watcher'->register();

sub new {
    #lock %Event::;

    shift if @_ & 1;
    my %arg = @_;
    my $o = 'Event::process'->allocate();
    $o->init([qw(pid timeout)], \%arg);
    $o->{any} = 1 if !exists $o->{pid};
    $o->start();
    $o;
}

my %cb;		# pid => [events]

Event->signal(signal => 'CHLD',  #CLD? XXX
	      callback => sub {
		  my ($o) = @_;
		  for (my $x=0; $x < $o->{count}; $x++) {
		      my $pid = wait;
		      last if $pid == -1;
		      my $status = $?;
		      
		      my $cbq = delete $cb{$pid} if exists $cb{$pid};
		      $cbq ||= $cb{any} if exists $cb{any};
		      
		      next if !$cbq;
		      for my $e (@$cbq) {
			  $e->{pid} = $pid;
			  $e->{status} = $status;
			  Event::queue($e);
		      }
		  }
	      },
	      desc => "Event::process SIGCHLD handler");

sub _start {
    my ($o, $repeat) = @_;
    my $key = exists $o->{any}? 'any' : $o->{pid};
    push @{$cb{ $key } ||= []}, $o;
    if (exists $o->{timeout}) {
	croak "Timeout for all child processes?" if $o->{any};
	$o->{at} = time + $o->{timeout};
    }
}

sub _stop {
    my $o = shift;
    my $key = exists $o->{any}? 'any' : $o->{pid};
    $cb{ $key } = [grep { $_->{id} != $o->{id} } @{$cb{ $key }} ];
    delete $cb{ $key } if
	@{ $cb{ $key }} == 0;
}

sub _alarm {
    my $o = shift;
    delete $o->{status};
    Event::queue($o);
}

sub _postCB {
    my $o = shift;
    if (exists $o->{timeout}) {
	delete $o->{timeout};
	$o->again;
    }
}

1;