package de.renew.application;

import java.io.PrintStream;
import java.util.Objects;

import de.renew.engine.thread.SimulationThreadPool;
import de.renew.plugin.command.CLCommand;
import de.renew.simulator.api.ISimulationManager;
import de.renew.simulatorontology.simulation.Simulator;


/**
 * This command line command provides several subcommand to control the
 * simulation engine. This includes single-step mode and consecutive runs as
 * well as simulation termination.
 * <p>
 * SimulationControlCommand.java Created: Wed Jul 16 2003
 *
 * @author Michael Duvigneau
 * @since Renew 2.0
 */
public class SimulationControlCommand implements CLCommand {

    private static final String HELP_COMMAND = "help";
    private static final String RUN_COMMAND = "run";
    private static final String STEP_COMMAND = "step";
    private static final String STOP_COMMAND = "stop";
    private static final String HALT_COMMAND = "halt";
    private static final String TERMINATE_COMMAND = "term";

    private final ISimulationManager _simulationManager;

    /**
     * Creates a new <code>SimulationControlCommand</code> instance.
     *
     * @param simulationManager necessary reference to the {@code ISimulationManager} instance
     *               where this command belongs to.
     * @throws NullPointerException if <code>plugin</code> is <code>null</code>.
     */
    public SimulationControlCommand(ISimulationManager simulationManager) {
        _simulationManager =
            Objects.requireNonNull(simulationManager, "Need ISimulationManager reference.");
    }

    /**
     * Controls a simulation or prints a help text. See this class
     * documentation for further details.
     *
     * @param args     one-element-array containing the simulation control
     *                 subcommand (see output of the
     *                 <code>showSyntax()</code> method).
     * @param response the <code>PrintStream</code> for user feedback.
     */
    @Override
    public void execute(String[] args, PrintStream response) {
        if ((args == null) || (args.length != 1) || (args[0] == null)) {
            response.println(
                "Error: Please give exactly one subcommand. Enter '" + HELP_COMMAND
                    + "' for a command help.");
        } else {
            handleCommand(args[0], response);
        }
    }

    /**
     * Actually interprets the command given to execute.
     *
     * @param command  the subcommand to interpret.
     * @param response the <code>PrintStream</code> for user feedback.
     */
    private void handleCommand(final String command, final PrintStream response) {
        if (command.startsWith(HELP_COMMAND)) {
            showSyntax(response);
            return;
        }
        if (command.startsWith(TERMINATE_COMMAND)) {
            _simulationManager.terminateSimulation();
            return;
        }

        final Simulator simulator = _simulationManager.getCurrentSimulator();
        if (simulator == null) {
            response.println("Error: There is no current simulation environment.");
            return;
        }

        if (command.startsWith(RUN_COMMAND)) {
            SimulationThreadPool.getCurrent().execute(simulator::startRun);
        } else if (command.startsWith(STEP_COMMAND) || command.isEmpty()) {
            SimulationThreadPool.getCurrent().execute(simulator::step);
        } else if (command.startsWith(STOP_COMMAND) || command.startsWith(HALT_COMMAND)) {
            SimulationThreadPool.getCurrent().execute(simulator::stopRun);
        } else {
            response.println(
                "Error: Unknown command. Enter '" + HELP_COMMAND + "' for a command help.");
        }
    }

    /* Non-JavaDoc: Specified by the CLCommand interface. */
    @Override
    public String getDescription() {
        return String.format(
            "control the simulation by subcommands (e.g. %s, %s, %s)", STEP_COMMAND, RUN_COMMAND,
            STOP_COMMAND);
    }

    /**
     * Prints command-line help for this command to <code>response</code>.
     *
     * @param response the <code>PrintStream</code> to write the help to.
     */
    public void showSyntax(PrintStream response) {
        response.println("Simulation control commands:");
        response.println(HELP_COMMAND + "   Displays this help.");
        response.println(RUN_COMMAND + "    Runs the simulation continuously.");
        response.println(STEP_COMMAND + "   Executes only one simulation step.");
        response.println(STOP_COMMAND + "   Stops the simulation, but it remains initialized.");
        response.println(HALT_COMMAND + "   Same as stop.");
        response.println(TERMINATE_COMMAND + "   Terminates the simulation.");
    }

    /**
     * @see de.renew.plugin.command.CLCommand#getArguments()
     */
    @Override
    public String getArguments() {
        return String.format(
            "(%s|%s|%s|%s|%s)", RUN_COMMAND, STEP_COMMAND, HALT_COMMAND, TERMINATE_COMMAND,
            HELP_COMMAND);
    }
}