package de.renew.formalism.function;

import java.lang.reflect.Field;

import org.junit.jupiter.api.Test;

import de.renew.unify.Impossible;
import de.renew.util.Value;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class StaticFieldFunctionTest {

    @Test
    public void testFunction() throws NoSuchFieldException, Impossible {
        //given
        Field testField = TestClass.class.getField("FAST_BOTH");
        StaticFieldFunction testStaticFieldFunction = new StaticFieldFunction(testField);
        int expectedValue = 3;
        //when
        Object actualObject = testStaticFieldFunction.function();
        //then
        assertTrue(actualObject instanceof Value);
        assertEquals(expectedValue, ((Value) actualObject).value);
    }

    @Test
    public void testToString() throws NoSuchFieldException {
        //given
        StaticFieldFunction testStaticFieldFunction =
            new StaticFieldFunction(TestClass.class.getField("FAST_BOTH"));
        String expectedString =
            "StaticFieldFunc(public static final int de.renew.formalism.function.StaticFieldFunctionTest$TestClass.FAST_BOTH)";
        //when
        String actualOutput = testStaticFieldFunction.toString();
        //then
        assertEquals(expectedString, actualOutput);
    }

    @Test
    public void testNullPointerException() throws NoSuchFieldException {
        //given
        Field testField = TestClass.class.getField("_nonStatic");
        StaticFieldFunction testStaticFieldFunction = new StaticFieldFunction(testField);
        String expectedExceptionMessage = "Static field access impossible: non-static field "
            + "(public int de.renew.formalism.function.StaticFieldFunctionTest$TestClass._nonStatic)";
        //when
        Impossible thrown = assertThrows(Impossible.class, () -> {
            testStaticFieldFunction.function();
        });
        //then
        assertTrue(thrown.getCause() instanceof NullPointerException);
        assertEquals(expectedExceptionMessage, thrown.getMessage());
    }

    @Test
    public void testException() throws NoSuchFieldException {
        //given
        Field testField = TestPrivateClass.class.getDeclaredField("FAST_BOTH");
        StaticFieldFunction testStaticFieldFunction = new StaticFieldFunction(testField);


        String expectedExceptionMessage =
            "Exception occured during static field access (private static final int "
                + "de.renew.formalism.function.StaticFieldFunctionTest$TestPrivateClass.FAST_BOTH): "
                + "java.lang.IllegalAccessException: class de.renew.formalism.function.StaticFieldFunction "
                + "cannot access a member of class de.renew.formalism.function.StaticFieldFunctionTest$TestPrivateClass "
                + "with modifiers \"private static final\"";
        //when
        Impossible thrown = assertThrows(Impossible.class, () -> {
            testStaticFieldFunction.function();
        });
        //then
        assertTrue(thrown.getCause() instanceof IllegalAccessException);
        assertEquals(expectedExceptionMessage, thrown.getMessage());
    }

    private static class TestClass {
        public int _nonStatic = 5;
        public static final int FAST_BOTH = 3;

    }

    private static class TestPrivateClass {
        private static final int FAST_BOTH = 3;
    }
}
