package de.renew.formalism.pt;

import org.junit.jupiter.api.Test;

import de.renew.simulatorontology.shadow.ShadowDeclarationNode;
import de.renew.simulatorontology.shadow.ShadowNet;
import de.renew.simulatorontology.shadow.ShadowNetSystem;
import de.renew.simulatorontology.shadow.ShadowPlace;
import de.renew.simulatorontology.shadow.SyntaxException;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;

public class SinglePTNetCompilerTest {
    @Test
    public void testCheckDeclarationNodeThrowsSyntaxException() {
        // given
        ShadowNet shadowNet = mock(ShadowNet.class);
        SinglePTNetCompiler compiler = new SinglePTNetCompiler();

        // when
        SyntaxException exception = assertThrows(SyntaxException.class, () -> {
            compiler.checkDeclarationNode("foo", true, shadowNet);
        });

        // then
        assertEquals("Declaration node is not allowed.", exception.getMessage());
    }

    @Test
    public void testCheckArcInscriptionWithNumber() throws SyntaxException {
        // given
        ShadowNet shadowNet = mock(ShadowNet.class);
        SinglePTNetCompiler compiler = new SinglePTNetCompiler();
        String inscription = "42";
        boolean special = true;

        // when
        String result = compiler.checkArcInscription(inscription, special, shadowNet);

        // then
        assertEquals("inscription", result);
    }

    @Test
    public void testCheckArcInscriptionWithNumberAndSpaces() throws SyntaxException {
        // given
        ShadowNet shadowNet = mock(ShadowNet.class);
        SinglePTNetCompiler compiler = new SinglePTNetCompiler();
        String inscription = "\t  42 \t";
        boolean special = true;

        // when
        String result = compiler.checkArcInscription(inscription, special, shadowNet);

        // then
        assertEquals("inscription", result);
    }

    @Test
    public void testCheckArcInscriptionThrowsIfNoNumber() {
        // given
        ShadowNet shadowNet = mock(ShadowNet.class);
        SinglePTNetCompiler compiler = new SinglePTNetCompiler();
        String inscription = "foo";
        boolean special = true;

        // when
        SyntaxException exception = assertThrows(SyntaxException.class, () -> {
            compiler.checkArcInscription(inscription, special, shadowNet);
        });

        // then
        assertEquals("Number expected", exception.getMessage());
        assertInstanceOf(NumberFormatException.class, exception.getCause());
        assertTrue(exception.getErrorObjects().contains(inscription));
    }

    @Test
    public void testCheckTransitionInscriptionThrowsSyntaxException() {
        // given
        ShadowNet shadowNet = mock(ShadowNet.class);
        SinglePTNetCompiler compiler = new SinglePTNetCompiler();

        // when
        SyntaxException exception = assertThrows(SyntaxException.class, () -> {
            compiler.checkTransitionInscription("foo", true, shadowNet);
        });

        // then
        assertEquals("Transition inscription is not allowed.", exception.getMessage());
    }

    @Test
    public void testCheckPlaceInscriptionWithNumber() throws SyntaxException {
        // given
        ShadowNet shadowNet = mock(ShadowNet.class);
        SinglePTNetCompiler compiler = new SinglePTNetCompiler();
        String inscription = "42";
        boolean special = true;

        // when
        String result = compiler.checkPlaceInscription(inscription, special, shadowNet);

        // then
        assertEquals("initialMarking", result);
    }

    @Test
    public void testCheckPlaceInscriptionWithNumberAndSpaces() throws SyntaxException {
        // given
        ShadowNet shadowNet = mock(ShadowNet.class);
        SinglePTNetCompiler compiler = new SinglePTNetCompiler();
        String inscription = "\t  42 \t";
        boolean special = true;

        // when
        String result = compiler.checkPlaceInscription(inscription, special, shadowNet);

        // then
        assertEquals("initialMarking", result);
    }

    @Test
    public void testCheckPlaceInscriptionThrowsIfNoNumber() {
        // given
        ShadowNet shadowNet = mock(ShadowNet.class);
        SinglePTNetCompiler compiler = new SinglePTNetCompiler();
        String inscription = "foo";
        boolean special = true;

        // when
        SyntaxException exception = assertThrows(SyntaxException.class, () -> {
            compiler.checkPlaceInscription(inscription, special, shadowNet);
        });

        // then
        assertEquals("Number expected", exception.getMessage());
        assertInstanceOf(NumberFormatException.class, exception.getCause());
        assertTrue(exception.getErrorObjects().contains(inscription));
    }

    @Test
    public void testParseDeclarationsDoesNotThrow() {
        // given
        ShadowNetSystem shadowNetSystem = mock(ShadowNetSystem.class);
        ShadowNet shadowNet = new ShadowNet("test_shadow_net", shadowNetSystem);
        ShadowPlace p1 = new ShadowPlace(shadowNet);
        ShadowPlace p2 = new ShadowPlace(shadowNet);
        SinglePTNetCompiler compiler = new SinglePTNetCompiler();

        // then
        assertDoesNotThrow(() -> {
            // when
            compiler.parseDeclarations(shadowNet);
        });
    }

    @Test
    public void testParseDeclarationsThrowsWhenShadowNetContainsDeclaration() {
        // given
        ShadowNetSystem shadowNetSystem = mock(ShadowNetSystem.class);
        ShadowNet shadowNet = new ShadowNet("test_shadow_net", shadowNetSystem);
        ShadowDeclarationNode d1 = new ShadowDeclarationNode(shadowNet, "foo");
        ShadowDeclarationNode d2 = new ShadowDeclarationNode(shadowNet, "bar");
        SinglePTNetCompiler compiler = new SinglePTNetCompiler();

        // when
        SyntaxException e = assertThrows(SyntaxException.class, () -> {
            compiler.parseDeclarations(shadowNet);
        });

        // then
        assertEquals("Declaration node is not allowed.", e.getMessage());
        assertTrue(e.getErrorObjects().contains(d1));
        assertTrue(e.getErrorObjects().contains(d2));
    }
}
