Simulation.java

package de.slothsoft.sprintsim.simulation;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;

import de.slothsoft.sprintsim.Member;
import de.slothsoft.sprintsim.config.SprintConfig;
import de.slothsoft.sprintsim.config.TaskConfig;
import de.slothsoft.sprintsim.execution.SprintExecutor;
import de.slothsoft.sprintsim.execution.SprintRetro;
import de.slothsoft.sprintsim.generation.SprintGenerator;
import de.slothsoft.sprintsim.generation.SprintPlanning;

public class Simulation {

	private Member[] members;
	private TaskConfig taskConfig = new TaskConfig();
	private SprintConfig sprintConfig = SprintConfig.createDefault();
	private Long seed;

	private final List<SimulationListener> simulationListeners = new ArrayList<>();
	private TimelineSimulationListener timelineSimulationListener;

	public Simulation(Member... members) {
		this.members = Objects.requireNonNull(members);
	}

	public SimulationResult runSprint() {
		return runMilestone(1);
	}

	public SimulationResult runMilestone(int numberOfSprints) {
		final SimulationInfo info = createInfo(numberOfSprints);
		fireListeners(listener -> listener.simulationStarted(info));

		final SprintGenerator generator = createGenerator();
		final SprintExecutor executor = createExecutor();

		final SprintPlanning[] sprintPlannings = new SprintPlanning[numberOfSprints];
		final SprintRetro[] sprintRetros = new SprintRetro[numberOfSprints];

		for (int sprintNumber = 0; sprintNumber < numberOfSprints; sprintNumber++) {
			final SprintPlanning sprintPlanning = generator.generate();
			fireListeners(listener -> listener.sprintPlanned(sprintPlanning));
			sprintPlannings[sprintNumber] = sprintPlanning;

			final SprintRetro retro = executor.execute(sprintPlanning.getSprint());
			fireListeners(listener -> listener.sprintExecuted(retro));
			sprintRetros[sprintNumber] = retro;
		}

		final SimulationResult result = new SimulationResult(sprintPlannings, sprintRetros);
		fireListeners(listener -> listener.simulationFinished(result));
		return result;
	}

	SimulationInfo createInfo(int numberOfSprints) {
		return new SimulationInfo(this.members, this.taskConfig, this.sprintConfig, numberOfSprints);
	}

	SprintGenerator createGenerator() {
		return new SprintGenerator(this.members).taskConfig(this.taskConfig).sprintConfig(this.sprintConfig)
				.seed(this.seed);
	}

	SprintExecutor createExecutor() {
		return new SprintExecutor(this.members).taskConfig(this.taskConfig);
	}

	protected void fireListeners(Consumer<SimulationListener> listenerAction) {
		for (final SimulationListener simulationListener : this.simulationListeners
				.toArray(new SimulationListener[this.simulationListeners.size()])) {
			listenerAction.accept(simulationListener);
		}
	}

	public void addSimulationListener(SimulationListener simulationListener) {
		this.simulationListeners.add(simulationListener);
	}

	public void removeSimulationListener(SimulationListener simulationListener) {
		this.simulationListeners.remove(simulationListener);
	}

	public void addTimelineListener(TimelineListener simulationListener) {
		getSafeTimelineSimulationListener().addTimelineListener(simulationListener);
	}

	public void removeTimelineListener(TimelineListener simulationListener) {
		getSafeTimelineSimulationListener().removeTimelineListener(simulationListener);
	}

	TimelineSimulationListener getSafeTimelineSimulationListener() {
		if (this.timelineSimulationListener == null) {
			this.timelineSimulationListener = new TimelineSimulationListener();
			this.simulationListeners.add(this.timelineSimulationListener);
		}
		return this.timelineSimulationListener;
	}

	public TaskConfig getTaskConfig() {
		return this.taskConfig;
	}

	public Simulation taskConfig(TaskConfig newTaskConfig) {
		setTaskConfig(newTaskConfig);
		return this;
	}

	public void setTaskConfig(TaskConfig taskConfig) {
		this.taskConfig = Objects.requireNonNull(taskConfig);
	}

	public Member[] getMembers() {
		return this.members;
	}

	public Simulation members(Member... newMembers) {
		setMembers(newMembers);
		return this;
	}

	public void setMembers(Member... members) {
		this.members = Objects.requireNonNull(members);
	}

	public SprintConfig getSprintConfig() {
		return this.sprintConfig;
	}

	public Simulation sprintConfig(SprintConfig newSprintConfig) {
		setSprintConfig(newSprintConfig);
		return this;
	}

	public void setSprintConfig(SprintConfig sprintConfig) {
		this.sprintConfig = Objects.requireNonNull(sprintConfig);
	}

	public Long getSeed() {
		return this.seed;
	}

	public Simulation seed(Long newSeed) {
		setSeed(newSeed);
		return this;
	}

	public void setSeed(Long seed) {
		this.seed = seed;
	}

}