package de.renew.ptchannel.single;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import de.renew.formalism.java.ParsedDeclarationNode;
import de.renew.simulatorontology.shadow.ShadowDeclarationNode;
import de.renew.simulatorontology.shadow.ShadowNet;
import de.renew.simulatorontology.shadow.SyntaxException;

import static org.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

/**
 * @author 1lopes
 */
public class SinglePTNetWithChannelCompilerTest {

    static class TestableSinglePTNetCompiler extends SinglePTNetWithChannelCompiler {

        public TestableSinglePTNetCompiler() {
            super();
        }

        @Override
        public ShadowDeclarationNode findDeclarationNode(ShadowNet shadowNet)
            throws SyntaxException
        {
            return super.findDeclarationNode(shadowNet);
        }

        @Override
        public ParsedDeclarationNode makeEmptyDeclarationNode() {
            return super.makeEmptyDeclarationNode();
        }

        @Override
        public ParsedDeclarationNode compile(ShadowDeclarationNode declarationNode)
            throws SyntaxException
        {
            return super.compile(declarationNode);
        }
    }

    private ShadowNet _mockShadowNet;
    private TestableSinglePTNetCompiler _spyCompiler;

    @BeforeEach
    void setUp() {
        _mockShadowNet = mock(ShadowNet.class);
        _spyCompiler = spy(new TestableSinglePTNetCompiler());
    }

    @Test
    void testParseDeclarationsDelegatesToMakeDeclarationNode() throws SyntaxException {
        //given
        doNothing().when(_spyCompiler).makeDeclarationNode(_mockShadowNet);

        //when
        _spyCompiler.parseDeclarations(_mockShadowNet);

        //then
        verify(_spyCompiler, times(1)).makeDeclarationNode(_mockShadowNet);
    }

    @Test
    void testMakeDeclarationNodeCreatesEmptyNodeWhenNoneFound() throws SyntaxException {
        //given
        ParsedDeclarationNode emptyNode = new ParsedDeclarationNode();

        doReturn(null).when(_spyCompiler).findDeclarationNode(_mockShadowNet);
        doReturn(emptyNode).when(_spyCompiler).makeEmptyDeclarationNode();

        //when
        _spyCompiler.makeDeclarationNode(_mockShadowNet);

        //then
        verify(_spyCompiler, times(1)).makeEmptyDeclarationNode();
        verify(_spyCompiler, never()).compile(any(ShadowDeclarationNode.class));
        assertSame(emptyNode, _spyCompiler.getDeclaration());
    }

    @Test
    void testMakeDeclarationNodeCompilesWhenNodeFound() throws SyntaxException {
        //given
        ShadowDeclarationNode mockNode = mock(ShadowDeclarationNode.class);
        ParsedDeclarationNode compiledNode = new ParsedDeclarationNode();

        doReturn(mockNode).when(_spyCompiler).findDeclarationNode(_mockShadowNet);
        doReturn(compiledNode).when(_spyCompiler).compile(mockNode);

        //when
        _spyCompiler.makeDeclarationNode(_mockShadowNet);

        //then
        verify(_spyCompiler, times(1)).compile(mockNode);
        verify(_spyCompiler, never()).makeEmptyDeclarationNode();
        assertSame(compiledNode, _spyCompiler.getDeclaration());
    }
}