package org.jtwig.functions.impl;

import org.jtwig.JtwigModel;
import org.jtwig.JtwigTemplate;
import org.jtwig.environment.EnvironmentConfiguration;
import org.jtwig.functions.impl.structural.exceptions.ParentFunctionOutsideBlockException;
import org.jtwig.functions.impl.structural.exceptions.ParentFunctionWithoutExtending;
import org.jtwig.resource.loader.InMemoryResourceLoader;
import org.jtwig.resource.loader.TypedResourceLoader;
import org.junit.Before;
import org.junit.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.jtwig.environment.EnvironmentConfigurationBuilder.configuration;
import static org.jtwig.resource.reference.ResourceReference.MEMORY;

public class ParentFunctionTest {
    private JtwigModel model;
    private EnvironmentConfiguration configTemplate;

    @Before
    public void setupModel() {
        model = JtwigModel.newModel();
    }

    @Before
    public void setupConfiguration() {
        TypedResourceLoader templateResourceA = new TypedResourceLoader(MEMORY, InMemoryResourceLoader.builder()
                .withResource("a", "x{% block a %}A{% endblock %}x{% block z %}{% endblock %}")
                .build());

        TypedResourceLoader templateResourceB = new TypedResourceLoader(MEMORY, InMemoryResourceLoader.builder()
                .withResource("b", "{% extends 'memory:a' %}" +
                        "{% block a %}B{{ parent() }}B{% endblock %}")
                .build());

        TypedResourceLoader templateResourceC = new TypedResourceLoader(MEMORY, InMemoryResourceLoader.builder()
                .withResource("c", "{% extends 'memory:b' %}" +
                        "{% block a %}C{{ parent() }}C{{ parent() }}C{% endblock %}")
                .build());

        configTemplate = configuration().resources().resourceLoaders()
                .add(templateResourceA)
                .add(templateResourceB)
                .add(templateResourceC)
                .and().and().build();
    }

    private void testWith(String template, String expected) {
        String result = JtwigTemplate.inlineTemplate(template, configTemplate)
                .render(JtwigModel.newModel());

        assertThat(result, is(expected));
    }

    @Test
    public void inheritsParentBlock() {
        testWith(
                "{% extends 'memory:a' %}" +
                        "{% block a %}B{{ parent() }}{% endblock %}",
                "xBAx"
        );
    }

    @Test
    public void inheritsParentBlockWithSuffix() {
        testWith(
                "{% extends 'memory:a' %}" +
                        "{% block a %}B{{ parent() }}B{% endblock %}",
                "xBABx"
        );
    }

    @Test(expected = ParentFunctionWithoutExtending.class)
    public void errorWhenCallingParentWithoutExtending() {
        testWith(
                "{% block a %}B{{ parent() }}{% endblock %}",
                ""
        );
    }

    @Test(expected = ParentFunctionOutsideBlockException.class)
    public void errorWhenCallingParentOutsideOfBlock() {
        testWith(
                "z{{ parent() }}z",
                ""
        );
    }

    @Test
    public void inheritsParentBlockTwiceIfRequested() {
        testWith(
                "{% extends 'memory:a' %}" +
                        "{% block a %}{{ parent() }}B{{ parent() }}{% endblock %}",
                "xABAx"
        );
    }

    @Test
    public void inheritsParentBlockThroughTwoLevels() {
        testWith(
                "{% extends 'memory:b' %}" +
                        "{% block a %}C{{ parent() }}C{% endblock %}",
                "xCBABCx"
        );
    }

    @Test
    public void inheritsParentBlockTwiceThroughTwoLevels() {
        testWith(
                "{% extends 'memory:b' %}" +
                        "{% block a %}C{{ parent() }}C{{ parent() }}C{% endblock %}",
                "xCBABCBABCx"
        );
    }

    @Test
    public void inheritsParentBlockThroughThreeLevels() {
        testWith(
                "{% extends 'memory:c' %}" +
                        "{% block a %}D{{ parent() }}D{% endblock %}",
                "xDCBABCBABCDx"
        );
    }

    @Test
    public void inheritsParentBlockTwiceThroughThreeLevels() {
        testWith(
                "{% extends 'memory:c' %}" +
                        "{% block a %}D{{ parent() }}D{{ parent() }}D{% endblock %}",
                "xDCBABCBABCDCBABCBABCDx"
        );
    }

    @Test
    public void blockFunctionInterpretsInheritedBlocks() {
        testWith(
                "{% extends 'memory:a' %}" +
                        "{% block a %}{{ parent() }}B{{ parent() }}{% endblock %}" +
                        "{% block z %}Z{{ block('a')}}Z{% endblock %}",
                "xABAxZABAZ"
        );
    }

    @Test
    public void blockFunctionInterpretsInheritedBlocksIndependentOfOrder() {
        testWith(
                "{% extends 'memory:a' %}" +
                        "{% block z %}Z{{ block('a')}}Z{% endblock %}" +
                        "{% block a %}{{ parent() }}B{{ parent() }}{% endblock %}",
                "xABAxZABAZ"
        );
    }

    @Test
    public void blockFunctionInterpretsInheritedBlocksThroughMultipleLevels() {
        testWith(
                "{% extends 'memory:b' %}" +
                        "{% block a %}C{{ parent() }}C{% endblock %}" +
                        "{% block z %}Z{{ block('a')}}Z{% endblock %}",
                "xCBABCxZCBABCZ"
        );
    }
}

@@ -924,18 +924,158 @@
 wModel()
+%0A                .with(%22aVar%22, %22A%22)%0A                .with(%22bVar%22, %22B%22)%0A                .with(%22cVar%22, %22C%22)%0A                .with(%22zVar%22, %22Z%22)
 ;%0A
-
     %7D%0A%0A 
@@ -1287,17 +1287,26 @@
 ock a %25%7D
-A
+%7B%7B aVar %7D%7D
 %7B%25 endbl
@@ -1577,33 +1577,42 @@
   %22%7B%25 block a %25%7D
-B
+%7B%7B bVar %7D%7D
 %7B%7B parent() %7D%7DB%7B
@@ -1877,33 +1877,42 @@
 %7DC%7B%7B parent() %7D%7D
-C
+%7B%7B cVar %7D%7D
 %7B%7B parent() %7D%7DC%7B
@@ -2356,37 +2356,21 @@
 .render(
-JtwigModel.newM
+m
 odel
-()
 );%0A%0A    
@@ -2557,33 +2557,42 @@
   %22%7B%25 block a %25%7D
-B
+%7B%7B bVar %7D%7D
 %7B%7B parent() %7D%7D%7B%25
@@ -2800,33 +2800,42 @@
   %22%7B%25 block a %25%7D
-B
+%7B%7B bVar %7D%7D
 %7B%7B parent() %7D%7DB%7B

add some variable-accesses to the test to ensurep proper variable inheritance