package LinuxIsdn::Call::Transmitter;
use strict;
use warnings;
use Carp;

use base qw(LinuxIsdn::Call::Audio);
use fields qw(fh reader low_buffer high_buffer line queued return);
use fields::accessor qw(low_buffer  $
                        high_buffer $
                        queued      .
                        );

sub new {
    my ($class, $fh, $low, $high, $debug) = @_;
    $class = ref($class) || $class;
    my LinuxIsdn::Call::Transmitter $transmit = $class->anew();
    $transmit->{low_buffer} = $low;
    $transmit->{high_buffer}= $high;
    $transmit->{debug}      = $debug || 0;
    $transmit->{fh}	    = $fh;
    $transmit->{line}       = "";
    $transmit->{return}	    = "";
    $transmit->{class}	    = "transmitter";
    return bless $transmit, $class;
}

sub listen_on {
    my LinuxIsdn::Call::Transmitter $transmit = shift;
    return if !$transmit->{fh} || $transmit->{aborted};
    if ($transmit->{reader}) {
        $transmit->{reader}->cb([$transmit, 'do_in']);
        $transmit->{reader}->again();
    } else {
        # Explicitely unweakened !
        $transmit->{reader} = Event->io(fd => $transmit->{fh},
                                        poll => "r",
                                        cb => [$transmit, 'do_in']);
    }
}

sub listen_off {
    my LinuxIsdn::Call::Transmitter $transmit = shift;
    return unless $transmit->{fh};
    if ($transmit->{reader}) {
        $transmit->{reader}->stop();
        $transmit->{reader}->cb(sub {});
    }
}

sub in_error {
    my LinuxIsdn::Call::Transmitter $transmit = shift;
    $transmit->{line} = "";
    print STDERR join("", @_);
}

sub do_in {
    my LinuxIsdn::Call::Transmitter $transmit = shift;
    my $offset = length($transmit->{line});
    my $rc = sysread($transmit->{fh}, $transmit->{line}, 16384, $offset);
    unless ($rc) {
        $transmit->{reader}->cancel();
        $transmit->{reader} = undef;
        $transmit->{fh} = undef;
        unless (defined($rc)) {
            $transmit->in_error("Error reading file descriptor: $!");
            return;
        }
        return if $offset == 0;
    }
    if ($transmit->{started}) {
        $transmit->out($transmit->{line});
        $transmit->{line} = "";
    } elsif (length($transmit->{line})+$transmit->{queued} >= $transmit->{high_buffer}) {
        $transmit->listen_off();
    }
}

sub out {
    my LinuxIsdn::Call::Transmitter $transmit = shift;
    my $call = $transmit->{call};
    $call || croak "Transmitted data available but call gone";
    my $isdn = $call->active();
    $isdn || croak "Transmitted data available but connection to device gone";
    my $length = length($_[0]) || return;
    $transmit->{queued} += $length;
    print STDERR "Queueing $length bytes\n" if $transmit->{debug} & 0x04;
    $transmit->listen_off() if $transmit->{queued} >= $transmit->{high_buffer};
    unshift(@_, $isdn);
    splice(@_, 2, @_-2, sub {
        print STDERR "Unqueue $length bytes, ", length($_[1][0]), " were unprocessed\n" if $transmit->{debug} & 0x04;
        # Pick up untransmitted bytes
        $transmit->{return} .= $_[1][0];
        $transmit->{queued} -= $length;
        $transmit->listen_on() if $transmit->{queued} <= $transmit->{low_buffer};
    });
    goto \&LinuxIsdn::transmit_unencoded_audio;
}

sub start {
    my LinuxIsdn::Call::Transmitter $transmit = $_[0];
    $transmit->{started} = 1;
    if ($transmit->{line} ne "" && 
        $transmit->{queued} <= $transmit->{low_buffer}) {
        $transmit->out($transmit->{line});
        $transmit->{line} = "";
    }
    print STDERR "Start audio transmitting\n" if $transmit->{debug};
    goto $transmit->{on_start};
}

sub abort {
    my LinuxIsdn::Call::Transmitter $transmit = shift;
    my LinuxIsdn::Call $call = $transmit->{call};
    return if !$call || !$transmit->{generation} || $transmit->{aborted};
    my $generation = shift;
    return if $generation && $generation != $transmit->{generation};
    $transmit->listen_off();
    $transmit->{aborted} = 1;
    $call->_abort_transmitting(@_);
    return $transmit;
}

sub DESTROY {
    my LinuxIsdn::Call::Transmitter $transmit = shift;
    print STDERR "Cleaning up $transmit\n" if $transmit->{debug} & 0x08;
    if ($transmit->{reader}) {
        $transmit->{reader}->cancel();
        $transmit->{reader} = undef;
    }
}

1;
