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.*;
import net.ranides.assira.trace.TraceUtils;

import net.ranides.test.mockup.reflection.ForObjectWalker;
import org.junit.Test;

import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

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

public class PrintRulesTest {

    @Test
    public void format() {
        StringWriter writer = new StringWriter();

        PrintRules rules = WalkerRules.printer()
                .rule().match(WalkerContexts.ObjectContext::isNull).term("{namePath,text} = <null>")
                .rule().match(String.class).term("{namePath,text} = '{value}'")
                .rule().match(Object.class).hint("{namePath,text} = {value}")
                .prepare();

        ObjectWalker.walk(FOO1, "foo1", rules.visitor(writer));
        ObjectWalker.walk(FOO1, "foo2", WalkerRules.PRINTER.visitor(writer));

        NewAssert.assertLineEquals(TestFiles.WALK_PRINT_FORMAT, writer.toString());
    }

    @Test
    public void formatRules() {
        StringWriter writer = new StringWriter();

        ObjectVisitor visitor = WalkerRules.printer()
                .rule()
                .match(WalkerContexts.ObjectContext::isNull)
                .term("{namePath,text} = <null>")
                .rule()
                .match(String.class)
                .term("{namePath,text} = '{value}'")
                .rule()
                .match(Number.class)
                .term("{namePath,text} = #{value}")
                .rule()
                .match(ForObjectWalker.Point.class)
                .term((self, ctx) -> {
                    self.accept(ctx.namePath() + "...");
                    ForObjectWalker.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)
                .hint("{namePath,text} = {value}")
                .prepare()
                .visitor(writer);

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

        NewAssert.assertLineEquals(TestFiles.WALK_PRINT_RULES, writer.toString());
    }

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

        StringWriter writer = new StringWriter();
        ObjectVisitor visitor = WalkerRules.printer()
                .rule()
                .match(List.class)
                .match(Collection.class)
                .match(Map.class)
                .hint("#{kind.shortName,text,16}:{depth} {id,text,4}.{namePath,text,-50} {typeName}")
                .rule()
                .match(WalkerContexts.ObjectContext::isNull)
                .term("#{kind.shortName,text,16}:{depth} {id,text,4}.{namePath,text,-50} <null>")
                .rule()
                .match(String.class)
                .term("#{kind.shortName,text,16}:{depth} {id,text,4}.{namePath,text,-50} String = '{value}'")
                .rule()
                .match(WalkerContexts.ObjectContext::isVisited)
                .term("#{kind.shortName,text,16}:{depth} {id,text,4}.{namePath,text,-50} <ref> = #{ref}")
                .rule()
                .match(Object.class)
                .hint("#{kind.shortName,text,16}:{depth} {id,text,4}.{namePath,text,-50} {actual.shortName} = {value}")
                .prepare()
                .visitor(writer);

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

        NewAssert.assertLineEquals(TestFiles.WALK_PRINT_RECURSIVE, writer.toString());
    }

    @Test
    public void arrays() {
        StringWriter writer = new StringWriter();

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

        ObjectWalker.walk(list, "record", WalkerRules.PRINTER.visitor(writer));

        NewAssert.assertLineEquals(TestFiles.WALK_PRINT_ARRAYS, writer.toString());
    }

    @Test
    public void matchText() {
        StringWriter writer = new StringWriter();

        ObjectVisitor visitor = WalkerRules.printer()
                .rule()
                .match(MyRecord.class)
                .match(Foo.class)
                .match(Bar.class)
                .match(Point.class)
                .text("<native>")
                .rules(WalkerRules.PRINTER)
                .property("TEST", TraceUtils.getTestFrame().orElse(null))
                .property("HOME", "#{TEST.methodName}")
                .property("PATH", "{HOME}: {typeName,text,20} {namePath,text,-50}")
                .prepare()
                .visitor(writer);

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

        NewAssert.assertLineEquals(TestFiles.WALK_PRINT_MATCH_TEXT, writer.toString());
    }

}