package net.ranides.assira.reflection.walker.rules;

import net.ranides.test.TestFiles;
import net.ranides.assira.junit.NewAssert;
import net.ranides.assira.reflection.IClass;
import net.ranides.assira.reflection.walker.ObjectVisitor;
import net.ranides.assira.reflection.walker.ObjectWalker;
import net.ranides.assira.reflection.walker.WalkerContexts;
import net.ranides.assira.reflection.walker.WalkerRules;
import org.junit.Test;

import java.util.*;
import java.util.regex.Pattern;

import static net.ranides.test.mockup.reflection.ForObjectWalker.*;

public class CollectRulesTest {

    @Test
    public void format() {
        List<String> target = new ArrayList<>();

        CollectRules<String> rules = WalkerRules.collectLines()
            .rule()
                .match(WalkerContexts.ObjectContext::isNull)
                .term(ctx -> ctx.namePath() + " = <null>")
            .rule()
                .match(String.class)
                .term(ctx -> ctx.namePath() + " = '" + ctx.value() + "'")
            .rule()
                .match(Object.class)
                .follow(ctx -> ctx.namePath() + " = " + ctx.value())
            .prepare();

        ObjectWalker.walk(FOO1, "foo1", rules.visitor(target));

        NewAssert.assertLineEquals(TestFiles.WALK_COLLECT_FORMAT, target);
    }

    @Test
    public void flat() {
        List<WalkerRules.WalkerEntry> target = new ArrayList<>();
        ObjectWalker.walk(FOO1, "foo2", WalkerRules.COLLECT.visitor(target));

        NewAssert.assertLineEquals(TestFiles.WALK_COLLECT_FLAT, target);
    }

    @Test
    public void formatRules() {
        List<String> target = new ArrayList<>();

        ObjectVisitor visitor = WalkerRules.collectLines()
            .rule()
                .match(WalkerContexts.ObjectContext::isNull)
                .term(ctx -> ctx.namePath() + " = <null>")
            .rule()
                .match(String.class)
                .term(ctx -> ctx.namePath() + " = '" + ctx.value() + "'")
            .rule()
                .match(Number.class)
                .term(ctx -> ctx.namePath() + " = #" + ctx.value())
            .rule()
                .match(Point.class)
                .term((self, ctx) -> {
                    self.accept(ctx.namePath() + "...");
                    Point value = ctx.value();
                    ctx.context("innerX", IClass.NUMBER, value.getX()).accept(self);
                    ctx.context("innerY", IClass.NUMBER, value.getY()).accept(self);
                    ctx.context("innerZ", IClass.NUMBER, value.getZ()).accept(self);
                    return null;
                })
            .rule()
                .match(Object.class)
                .follow(ctx -> ctx.namePath() + " = " + ctx.value())
                .prepare()
                .visitor(target);

        ObjectWalker.walk(FOO1, "foo1", visitor);

        NewAssert.assertLineEquals(TestFiles.WALK_PRINT_RULES, target);
    }

    @Test
    public void formatRecursive() {
        List<Foo> list = Arrays.asList(FOO2, FOO3, FOO2, FOO4);

        List<WalkerRules.WalkerEntry> target = new ArrayList<>();
        ObjectVisitor visitor = WalkerRules.collectEntries()
            .rule()
                .match(List.class)
                .match(Collection.class)
                .match(Map.class)
                .follow(WalkerRules.WalkerEntry::new)
            .rule()
                .match(WalkerContexts.ObjectContext::isNull)
                .term(WalkerRules.WalkerEntry::new)
            .rule()
                .match(String.class)
                .term(WalkerRules.WalkerEntry::new)
            .rule()
                .match(Pattern.compile(".*/[xyz]"))
                .term(WalkerRules.WalkerEntry::new)
            .rule()
                .match(WalkerContexts.ObjectContext::isVisited)
                .term(WalkerRules.WalkerEntry::new)
            .rule()
                .match(Object.class)
                .follow()
            .prepare()
            .visitor(target);

        ObjectWalker.walk(list, "list", visitor);

        NewAssert.assertLineEquals(TestFiles.WALK_COLLECT_RECURSIVE, target);
    }

    @Test
    public void arrays() {
        List<WalkerRules.WalkerEntry> target = new ArrayList<>();

        List<Object> list = Arrays.asList(MY_RECORD, FOO1, QUAZ);

        ObjectWalker.walk(list, "record", WalkerRules.COLLECT.visitor(target));

        NewAssert.assertLineEquals(TestFiles.WALK_COLLECT_ARRAYS, target);
    }

    @Test
    public void matchText() {
        List<WalkerRules.WalkerEntry> target = new ArrayList<>();

        ObjectVisitor visitor = WalkerRules.collectEntries()
            .rule()
                .match(Bar.class)
                .match(Point.class)
                .term(WalkerRules.WalkerEntry::new)
            .rule()
                .match(MyRecord.class)
                .match(Foo.class)
                .follow()
            .rules(WalkerRules.COLLECT)
            .prepare()
            .visitor(target);

        ObjectWalker.walk(Arrays.asList(MY_RECORD, FOO1), "record", visitor);

        NewAssert.assertLineEquals(TestFiles.WALK_COLLECT_MATCH_TEXT, target);
    }

}