#!perl
use warnings;
use strict;
# TIMEOUT_MULT allows scaling all timing values for slow machines (default: 1)
use constant TIMEOUT_MULT => $ENV{PERL_TEST_TIME_OUT_FACTOR} || ($ENV{AUTOMATED_TESTING} ? 2 : 1);
use constant HARDER => $ENV{RELEASE_TESTING} ? 10 : 1;
use constant CLIENTS_11 => HARDER * 2;
use constant CLIENTS_10 => HARDER * 2;
use constant CLIENTS => CLIENTS_11 * 2 + CLIENTS_10;
use Test::More tests => 7 + 44 * CLIENTS_11 + 22 * CLIENTS_10;
use Test::Fatal;
use lib 't'; use Utils;

BEGIN { use_ok('Feersum') };

my ($socket,$port) = get_listen_socket();
ok $socket, "made listen socket";
ok $socket->fileno, "has a fileno";

my $evh = Feersum->new();

{
    no warnings 'redefine';
    *Feersum::DIED = sub {
        my $err = shift;
        fail "Died during request handler: $err";
    };
}

my $cv = AE::cv;
my $started = 0;
my $finished = 0;
$evh->request_handler(sub {
    my $r = shift;
    isa_ok $r, 'Feersum::Connection', 'got an object!';
    my $env = $r->env();
    ok $env && ref($env) eq 'HASH';

    ok $env->{'psgi.streaming'}, 'got psgi.streaming';
    my $cnum = $env->{HTTP_X_CLIENT};
    ok $cnum, "got client number";

    ok !$r->can('write'), "write method removed from connection object";

    $cv->begin;
    my $w = $r->start_streaming("200 OK", ['Content-Type' => 'text/plain', 'X-Client' => $cnum, 'X-Fileno' => $r->fileno ]);
    $started++;
    isa_ok($w, 'Feersum::Connection::Writer', "got a writer $cnum");
    isa_ok($w, 'Feersum::Connection::Handle', "... it's a handle $cnum");
    my $n = 0;
    my $wrote_third = 0;
    my $t; $t = AE::timer rand()/5,rand()/5, sub {
        $n++;
        eval {
            if ($n == 1) {
                ok blessed($w), "still blessed? $cnum";
                # cover PADTMP case
                $w->write("$cnum Hello streaming world! chunk ".
                          ($n==1?"one":"WTF")."\n");
                pass "wrote chunk $n $cnum";
            }
            elsif ($n == 2) {
                ok blessed($w), "still blessed? $cnum";
                # cover PADMY case
                my $d = "$cnum Hello streaming world! chunk ".
                        ($n==1?"WTF":"'two'")."\n";
                $w->write($d);
                pass "wrote chunk $n $cnum";
            }
            elsif ($n == 3) {
                ok blessed($w), "still blessed? $cnum";
                my $buf = "$cnum Hello streaming world! chunk three\n";
                $w->poll_cb(sub {
                    my $w2 = shift;
                    isa_ok($w2, 'Feersum::Connection::Writer',
                        "got another writer $cnum");
                    $w2->write($buf);
                    $w2->poll_cb(undef); # unset
                    $wrote_third = 1;
                });
            }
            elsif ($wrote_third) {
                ok blessed($w), "still blessed? $cnum";
                $w->close();
                pass "async writer finished $cnum";
                like exception {
                    $w->write("after completion");
                }, qr/closed/i, "can't write after completion $cnum";
                $finished++;
                $cv->end;
                undef $t; # important ref
            }
        }; if ($@) {
            warn "oshit $cnum $@";
        }
    };
});

is exception {
    $evh->use_socket($socket);
}, undef, 'assigned socket';

sub client {
    my $cnum = sprintf("%04d",shift);
    my $is_chunked = shift || 0;
    my $is_keepalive = shift || 0;
    $cv->begin;
    my $h; $h = simple_client GET => '/foo',
        name => $cnum,
        timeout => 3 * TIMEOUT_MULT,
        proto => $is_chunked ? '1.1' : '1.0',
        headers => {
            "Accept" => "*/*",
            'X-Client' => $cnum,
        },
        $is_chunked && $is_keepalive ? (keepalive => 1) : (),
    sub {
        my ($body, $headers) = @_;
        is $headers->{Status}, 200, "$cnum got 200"
            or diag $headers->{Reason};
        if ($is_chunked) {
            is $headers->{HTTPVersion}, '1.1';
            is $headers->{'transfer-encoding'}, "chunked", "$cnum got chunked!";
            if (!$is_keepalive) {
                is $headers->{'connection'}, 'close', "$cnum conn close";
            } else {
                ok !exists($headers->{'connection'}), 'conn keep';
            }
        }
        else {
            is $headers->{HTTPVersion}, '1.0';
            ok !exists $headers->{'transfer-encoding'}, "$cnum not chunked!";
            if ($is_keepalive) {
                is $headers->{'connection'}, 'keep-alive', "$cnum conn keep";
            } else {
                ok !exists($headers->{'connection'}), 'conn close';
            }
        }
        is_deeply [split /\n/,$body], [
            "$cnum Hello streaming world! chunk one",
            "$cnum Hello streaming world! chunk 'two'",
            "$cnum Hello streaming world! chunk three",
        ], "$cnum got all three lines"
            or do {
                warn "descriptor ".$headers->{'x-fileno'}." failed!";
                exit 2;
            };
        $cv->end;
        undef $h;
    };
}


client(1000+$_,1) for (1..CLIENTS_11);
client(2000+$_,0) for (1..CLIENTS_10); # HTTP/1.0 style

$evh->set_keepalive(1);
client(1000+$_,1,1) for (1..CLIENTS_11);

$cv->recv;
is $started, CLIENTS, 'handlers started';
is $finished, CLIENTS, 'handlers finished';

pass "all done";
