package de.renew.formalism.function;

import org.junit.jupiter.api.Test;

import de.renew.net.Net;
import de.renew.net.inscription.arc.Arc;
import de.renew.unify.Copier;
import de.renew.unify.Impossible;
import de.renew.unify.Tuple;
import de.renew.unify.TupleIterator;

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

public class DynamicConstructorFunctionTest {
    @Test
    public void testToString() {
        //given
        DynamicConstructorFunction testeeArc = new DynamicConstructorFunction(Arc.class);
        String expectedStringArc = "DynConstrFunc: " + "de.renew.net.inscription.arc.Arc"
            + ".<init>" + "<unknown parameters>";
        //when
        String actualStringArc = testeeArc.toString();
        //then
        assertEquals(expectedStringArc, actualStringArc);
    }

    @Test
    public void testFunctionWithoutParams() throws Impossible {
        //given
        DynamicConstructorFunction testeeCopier =
            new DynamicConstructorFunction(de.renew.unify.Copier.class);
        Object[] testParams = { };
        Tuple testTuple = new Tuple(testParams, null);
        //when
        Object actualResult = testeeCopier.function(testTuple);
        //then
        assertTrue(actualResult.getClass() == Copier.class);
    }

    @Test
    public void testFunctionWithParams() throws Impossible {
        //given
        DynamicConstructorFunction testeeTupleIterator =
            new DynamicConstructorFunction(de.renew.unify.TupleIterator.class);
        Object[] testParams = { new Tuple(5) };
        Tuple testTuple = new Tuple(testParams, null);
        //when
        Object actualResult = testeeTupleIterator.function(testTuple);
        for (int i = 0; i < 5; i++) {
            ((TupleIterator) actualResult).next();
        }
        //then
        assertTrue(actualResult.getClass() == TupleIterator.class);
        assertFalse(((TupleIterator) actualResult).hasNext());
    }

    @Test
    public void testFunctionNoSuchMethodException() {
        //given
        DynamicConstructorFunction testeeTupleIterator =
            new DynamicConstructorFunction(de.renew.unify.TupleIterator.class);
        Object[] testParams = { };
        Tuple testTuple = new Tuple(testParams, null);
        //when
        Impossible thrown = assertThrows(Impossible.class, () -> {
            testeeTupleIterator.function(testTuple);
        });
        //then
        assertInstanceOf(NoSuchMethodException.class, thrown.getCause());
        assertTrue(thrown.getMessage().contains("Exception occured during constructor call:"));
    }

    @Test
    public void testFunctionNullPointerException() {
        //given
        DynamicConstructorFunction testeeTupleIterator =
            new DynamicConstructorFunction(de.renew.unify.TupleIterator.class);
        Object[] testParams = { null };
        Tuple testTuple = new Tuple(testParams, null);
        //when
        Impossible thrown = assertThrows(Impossible.class, () -> {
            testeeTupleIterator.function(testTuple);
        });
        //then
        assertInstanceOf(NullPointerException.class, thrown.getCause());
        assertTrue(thrown.getMessage().contains("Exception occured during constructor call:"));
    }

    @Test
    public void testFunctionConstructorCallException() {
        //given
        DynamicConstructorFunction testeeNet = new DynamicConstructorFunction(Net.class);
        Object[] testParams = { };
        Tuple testTuple = new Tuple(testParams, null);
        //when
        Impossible thrown = assertThrows(Impossible.class, () -> {
            testeeNet.function(testTuple);
        });
        //then
        assertInstanceOf(AssertionError.class, thrown.getCause());
        assertTrue(thrown.getMessage().contains("Constructor call resulted in an exception:"));
    }
}
