package net.ranides.assira.collection.query.derived;

import lombok.RequiredArgsConstructor;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.test.CQueryAssert;
import org.junit.Assert;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import static org.junit.Assert.*;

public class CQTreeDFSTest {

    private final MyNode tree = new MyNode(
        "1", Arrays.asList(
            new MyNode("1/1", Arrays.asList(
                new MyNode("1/1/1", Arrays.asList()),
                new MyNode("1/1/2", Arrays.asList()),
                new MyNode("1/1/3", Arrays.asList()),
                new MyNode("1/1/4", Arrays.asList())
            )),
            new MyNode("1/2", Arrays.asList(
                new MyNode("1/2/1", Arrays.asList()),
                new MyNode("1/2/2", Arrays.asList(
                    new MyNode("1/2/2/1", Arrays.asList()),
                    new MyNode("1/2/2/2", Arrays.asList()),
                    new MyNode("1/2/2/3", Arrays.asList())
                )),
                new MyNode("1/2/3", Arrays.asList())
            )),
            new MyNode("1/3", Arrays.asList(
                new MyNode("1/3/1", Arrays.asList()),
                new MyNode("1/3/2", Arrays.asList()),
                new MyNode("1/3/3", Arrays.asList()),
                new MyNode("1/3/4", Arrays.asList()),
                new MyNode("1/3/5", Arrays.asList())
            ))
        )
    );

    private final List<String> dfs = Arrays.asList(
        "1",
        "1/1",
        "1/1/1",
        "1/1/2",
        "1/1/3",
        "1/1/4",
        "1/2",
        "1/2/1",
        "1/2/2",
        "1/2/2/1",
        "1/2/2/2",
        "1/2/2/3",
        "1/2/3",
        "1/3",
        "1/3/1",
        "1/3/2",
        "1/3/3",
        "1/3/4",
        "1/3/5"
        );

    @Test
    public void testBasic() {
        CQuery<MyNode> query = CQuery.from().tree().dfs(() -> tree, node -> CQuery.from(node.nodes));
        CQueryAssert.assertEqualsNull(dfs, query.map(v -> v.value));
    }

    @Test
    public void testForEach() {
        CQuery<MyNode> query = CQuery.from().tree().dfs(() -> tree, node -> CQuery.from(node.nodes));

        List<String> out = new ArrayList<>();
        query.forEach(v -> out.add(v.value));

        assertEquals(dfs, out);
    }

    @Test
    public void testWhileEach() {
        CQuery<MyNode> query = CQuery.from().tree().dfs(() -> tree, node -> CQuery.from(node.nodes));

        List<String> out1 = new ArrayList<>();
        assertTrue(query.whileEach(v -> {
            out1.add(v.value);
            return true;
        }));

        List<String> out2 = new ArrayList<>();
        assertFalse(query.whileEach(v -> {
            out2.add(v.value);
            return !"1/3/2".equals(v.value);
        }));

        List<String> out3 = new ArrayList<>();
        assertFalse(query.whileEach(v -> {
            out3.add(v.value);
            return !"1/2/2/3".equals(v.value);
        }));

        assertEquals(dfs, out1);
        assertEquals(dfs.subList(0, 16), out2);
        assertEquals(dfs.subList(0, 12), out3);
    }

    @Test
    public void testIterator() {
        CQuery<MyNode> query = CQuery.from().tree().dfs(() -> tree, node -> CQuery.from(node.nodes));

        List<String> out = new ArrayList<>();
        Iterator<MyNode> itr = query.iterator();
        while (itr.hasNext()) {
            out.add(itr.next().value);
        }
        assertEquals(dfs, out);
    }

    @Test
    public void testList() {
        CQuery<MyNode> query = CQuery.from().tree().dfs(() -> tree, node -> CQuery.from(node.nodes));

        List<String> out = query.map(v -> v.value).list();
        assertEquals(dfs, out);
    }

    @RequiredArgsConstructor
    private static class MyNode {
        final String value;
        final List<MyNode> nodes;
    }

}