package com.atlassian.bitbucket.scm.git.protocol;

import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;

import javax.annotation.Nonnull;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.zip.GZIPOutputStream;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class GitPacketStreamTest {

    private GitPacketStream stream;
    private List<byte[]> packets;

    @Before
    public void setUp() {
        stream = new GitPacketStream();
        packets = Lists.newArrayList();

        stream.addListener(new GitPacketListener() {
            @Override
            public void onPacket(@Nonnull byte[] packet) {
                packets.add(packet);
            }
        });
    }

    @Test
    public void testAppendFlushPacket() {
        byte[] bytes = "0006aa00000008abcd".getBytes();
        stream.append(bytes);

        assertPackets("0006aa", "0000", "0008abcd");
    }

    @Test(expected = IllegalArgumentException.class)
    public void testAppendNonPacketData() {
        stream.append("not a packet stream".getBytes());
    }

    @Test
    public void testAppendPackData() {
        stream.append(GitTestData.PUSH_INPUT.getBytes(StandardCharsets.UTF_8));

        assertTrue(stream.isInPackSection());
    }

    @Test
    public void testAppendWithOffsetAndLength() {
        byte[] bytes = "ignored0005a0005bignored".getBytes();
        int prefixLength = "ignored".length();

        // read the first packet + 1 byte of the next packet
        stream.append(bytes, prefixLength, 6);
        assertPackets("0005a");

        stream.append(bytes, prefixLength + 6, 4);
        assertPackets("0005a", "0005b");
    }

    @Test
    public void testGzipDetection() throws IOException {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        try (OutputStreamWriter writer = new OutputStreamWriter(new GZIPOutputStream(bytes))) {
            //The packet is valid, but because it's gzip-compressed the packet stream shouldn't
            //try to parse it
            writer.write("0089want 090ac3c4f4afc608fab0b21c3341da96333ec587 multi_ack_detailed " +
                    "no-done side-band-64k thin-pack ofs-delta agent=git/2.3.7.windows.1\n");
        }

        stream.append(bytes.toByteArray());
        assertPackets();
        assertTrue(stream.isInPackSection());
    }

    @Test
    public void testPartialPackets() {
        stream.append("00".getBytes());
        assertPackets();

        stream.append("08a".getBytes());
        assertPackets();

        stream.append("bcd00".getBytes());
        assertPackets("0008abcd");

        stream.append("00".getBytes());
        assertPackets("0008abcd", "0000");

        stream.append("000".getBytes());
        assertPackets("0008abcd", "0000");

        stream.append("5a".getBytes());
        assertPackets("0008abcd", "0000", "0005a");
    }

    private void assertPackets(String... expectedPackets) {
        assertEquals("Mismatch in number of packets", expectedPackets.length, packets.size());
        for (int i = 0; i < expectedPackets.length; ++i) {
            assertEquals("packet " + i, expectedPackets[i], new String(packets.get(i)));
        }
    }

}
