package de.renew.engine.simulator;

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

import de.renew.engine.thread.SimulationThreadPool;

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


/**
 * Tests for deadlock bug #3616.
 *
 * @author Michael Simon
 */
public class ConcurrentSimulatorTest {
    private boolean _finished = false;
    private ConcurrentSimulator _simulator;
    private final Object _lock = new Object();
    private volatile boolean _threadSuspended = false;

    /**
     * Setup method to initialize this test environment.
     */
    @BeforeEach
    public void setUp() {
        _simulator = new ConcurrentSimulator(true) {
            @Override
            protected void checkSuspended() {
                // This code runs inside the simulator thread
                synchronized (_lock) {
                    while (_threadSuspended) {
                        try {
                            _lock.wait(); // Safely pauses the thread
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                }
            }
        };

        _simulator.step();
        System.out.println("Set up");
    }

    /**
     * Tests whether race conditions can occur when using the methods
     * {@link ConcurrentSimulator#startRun} and {@link ConcurrentSimulator#terminateRun}.
     * <p>
     * Deprecated for now because creating a {@link ConcurrentSimulator} throws
     * an exception since this thread is not in a simulation thread.
     *
     * @throws InterruptedException if race conditions occur and any thread
     *                              interrupts the currently active thread
     *                              while it is sleeping
     */
    @SuppressWarnings("deprecation")
    @Ignore
    public void testRunTerminateRaceCondition() throws InterruptedException {
        //TODO Check this test
        // Find the simulator thread.

        // Suspend the simulator thread.
        // Instead of Thread.suspend(), we set the flag so the simulator waits
        // inside the checkSuspended() method within the run loop.
        synchronized (_lock) {
            _threadSuspended = true;
        }

        // Issue run request.
        _simulator.startRun();
        // Wait for the run request to be processed.
        Thread.sleep(10);

        // Issue termination request in a concurrent thread.
        SimulationThreadPool.getCurrent().execute(() -> {
            _simulator.terminateRun();
            _finished = true;
        });
        // Wait the termination request to reach the point where it blocks.
        Thread.sleep(10);

        // Resume the simulator thread.
        // We clear the flag and notify the lock to release the waiting simulator.
        synchronized (_lock) {
            _threadSuspended = false;
            _lock.notifyAll(); // Releases the simulator thread
        }
        // Wait for the simulator thread to exit the AbstractConcurrentSimulator.run() method.
        Thread.sleep(10);

        // Now both the simulator thread and the termination request should be finished.
        assertTrue(_finished);
    }
}