package MConnection::Std;
use strict;
use vars qw(
  @ISA
  $Instance
);

use IO::Handle qw();
use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
use POSIX qw(:errno_h);

use MCoreTools;
use MConnection;
use MIOManager;
@ISA = qw(MConnection MInitializable);

### Instance methods ###########################################################

sub _initialize {
  MConnection->register_listener(__PACKAGE__);
}

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

  if ($Instance) {
    $self->disconnect("can't have multiple MConnection::Stds");
  }
  $Instance = $self;
  MIOManager->add(*STDIN, 'read', MEvent::Message->new(target => $self, method => '_read_input', no_store => 1));

  fcntl(STDIN,  F_SETFL, fcntl(STDIN,  F_GETFL, 0) | O_NONBLOCK) or $self->id_log("ERROR/IO: Error setting nonblocking mode: $!");
  fcntl(STDOUT, F_SETFL, fcntl(STDOUT, F_GETFL, 0) | O_NONBLOCK) or $self->id_log("ERROR/IO: Error setting nonblocking mode: $!");

  $self->{std_ok} = 1;
  $self->setstate('login');
}  

sub disconnect {
  my $self = shift;
  
  if ($self->{std_ok}) {
    MIOManager->remove(*STDIN, 'read');
    MIOManager->remove(*STDOUT, 'write');
  }
  $self->SUPER::disconnect(@_);
  undef $Instance;
}

sub send_echo_off   {IS_MACOS ? `stty raw` : `/bin/stty -echo`}
sub send_echo_on    {IS_MACOS ? `stty sane` : `/bin/stty echo`}

sub send_str_raw {
  my ($self, $t) = @_;

  if ($self->{syswrite_buffer}) {
    $self->{syswrite_buffer} .= $t;
    return 1;
  }
  my $len = syswrite STDOUT, $t, length($t);
  if (!defined $len) {
    if ($! == EWOULDBLOCK) {
      $len = 0;
    } else {
      $self->disconnect("syswrite error: $!", 1);
      return 0;
    }
  }
  if ($len < length($t)) {
    $self->{syswrite_buffer} = substr($t, $len);
    MIOManager->add(*STDOUT, 'write', MEvent::Message->new(target => $self, method => '_flush_syswrite', no_store => 1));
  }
  return 1;
}

sub _flush_syswrite {
  my ($self) = @_;
  my $t = $self->{syswrite_buffer};
  delete $self->{syswrite_buffer};
  MIOManager->remove(*STDOUT, 'write');
  $self->send_str_raw($t);
}

sub _read_input {
  my ($self) = @_;
  
  my $res = sysread STDIN, my($tx), 4096;
  if (!defined $res) {
    $! == EWOULDBLOCK and return;
    $self->id_log("sysread error: ".(0+$!)." $!");
    return;
  }
  $self->{in_buffer} .= $tx if defined $tx;

  $self->handle_input;
}

sub source {"std"}


1;
