package org.requirementsascode.builder;

import java.util.Objects;

import org.requirementsascode.Condition;
import org.requirementsascode.Flow;
import org.requirementsascode.FlowStep;
import org.requirementsascode.Model;
import org.requirementsascode.UseCase;
import org.requirementsascode.exception.ElementAlreadyInModel;
import org.requirementsascode.exception.NoSuchElementInModel;
import org.requirementsascode.flowposition.After;
import org.requirementsascode.flowposition.Anytime;
import org.requirementsascode.flowposition.InsteadOf;

/**
 * Part used by the {@link ModelBuilder} to build a {@link Model}.
 *
 * @see Flow
 * @author b_muth
 */
public class FlowPart {
	private Flow flow;
	private UseCase useCase;
	private UseCasePart useCasePart;
	private FlowPositionPart optionalFlowPositionPart;

	/**
	 * Creates a flow part for the basic flow.
	 * 
	 * @param useCasePart the use case part whose basic flow this is
	 */
	FlowPart(UseCasePart useCasePart) {
		this.useCasePart = useCasePart;
		this.useCase = useCasePart.getUseCase();
		this.flow = useCase.getBasicFlow();
	}
	
	static FlowPart buildBasicFlowPart(UseCasePart useCasePart) {
		return new FlowPart(useCasePart);
	}
	
	/**
	 * Creates a flow part for a flow with the specified name.
	 * 
	 * @param flowName the name of the flow
	 * @param useCasePart the use case part whose flow this is
	 */
	private FlowPart(String flowName, UseCasePart useCasePart) {
		this.useCasePart = useCasePart;
		this.useCase = useCasePart.getUseCase();
		this.flow = useCase.newFlow(flowName);
	}
	
	static FlowPart buildFlowPart(String flowName, UseCasePart useCasePart) {
		return new FlowPart(flowName, useCasePart);
	}

	/**
	 * Starts the flow after the specified step has been run, in this flow's use
	 * case.
	 * 
	 * Note: You should use after to handle exceptions that occurred in the
	 * specified step.
	 *
	 * @param stepName the name of the step to start the flow after
	 * @return the flow position part, to ease creation of the condition and the
	 *         first step of the flow
	 * @throws NoSuchElementInModel if the specified step is not found in a flow of
	 *                              this use case
	 * 
	 */
	public FlowPositionPart after(String stepName) {
		FlowStep flowStep = (FlowStep)useCase.findStep(stepName);
		After after = new After(flowStep);
		optionalFlowPositionPart = new FlowPositionPart(after, this);
		return optionalFlowPositionPart;
	}

	/**
	 * Creates the first step of this flow, without specifying position or
	 * condition. It can be interrupted by any other flow that has an explicit
	 * position and/or condition. It can be run when no other step has been run
	 * before.
	 *
	 * @param stepName the name of the step to be created
	 * @return the newly created step part, to ease creation of further steps
	 * @throws ElementAlreadyInModel if a step with the specified name already
	 *                               exists in the use case
	 */
	public StepPart step(String stepName) {
		StepPart stepPart = new StepPart(stepName, FlowPart.this);
		return stepPart;
	}

	/**
	 * Starts the flow as an alternative to the specified step, in this flow's use
	 * case.
	 *
	 * @param stepName the name of the specified step
	 * @return the flow position part, to ease creation of the condition and the
	 *         first step of the flow
	 * @throws NoSuchElementInModel if the specified step is not found in this
	 *                              flow's use case
	 */
	public FlowPositionPart insteadOf(String stepName) {
		FlowStep flowStep = (FlowStep) useCase.findStep(stepName);
		InsteadOf insteadOf = new InsteadOf(flowStep);
		optionalFlowPositionPart = new FlowPositionPart(insteadOf, this);
		return optionalFlowPositionPart;
	}

	/**
	 * Starts the flow after any step that has been run, or at the beginning.
	 * 
	 * @return the flow position part, to ease creation of the condition and the
	 *         first step of the flow
	 */
	public FlowPositionPart anytime() {
		Anytime anytime = new Anytime();
		optionalFlowPositionPart = new FlowPositionPart(anytime, this);
		return optionalFlowPositionPart;
	}

	/**
	 * Constrains the flow's condition: only if the specified condition is true, the
	 * flow is started.
	 *
	 * @param condition the condition that constrains when the flow is started
	 * @return the condition part, to ease creation of the first step of the flow
	 */
	public FlowConditionPart condition(Condition condition) {
		Objects.requireNonNull(condition);
		optionalFlowPositionPart = new FlowPositionPart(new Anytime(), this);
		FlowConditionPart conditionPart = optionalFlowPositionPart.condition(condition);
		return conditionPart;
	}

	Flow getFlow() {
		return flow;
	}

	UseCasePart getUseCasePart() {
		return useCasePart;
	}

	ModelBuilder getModelBuilder() {
		return useCasePart.getModelBuilder();
	}
}

@@ -805,135 +805,19 @@
 rt;%0A
-%0A%09/**%0A%09 * Creates a flow part for the basic flow.%0A%09 * %0A%09 * @param useCasePart the use case part whose basic flow this is%0A%09 */%0A%09
+%09%0A%09private 
 Flow
@@ -836,32 +836,43 @@
 Part useCasePart
+, Flow flow
 ) %7B%0A%09%09this.useCa
@@ -954,36 +954,142 @@
 w = 
-useCase.getB
+flow;%0A%09%7D%0A%0A%09/**%0A%09 * Creates a flow part for the basic flow.%0A%09 * %0A%09 * @param useCasePart the use case part whose b
 asic
-Flow();%0A%09%7D%0A%09
+ flow this is%0A%09 */
 %0A%09st
@@ -1181,23 +1181,63 @@
 CasePart
+, useCasePart.getUseCase().getBasicFlow()
 );%0A%09%7D%0A
-%09
 %0A%09/**%0A%09 
@@ -1303,49 +1303,8 @@
  * %0A
-%09 * @param flowName the name of the flow%0A
 %09 * 
@@ -1366,194 +1366,53 @@
 %0A%09 *
-/%0A%09private FlowPart(String flowName, UseCasePart useCasePart) %7B%0A%09%09this.useCasePart = useCasePart;%0A%09%09this.useCase = useCasePart.getUseCase();%0A%09%09this.flow = useCase.newFlow(flowName);%0A%09%7D%0A%09
+ @param flowName    the name of the flow%0A%09 */
 %0A%09st
@@ -1505,24 +1505,27 @@
 lowPart(
-flowName
+useCasePart
 , useCas
@@ -1529,16 +1529,47 @@
 CasePart
+.getUseCase().newFlow(flowName)
 );%0A%09%7D%0A%0A%09

Build flows with static methods