#!/usr/bin/perl -w

use strict;
use warnings;

use Test2::Bundle::Extended;
use Test2::Tools::Explain;
use Test2::Plugin::NoWarnings;

use File::Temp qw/tempfile tempdir/;
use File::Slurper ();

use Fcntl;

#use Errno qw/ENOENT EBADF/;

use Test::MockFile;    # Everything below this can have its open overridden.
my ( undef, $filename ) = tempfile();
unlink $filename;

{
    note "-------------- REAL MODE --------------";
    is( sysopen( my $fh, $filename, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC ), 1, "Sysopen for write" );
    my $str = join( "", "a" .. "z" );
    is( syswrite( $fh, $str ), 26, "2 arg syswrite" );
    my $str_cap = join( "", "A" .. "Y" );
    is( syswrite( $fh, $str_cap, 13 ), 13, "3 arg syswrite" );
    is( syswrite( $fh, $str_cap, 12, 13 ), 12, "4 arg syswrite" );
    is( close $fh, 1, "sysclose \$fh" );
    is( File::Slurper::read_binary($filename), $str . $str_cap, "file contents match what was written" );
    unlink $filename;
}

{
    my $str     = join( "", "a" .. "z" );
    my $str_cap = join( "", "A" .. "Y" );

    note "-------------- MOCK MODE --------------";
    my $bar = Test::MockFile->file($filename);
    is( sysopen( my $fh, $filename, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC ), 1, "Sysopen for write" );

    is( syswrite( $fh, $str ), 26, "2 arg syswrite" );
    is( syswrite( $fh, $str_cap, 13 ), 13, "3 arg syswrite" );
    is( syswrite( $fh, $str_cap, 12, 13 ), 12, "4 arg syswrite" );
    is( close $fh, 1, "sysclose \$fh" );
    is( $bar->contents, $str . $str_cap, "Fake file contents match what was written" );
    undef $bar;
    ok( !-e $filename, "mocked $filename is not present after mock file goes offline" );
}
is( \%Test::MockFile::files_being_mocked, {}, "No mock files are in cache" ) or die;

{
    my $str     = join( "", "a" .. "z" );
    my $str_cap = join( "", "A" .. "Y" );

    note "-------------- REAL MODE --------------";
    File::Slurper::write_binary( $filename, $str_cap . $str );
    is( sysopen( my $fh, $filename, O_RDONLY | O_NOFOLLOW ), 1, "Sysopen for read" );
    my $buf = "blah";
    is( sysread( $fh, $buf, 2, 4 ), 2, "Read 2 into buf at EOL" );
    is( $buf, "blahAB", "Confirm 2 line read" );

    is( sysread( $fh, $buf, 2, 0 ), 2, "Read  into buf at pos 0 truncates the buffer." );
    is( $buf, "CD", "Confirm 2 line read" );

    $buf = "a" x 10;
    is( sysread( $fh, $buf, 0, 0 ), 0, "Read 0 into buf at pos 0 truncates the buffer completely." );
    is( $buf, "", "Buffer is clear" );

    $buf = "b" x 10;
    is( sysread( $fh, $buf, 2, 5 ), 2, "Read 2 into buf at pos 5 truncates after the buffer." );
    is( $buf, "bbbbbEF", "Line is as expected." );

    $buf = "c" x 2;
    is( sysread( $fh, $buf, 3, 6 ), 3, "Read 3 into buf after EOL for the buffer fills in zeroes." );
    is( $buf, "cc\0\0\0\0GHI", "Buffer has null bytes in the middle of it." );

    $buf = "d" x 5;
    is( seek( $fh, 49, 0 ), 1, "Seek to near EOF" );
    is( sysread( $fh, $buf, 4 ), 2, "Read 2 into buf since we're at EOF" );
    is( $buf, "yz", "Buffer is clear" );
}

{
    my $str     = join( "", "a" .. "z" );
    my $str_cap = join( "", "A" .. "Y" );

    note "-------------- MOCK MODE --------------";
    my $bar = Test::MockFile->file( $filename, $str_cap . $str );
    is( sysopen( my $fh, $filename, O_RDONLY | O_NOFOLLOW ), 1, "Sysopen for read" );
    like( "$fh", qr/^IO::File=GLOB\(0x[0-9a-f]+\)$/, '$fh stringifies to a IO::File GLOB' );

    my $buf = "blah";
    is( sysread( $fh, $buf, 2, 4 ), 2, "Read 2 into buf at EOL" );
    is( $buf, "blahAB", "Confirm 2 line read" );

    is( sysread( $fh, $buf, 2, 0 ), 2, "Read  into buf at pos 0 truncates the buffer." );
    is( $buf, "CD", "Confirm 2 line read" );

    $buf = "a" x 10;
    is( sysread( $fh, $buf, 0, 0 ), 0, "Read 0 into buf at pos 0 truncates the buffer completely." );
    is( $buf, "", "Buffer is clear" );

    $buf = "b" x 10;
    is( sysread( $fh, $buf, 2, 5 ), 2, "Read 2 into buf at pos 5 truncates after the buffer." );
    is( $buf, "bbbbbEF", "Line is as expected." );

    $buf = "c" x 2;
    is( sysread( $fh, $buf, 3, 6 ), 3, "Read 3 into buf after EOL for the buffer fills in zeroes." );
    is( $buf, "cc\0\0\0\0GHI", "Buffer has null bytes in the middle of it." );

    $buf = "d" x 5;
    is( seek( $fh, 49, 0 ), 1, "Seek to near EOF" );
    is( sysread( $fh, $buf, 4 ), 2, "Read 2 into buf since we're at EOF" );
    is( $buf, "yz", "Buffer is clear" );
    close $fh;
    undef $bar;
}

{
    my $str     = join( "", "a" .. "z" );
    my $str_cap = join( "", "A" .. "Y" );

    note "-------------- REAL MODE --------------";
    File::Slurper::write_binary( $filename, $str_cap . $str );
    is( sysopen( my $fh, $filename, O_RDONLY | O_NOFOLLOW ), 1, "Sysopen for read" );
    my $buf;
    is( sysread( $fh, $buf, 2 ), 2, "Read 2 into buf when buf is undef." );
    is( $buf, "AB", "Confirm 2 char is read" );
    unlink $filename;
}

{
    my $str     = join( "", "a" .. "z" );
    my $str_cap = join( "", "A" .. "Y" );

    note "-------------- MOCK MODE --------------";
    my $bar = Test::MockFile->file( $filename, $str_cap . $str );
    is( sysopen( my $fh, $filename, O_RDONLY | O_NOFOLLOW ), 1, "Sysopen for read" );
    my $buf;
    is( sysread( $fh, $buf, 2 ), 2, "Read 2 into buf when buf is undef." );
    is( $buf, "AB", "Confirm 2 char is read" );
}

is( \%Test::MockFile::files_being_mocked, {}, "No mock files are in cache" );

done_testing();
exit;
