package org.requirementsascode;

import static org.requirementsascode.UseCaseStepCondition.afterStep;
import static org.requirementsascode.UseCaseStepCondition.atFirstStep;
import static org.requirementsascode.UseCaseStepCondition.isSystemInDifferentFlowThan;

import java.util.Objects;
import java.util.function.Predicate;

public class UseCaseFlow extends UseCaseModelElement {
	private UseCase useCase;
	private Predicate<UseCaseRunner> stepPredicate;
	private Predicate<UseCaseRunner> completePredicate;

	public UseCaseFlow(String name, UseCase useCase) {
		super(name, useCase.getModel());

		Objects.requireNonNull(useCase);
		this.useCase = useCase;
	}

	public UseCase getUseCase() {
		return useCase;
	}
	
	public UseCase continueAfter(String stepName) {
		Objects.requireNonNull(stepName);

		continueAfter(stepName, null, completePredicate);
		return getUseCase();
	}

	private void continueAfter(String continueAfterStepName, UseCaseStep stepBeforeJumpHappens,
			Predicate<UseCaseRunner> predicate) {
		UseCaseStep continueAfterStep = getUseCase().getStep(continueAfterStepName);
		String stepWhereJumpHappensName = uniqueStepWhereJumpHappensName(continueAfterStepName);

		newStep(stepWhereJumpHappensName, stepBeforeJumpHappens, predicate).system(jumpTo(continueAfterStep));
	}
	
	void continueAfter(String continueAfterStepName, UseCaseStep stepBeforeJumpHappens) {
		Objects.requireNonNull(continueAfterStepName);
		Objects.requireNonNull(stepBeforeJumpHappens);

		continueAfter(continueAfterStepName, stepBeforeJumpHappens, null);
	}
	
	public UseCaseStep newStep(String stepName) {
		Objects.requireNonNull(stepName);

		UseCaseStep newStep = newStep(stepName, null, completePredicate);

		return newStep;
	}

	private UseCaseStep newStep(String stepName, UseCaseStep previousStep, Predicate<UseCaseRunner> predicate) {
		UseCaseStep stepToLeave = getUseCase().newStep(stepName, this, previousStep, predicate);
		return stepToLeave;
	}

	Runnable jumpTo(UseCaseStep stepToContinueAfter) {
		return () -> getModel().getUseCaseRunner().setLatestStep(stepToContinueAfter);
	}

	public UseCaseFlow atFirst() {
		setCompleteStepPredicate(isRunInDifferentFlow().and(atFirstStep()));
		return this;
	}
	private void setCompleteStepPredicate(Predicate<UseCaseRunner> stepPredicate){
		this.stepPredicate = stepPredicate;
		this.completePredicate = stepPredicate;
	}

	public UseCaseFlow after(String stepName) {
		Objects.requireNonNull(stepName);

		UseCaseStep useCaseStep = useCase.getStep(stepName);
		setCompleteStepPredicate(isRunInDifferentFlow().and(afterStep(useCaseStep)));

		return this;
	}

	public UseCaseFlow when(Predicate<UseCaseRunner> whenPredicate) {
		Objects.requireNonNull(whenPredicate);

		if(stepPredicate == null){
			this.stepPredicate = isRunInDifferentFlow();
		}
		completePredicate = stepPredicate.and(whenPredicate);
		return this;
	}
	
	private Predicate<UseCaseRunner> isRunInDifferentFlow() {
		Predicate<UseCaseRunner> inDifferentFlowPredicate = isSystemInDifferentFlowThan(UseCaseFlow.this);
		return inDifferentFlowPredicate;
	}
	
	protected String uniqueStepWhereJumpHappensName(String stepName) {
		return uniqueStepName("Continue after " + stepName);
	}
}

@@ -280,16 +280,43 @@
 bjects;%0A
+import java.util.Optional;%0A
 import j
@@ -344,16 +344,16 @@
 dicate;%0A
-
 %0Apublic 
@@ -427,32 +427,41 @@
 eCase;%0A%09private 
+Optional%3C
 Predicate%3CUseCas
@@ -460,34 +460,43 @@
 e%3CUseCaseRunner%3E
- s
+%3E optionalS
 tepPredicate;%0A%09p
@@ -670,16 +670,16 @@
 eCase);%0A
-
 %09%09this.u
@@ -696,16 +696,65 @@
 seCase;%0A
+%09%09this.optionalStepPredicate = Optional.empty();%0A
 %09%7D%0A%0A%09pub
@@ -2386,25 +2386,33 @@
 te)%7B%0A%09%09this.
-s
+optionalS
 tepPredicate
@@ -2406,32 +2406,44 @@
 StepPredicate = 
+Optional.of(
 stepPredicate;%0A%09
@@ -2439,16 +2439,17 @@
 redicate
+)
 ;%0A%09%09this
@@ -2840,15 +2840,16 @@
 %0A%0A%09%09
-if(s
+comple
 te
-p
 Pred
@@ -2859,26 +2859,18 @@
 te =
-= null)%7B%0A%09%09%09this.s
+ optionalS
 tepP
@@ -2877,19 +2877,24 @@
 redicate
- = 
+.orElse(
 isRunInD
@@ -2911,49 +2911,9 @@
 ow()
-;%0A%09%09%7D%0A%09%09completePredicate = stepPredicate
+)
 .and

Refactor UseCaseFlow