#!/usr/bin/perl -w
use strict;
use warnings;
use Data::Dumper;
use Data::ParseBinary;
use Test::More tests => 27;
#use Test::More qw(no_plan);
$| = 1;

my ($data1, $data2);
my $string;
my $inner;
my $s;
my $stream1;
my $stream2;

$s = Struct("foo",
    UBInt8("a"),
    SLInt16("b")
);
$string = "\x07\x00\x01";
$data1 = {a => 7, b => 256};
$stream1 = CreateStreamReader($string);
is_deeply( $s->parse($stream1), $data1, "String Stream: Parse: Simple");
$stream1 = CreateStreamWriter("");
$s->build($data1, $stream1);
ok( $stream1->Flush() eq $string, "String Stream: Build: Simple");
$s->build($data1, $stream1);
ok( $stream1->Flush() eq $string.$string, "String Stream: Build: Twice");
$string = "\x07\x00\x01\x08\x00\x01";
$data1 = {a => 7, b => 256};
$data2 = {a => 8, b => 256};
$stream1 = CreateStreamReader($string);
is_deeply( $s->parse($stream1), $data1, "String Stream: Parse: First of two");
is_deeply( $s->parse($stream1), $data2, "String Stream: Parse: Second of two");

$stream1 = CreateStreamWriter("\x07\x00\x01");
$data1 = {a => 8, b => 256};
$string = "\x07\x00\x01\x08\x00\x01";
$s->build($data1, $stream1);
ok( $stream1->Flush() eq $string, "String Stream: Build: Continues");

$inner = "\x07\x00\x01";
$stream1 = CreateStreamWriter(StringRef=>\$inner);
$data1 = {a => 8, b => 256};
$string = "\x07\x00\x01\x08\x00\x01";
$s->build($data1, $stream1);
ok( $inner eq $string, "StringRef Stream: Build: Continues1");
ok( ${ $stream1->Flush() } eq $string, "StringRef Stream: Build: Continues2");

$inner = "\x07\x00\x01\x08\x00\x01";
$data1 = {a => 7, b => 256};
$data2 = {a => 8, b => 256};
$stream1 = CreateStreamReader(StringRef=>\$inner);
is_deeply( $s->parse($stream1), $data1, "StringRef Stream: Parse: First of two");
is_deeply( $s->parse($stream1), $data2, "StringRef Stream: Parse: Second of two");

$stream2 = CreateStreamReader(StringRef=>\$inner);
$stream1 = CreateStreamReader(StringBuffer => $stream2);
is_deeply( $s->parse($stream1), $data1, "StringBuffer Stream: Parse: Start");
ok( $stream2->tell() == 3, "StringBuffer Stream: Parse: Step1");
is_deeply( $s->parse($stream1), $data2, "StringBuffer Stream: Parse: Step2");
ok( $stream2->tell() == 6, "StringBuffer Stream: Parse: End");
eval { $s->parse($stream1) };
ok( $@, "StringBuffer Stream: Parse: Dies");

$s = BitStruct("foo",
    Padding(1),
    Flag("myflag"),
    Padding(3),
);
$inner = "\x40\0";
$stream1 = CreateStreamReader(StringRef => \$inner);
$data1 = {myflag => 1};
$data2 = {myflag => 0};
is_deeply( $s->parse($stream1), $data1, "BitStruct over StringRef: Parse: First of two");
is_deeply( $s->parse($stream1), $data2, "BitStruct over StringRef: Parse: Second of two");

$inner = "\x42\0";
$stream1 = CreateStreamReader(Bit => StringRef => \$inner);
$data1 = {myflag => 1};
$data2 = {myflag => 0};
is_deeply( $s->parse($stream1), $data1, "Continues BitStream: Parse: First of three");
is_deeply( $s->parse($stream1), $data1, "Continues BitStream: Parse: Second of three");
is_deeply( $s->parse($stream1), $data2, "Continues BitStream: Parse: Third of three");

$inner = "\x40\x40\0";
$stream1 = CreateStreamWriter(Bit => String => undef);
$s->build($data1, $stream1);
$s->build($data1, $stream1);
$s->build($data2, $stream1);
ok( $stream1->Flush() eq $inner, "Continues BitStream: Build: OK");

$inner = "\x42\0";
$stream1 = CreateStreamWriter(Wrap => Bit => String => undef);
$s->build($data1, $stream1);
$s->build($data1, $stream1);
$s->build($data2, $stream1);
ok( $stream1->Flush()->Flush() eq $inner, "Continues BitStream: Build: OK");

$s = Struct("foo",
    Pointer(sub { 4 }, Byte("data1")),   # <-- data1 is at (absolute) position 4
    Pointer(sub { 7 }, Byte("data2")),   # <-- data2 is at (absolute) position 7
);
$data1 = {data1 => 1, data2=> 2};
$inner = "\x00\x00\x00\x00\x01\x00\x00\x02\0x01\0x01";
$stream2 = UnseekableReader->new($inner);
$stream1 = CreateStreamReader(StringBuffer => $stream2);
is_deeply( $s->parse($stream1), $data1, "StringBuffer: Parse: Pointer passed");
ok( $stream2->tell() == 8, "StringBuffer: Parse: Read the right amount");
$stream2 = UnseekableWriter->new();
$stream1 = CreateStreamWriter(StringBuffer => $stream2);
$inner = "\x00\x00\x00\x00\x01\x00\x00\x02";
ok( $s->build($data1, $stream1) eq $inner, "StringBuffer: Build: passed");

open my $fh, ">", "t_file_stream.bin" or die "Can not open temp file to write";
binmode $fh;
$stream1 = CreateStreamWriter(File => $fh);
$s->build($data1, $stream1);
close $fh;
open $fh, "<", "t_file_stream.bin" or die "Can not open temp file to read";
binmode $fh;
{
    local $/ = undef;
    my $content = <$fh>;
    ok($content eq $inner, "File: written OK");
}
seek($fh, 0, 0);
$stream1 = CreateStreamReader(File => $fh);
is_deeply( $s->parse($stream1), $data1, "File: read OK");
close $fh;
unlink "t_file_stream.bin";


#print Dumper($data1);

package UnseekableReader;
our @ISA;
BEGIN { @ISA = qw{Data::ParseBinary::Stream::StringReader} }

sub seek { die "UnseekableReader: seek should not be called" }

package UnseekableWriter;
our @ISA;
BEGIN { @ISA = qw{Data::ParseBinary::Stream::StringWriter} }

sub seek { die "UnseekableWriter: seek should not be called" }
