using CodecBzip2
using Test
using Aqua: Aqua
import TranscodingStreams
using TestsForCodecPackages:
    test_roundtrip_read,
    test_roundtrip_write,
    test_roundtrip_transcode,
    test_roundtrip_lines,
    test_roundtrip_seekstart,
    test_reuse_encoder

Aqua.test_all(CodecBzip2)

@testset "Bzip2 Codec" begin
    codec = Bzip2Compressor()
    @test codec isa Bzip2Compressor
    @test occursin(r"^(CodecBzip2\.)?Bzip2Compressor\(blocksize100k=\d+, workfactor=\d+, verbosity=\d+\)$", sprint(show, codec))
    @test CodecBzip2.initialize(codec) === nothing
    @test CodecBzip2.finalize(codec) === nothing

    codec = Bzip2Decompressor()
    @test codec isa Bzip2Decompressor
    @test occursin(r"^(CodecBzip2\.)?Bzip2Decompressor\(small=(true|false), verbosity=\d+\)$", sprint(show, codec))
    @test CodecBzip2.initialize(codec) === nothing
    @test CodecBzip2.finalize(codec) === nothing

    # Generated by `bz2.compress(b"foo")` on CPython 3.5.2.
    data = b"BZh91AY&SYI\xfe\xc4\xa5\x00\x00\x00\x01\x00\x01\x00\xa0\x00!\x00\x82,]\xc9\x14\xe1BA'\xfb\x12\x94"
    @test read(Bzip2DecompressorStream(IOBuffer(data))) == b"foo"
    @test read(Bzip2DecompressorStream(IOBuffer(vcat(data, data)))) == b"foofoo"

    # concatenate two bzip2 blocks
    buf = IOBuffer()
    stream = Bzip2CompressorStream(buf)
    write(stream, b"foo", TranscodingStreams.TOKEN_END)
    write(stream, b"bar", TranscodingStreams.TOKEN_END)
    @test read(Bzip2DecompressorStream(IOBuffer(take!(buf)))) == b"foobar"

    @test Bzip2CompressorStream <: TranscodingStreams.TranscodingStream
    @test Bzip2DecompressorStream <: TranscodingStreams.TranscodingStream

    test_roundtrip_read(Bzip2CompressorStream, Bzip2DecompressorStream)
    test_roundtrip_write(Bzip2CompressorStream, Bzip2DecompressorStream)
    test_roundtrip_lines(Bzip2CompressorStream, Bzip2DecompressorStream)
    test_roundtrip_seekstart(Bzip2CompressorStream, Bzip2DecompressorStream)
    test_roundtrip_transcode(Bzip2Compressor, Bzip2Decompressor)
    test_reuse_encoder(Bzip2Compressor, Bzip2Decompressor)

    @test_throws ArgumentError Bzip2Compressor(blocksize100k=10)
    @test_throws ArgumentError Bzip2Compressor(workfactor=251)
    @test_throws ArgumentError Bzip2Compressor(verbosity=5)
    @test_throws ArgumentError Bzip2Decompressor(verbosity=5)

    @testset "unexpected end of stream errors" begin
        # issue #32
        local uncompressed = rand(UInt8, 1000)
        local compressed = transcode(Bzip2Compressor, uncompressed)
        for i in 0:length(compressed)-1
            @test_throws CodecBzip2.BZ2Error(CodecBzip2.BZ_UNEXPECTED_EOF) transcode(Bzip2Decompressor, compressed[1:i])
        end
        @test transcode(Bzip2Decompressor, compressed) == uncompressed
        # compressing empty vector should still work
        @test transcode(Bzip2Decompressor, transcode(Bzip2Compressor, UInt8[])) == UInt8[]
    end
    @testset "data errors" begin
        @test_throws CodecBzip2.BZ2Error(CodecBzip2.BZ_DATA_ERROR_MAGIC) transcode(Bzip2Decompressor, zeros(UInt8, 10))
        local uncompressed = rand(UInt8, 1000)
        local compressed = transcode(Bzip2Compressor, uncompressed)
        compressed[70] ⊻= 0x01
        @test_throws CodecBzip2.BZ2Error(CodecBzip2.BZ_DATA_ERROR) transcode(Bzip2Decompressor, compressed)
    end
    @testset "error printing" begin
        @test sprint(Base.showerror, CodecBzip2.BZ2Error(CodecBzip2.BZ_CONFIG_ERROR)) ==
            "BZ2Error: BZ_CONFIG_ERROR: the library has been improperly compiled on your platform"
        @test sprint(Base.showerror, CodecBzip2.BZ2Error(CodecBzip2.BZ_SEQUENCE_ERROR)) ==
            "BZ2Error: BZ_SEQUENCE_ERROR: invalid function sequence, there is a bug in CodecBzip2"
        @test sprint(Base.showerror, CodecBzip2.BZ2Error(CodecBzip2.BZ_PARAM_ERROR)) ==
            "BZ2Error: BZ_PARAM_ERROR: function parameter is out of range, there is a bug in CodecBzip2"
        @test sprint(Base.showerror, CodecBzip2.BZ2Error(CodecBzip2.BZ_UNEXPECTED_EOF)) ==
            "BZ2Error: BZ_UNEXPECTED_EOF: the compressed stream may be truncated"
        @test sprint(Base.showerror, CodecBzip2.BZ2Error(CodecBzip2.BZ_DATA_ERROR)) ==
            "BZ2Error: BZ_DATA_ERROR: a data integrity error is detected in the compressed stream"
        @test sprint(Base.showerror, CodecBzip2.BZ2Error(CodecBzip2.BZ_DATA_ERROR_MAGIC)) ==
            "BZ2Error: BZ_DATA_ERROR_MAGIC: the compressed stream doesn't begin with the right magic bytes"
        @test sprint(Base.showerror, CodecBzip2.BZ2Error(-100)) ==
            "BZ2Error: unknown bzip2 error code: -100"
    end
    @testset "memory leaks" begin
        # issue #27
        for i in 1:200000
            c = transcode(Bzip2Compressor(), zeros(UInt8,16))
            u = transcode(Bzip2Decompressor(), c)
        end
    end
end
