package com.atlassian.bitbucket.mesh.git;

import com.atlassian.bitbucket.mesh.git.builder.catfile.DefaultGitCatFile;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.List;

import static org.junit.Assert.*;

@RunWith(Parameterized.class)
public class GitTreeNodeTest {

    private final boolean useNio;

    public GitTreeNodeTest(boolean useNio) {
        this.useNio = useNio;
    }

    @Parameterized.Parameters(name = "useNio: {0}")
    public static Object[][] modes() {
        return new Object[][]{
                {true},
                {false}
        };
    }

    @Test
    public void testParse() throws Exception {
        ClassPathResource resource = new ClassPathResource("git-cat-file-batch-unicode.txt", DefaultGitCatFile.class);
        try (InputStream inputStream = resource.getInputStream()) {
            List<GitTreeNode> tree = parseTree(inputStream, useNio);
            assertEquals(33, tree.size()); //Should have parsed 33 nodes from the tree

            //Some of the nodes are Unicode, and some are plain ASCII. This spot checks the parsing for
            //some specific nodes to cover those cases

            //Directories
            assertNode(tree.get(0), "1000_files", "b4792a82dd7b0d63d4600e1527d3e388165f0120", true);
            assertNode(tree.get(9),
                    "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vitae erat vitae augue " +
                            "consequat cursus. Nunc a ipsum erat, mattis aliquet ante",
                    "cac7584e571b7f11a5998a0f2fe6fbafb4401fda", true);
            assertNode(tree.get(28), "z国際交流基金海外運営専門員", "1f8f3dc0c09ab2e1e5c303031f4205c64eab8ef4", true);
            assertNode(tree.get(29), "{dir%number#1}    @_)(&^%$#~!", "a22319326dbe7bffeadc8ae96d882cfc2bea7d4c", true);
            assertNode(tree.get(32), "国際交流基金海外運営専門員", "b45f3198bd5b2d44f464051e398dc03745b089a8", true);

            //Files
            assertNode(tree.get(2), "CRCRLF.txt", "d2c380e4127e53a4f03122a76ef8a30a6b37f96e", false);
            assertNode(tree.get(8),
                    "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vitae erat vitae augue " +
                            "consequat cursus. Nunc a ipsum erat, mattis aliquet ante.txt",
                    "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", false);
            assertNode(tree.get(31), "国際交流基金海外運営専門員.txt", "23a6b39161ef14b56fc0ed0c1f2af5c6dacec17a", false);
        }
    }

    private static void assertNode(GitTreeNode node, String path, String contentId, boolean directory) {
        assertEquals(path, node.getPath());
        assertEquals(contentId, node.getContentId());
        if (directory) {
            assertTrue("Expected a directory, but found a file", node.isDirectory());
            assertFalse("GitTreeNode.isDirectory() and isFile() should never both be true", node.isFile());
        } else {
            assertTrue("Expected a file, but found a directory", node.isFile());
            assertFalse("GitTreeNode.isFile() and isDirectory() should never both be true", node.isDirectory());
        }
    }

    private static List<GitTreeNode> parseTree(InputStream inputStream, boolean nio) throws IOException {
        List<GitTreeNode> tree = new ArrayList<>();

        //Before the first node there is cat-file header information that needs to be read and, for the purposes
        //of this test, discarded
        inputStream.skip(51L);

        if (nio) {
            //200 here is large enough to get multiple nodes in a single read(ByteBuffer) result but small enough
            //we end up using multiple reads to stream the entire InputStream, which ensures we test handling for
            //"split buffers" (single nodes whose bytes are spread across multiple buffers)
            ByteBuffer buffer = ByteBuffer.allocate(200);

            ReadableByteChannel channel = Channels.newChannel(inputStream);
            while (channel.read(buffer) > -1) {
                buffer.flip();

                GitTreeNode node;
                while ((node = GitTreeNode.parse(buffer)) != null) {
                    tree.add(node);
                }

                buffer.compact();
            }
        } else {
            GitTreeNode node;
            while ((node = GitTreeNode.parse(inputStream)) != null) {
                tree.add(node);
            }
        }

        return tree;
    }
}