package de.renew.logging.gui.test;

import java.util.Arrays;
import java.util.Objects;

import org.junit.jupiter.api.Test;
import org.opentest4j.AssertionFailedError;

import de.renew.engine.events.SimulationEvent;
import de.renew.logging.gui.StepTrace;
import de.renew.logging.gui.test.helper.LoggingMocks;
import de.renew.logging.gui.test.helper.TestStepTraceChangeListener;
import de.renew.simulatorontology.simulation.StepIdentifier;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;

public class StepTraceTest {



    @Test
    public void testToString() {
        final StepIdentifier stepIdentifierMock = LoggingMocks.getStepIdentifier();
        when(stepIdentifierMock.toString()).thenReturn("mockStep");
        final StepTrace stepTrace = new StepTrace(stepIdentifierMock);

        final String toString = stepTrace.toString();

        assertEquals("Simulator Step " + "mockStep", toString);
    }

    @Test
    public void testGetStepIdentifier() {
        final StepIdentifier stepIdentifierMock = LoggingMocks.getStepIdentifier();
        final StepTrace stepTrace = new StepTrace(stepIdentifierMock);

        final StepIdentifier stepIdentifier = stepTrace.getStepIdentifier();

        assertEquals(stepIdentifierMock, stepIdentifier);
    }

    @Test
    public void testGetEvents() {
        // Mocking
        final StepIdentifier stepIdentifierMock = LoggingMocks.getStepIdentifier();
        final SimulationEvent simulationEventMock = LoggingMocks.getSimulationEvent();
        final SimulationEvent simulationEventMock2 = LoggingMocks.getSimulationEvent();

        final StepTrace stepTrace = new StepTrace(stepIdentifierMock);
        assertEquals(0, stepTrace.getEvents().length);

        // first event
        stepTrace.log(simulationEventMock);
        assertEquals(1, stepTrace.getEvents().length);
        assertContainsObject(stepTrace.getEvents(), simulationEventMock);

        // same event twice
        stepTrace.log(simulationEventMock);
        assertEquals(1, stepTrace.getEvents().length);
        assertContainsObject(stepTrace.getEvents(), simulationEventMock);

        // second event
        stepTrace.log(simulationEventMock2);
        assertEquals(2, stepTrace.getEvents().length);
        assertContainsObject(stepTrace.getEvents(), simulationEventMock);
        assertContainsObject(stepTrace.getEvents(), simulationEventMock2);
    }

    @Test
    public void testAddStepTraceChangeListener() {
        final StepIdentifier stepIdentifierMock = LoggingMocks.getStepIdentifier();
        final TestStepTraceChangeListener listener = new TestStepTraceChangeListener();

        final StepTrace stepTrace = new StepTrace(stepIdentifierMock);
        stepTrace.addStepTraceChangeListener(listener);
        stepTrace.fireStepTraceChanged();
        assertEquals(1, listener.getStepTraces().size());
        assertEquals(stepTrace, listener.getStepTraces().get(0));

        final boolean remove = stepTrace.removeStepTraceChangeListener(listener);
        assertTrue(remove);
        final boolean remove2 = stepTrace.removeStepTraceChangeListener(listener);
        assertFalse(remove2);
    }

    private <V> void assertContainsObject(final V[] array, final V obj) {
        for (final V event : array) {
            if (Objects.equals(event, obj)) {
                return;
            }
        }
        throw new AssertionFailedError(
            String.format(
                "Object is not in array.\n expected: %s\n actual: %s\n", obj,
                Arrays.toString(array)));
    }


}
