package AnyEvent::Impl::Perl;

no warnings;
use strict;

use Time::HiRes qw(time);
use Scalar::Util ();

our $VERSION = 0.1;

my ($fds_r, $fds_w) = ({ ref => {} }, { ref => {} });
my @timer;
my $need_sort;

sub add_fh($$) {
   my ($self, $fds) = @_;

   (vec $fds->{v}, $self->{fd}, 1) = 1
      unless $fds->{w}{$self->{fd}};

   push @{ $fds->{w}{$self->{fd}} }, $self;
   Scalar::Util::weaken $fds->{w}{$self->{fd}}[-1];
}

sub del_fh($$) {
   my ($self, $fds) = @_;

   if (@{ $fds->{w}{$self->{fd}} } == 1) {
      delete $fds->{w}{$self->{fd}};
      (vec $fds->{v}, $self->{fd}, 1) = 0;
   } else {
      $fds->{w}{$self->{fd}} = [
         grep $_ != $self, @{ $fds->{w}{$self->{fd}} }
      ];
   }
}

sub fds_chk($$) {
   my ($fds, $vec) = @_;

   for my $fd (keys %{ $fds->{w} }) {
      if (vec $vec, $fd, 1) {
         $_->{cb}()
            for @{ $fds->{w}{$fd} || [] };
      }
   }
}

# the pure perl mainloop
sub one_event {
   # 1. sort timers if required (slow)
   if ($need_sort) {
      undef $need_sort;
      @timer = sort { $a->[0] <=> $b->[0] } @timer;
   }

   my $NOW = time;

   # 2. check timers
   if (@timer && $timer[0][0] <= $NOW) {
      my $timer = shift @timer;
      $timer->[1]{cb}() if $timer->[1];
      return;
   }

   # 3. select
   my $fds = select
      my $r = $fds_r->{v},
      my $w = $fds_w->{v},
      undef,
      @timer ? $timer[0][0] - $NOW  + 0.0009 : 3600;

   if ($fds) {
      fds_chk $fds_w, $w;
      fds_chk $fds_r, $r;
   }
}

sub io {
   my ($class, %arg) = @_;

   $arg{fd} = fileno $arg{fh};
   
   my $self = bless \%arg, $class;

   $self->add_fh ($fds_r) if $self->{poll} eq "r";
   $self->add_fh ($fds_w) if $self->{poll} eq "w";

   $self
}

sub timer {
   my ($class, %arg) = @_;
   
   my $self = bless \%arg, $class;

   push @timer, [time + $self->{after}, $self];
   Scalar::Util::weaken $timer[-1][1];
   $need_sort = 1;

   $self
}

sub DESTROY {
   my ($self) = @_;

   $self->del_fh ($fds_r) if $self->{poll} eq "r";
   $self->del_fh ($fds_w) if $self->{poll} eq "w";

   %$self = ();
}

1

