/**
 * Copyright (c) 2015 itemis AG.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 * 	itemis AG - initial API and implementation
 * 
 */
package org.yakindu.sct.model.stext.test.validation;

import static org.eclipse.xtext.EcoreUtil2.eAllOfType;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.yakindu.sct.model.sgraph.util.SGgraphUtil.firstNamed;

import java.util.HashMap;

import org.eclipse.xtext.junit4.InjectWith;
import org.eclipse.xtext.junit4.XtextRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.yakindu.sct.model.sgraph.Choice;
import org.yakindu.sct.model.sgraph.Entry;
import org.yakindu.sct.model.sgraph.Exit;
import org.yakindu.sct.model.sgraph.Region;
import org.yakindu.sct.model.sgraph.State;
import org.yakindu.sct.model.sgraph.Synchronization;
import org.yakindu.sct.model.sgraph.Transition;
import org.yakindu.sct.model.stext.stext.ReactionTrigger;
import org.yakindu.sct.model.stext.stext.StextFactory;
import org.yakindu.sct.model.stext.test.util.STextInjectorProvider;
import org.yakindu.sct.model.stext.validation.STextValidationMessages;

/** 
 * @author terfloth - Initial contribution
 * 
 */
@RunWith(XtextRunner.class)
@InjectWith(STextInjectorProvider.class)
public class TransitionsWithNoTriggerTest extends AbstractSTextValidationTest implements STextValidationMessages {
	
	
	
	private Entry entry;
	protected State stateA;
	private State stateB;
	private State stateC;
	private State stateD;
	private Choice choice;
	private State stateE;
	private Transition entryToStateA;
	private Transition stateAToStateB;
	private Transition stateBToStateC;
	private Transition stateCToStateD;
	private Transition choiceToStateE;
	private State stateBB;
	private Entry entryB;
	private Exit exitB;
	private Region regionB;
	private Transition entryBToStateBB;
	private Transition stateBBToExitB;

	
	@Override @Before 
	public void setup() {
		super.setup();
		
		StextFactory stextFactory = StextFactory.eINSTANCE;
		
		Region region = factory.createRegion();

		entry = factory.createEntry();
		stateA = factory.createState();
		stateA.setName("A");
		stateB = factory.createState();
		stateB.setName("B");
		stateC = factory.createState();
		stateC.setName("C");
		stateD = factory.createState();
		stateD.setName("D");
		choice = factory.createChoice();
		stateE = factory.createState();
		stateE.setName("E");
		entryToStateA = createTransition(entry, stateA);
		stateAToStateB = createTransition(stateA, stateB);
		stateBToStateC = createTransition(stateB, stateC);

		stateCToStateD = createTransition(stateC, stateD);
		stateCToStateD.setSpecification("always");
		ReactionTrigger triggerCToD = stextFactory.createReactionTrigger();
		triggerCToD.getTriggers().add(stextFactory.createAlwaysEvent());
		stateCToStateD.setTrigger(triggerCToD);
		
		choiceToStateE = createTransition(choice, stateE);

		stateBB = factory.createState();
		stateBB.setName("BB");
		entryB = factory.createEntry();
		exitB = factory.createExit();

		regionB = factory.createRegion();
		regionB.getVertices().add(entryB);
		regionB.getVertices().add(stateBB);
		regionB.getVertices().add(exitB);
		stateB.getRegions().add(regionB);
		entryBToStateBB = createTransition(entryB, stateBB);
		stateBBToExitB = createTransition(stateBB, exitB);

		region.getVertices().add(entry);
		region.getVertices().add(stateA);
		region.getVertices().add(stateB);
		region.getVertices().add(stateC);
		region.getVertices().add(stateD);
		statechart.getRegions().add(region);	
	}

	
	
	/** A transition from an entry to a state must have no trigger. */
	@Test public void noTriggerOnTopLevelEntryToState() {

		assertTrue(validator.validate(entryToStateA, diagnostics, new HashMap<Object, Object>()));
		assertIssueCount(diagnostics, 0);
	}

	/** A transition from an entry to a state must have no trigger. */
	@Test public void noTriggerOnSubStateEntryToState() {

		assertTrue(validator.validate(entryBToStateBB, diagnostics, new HashMap<Object, Object>()));
		assertIssueCount(diagnostics, 0);
	}

	/** No trigger on a transition from a choice to a state is valid. */
	@Test public void noTriggerOnChoiceToState() {

		assertTrue(validator.validate(choiceToStateE, diagnostics, new HashMap<Object, Object>()));
		assertIssueCount(diagnostics, 0);
	}

	/** A transition from a state with exit may have no trigger. */
	@Test public void noTriggerOnExitTransition() {

		assertTrue(validator.validate(stateBToStateC, diagnostics, new HashMap<Object, Object>()));
		assertIssueCount(diagnostics, 0);
	}

	/** A transition between two regular states (with no exit) should have a trigger. */
	@Test public void missingTriggerOnStateToState() {

		assertTrue(validator.validate(stateAToStateB, diagnostics, new HashMap<Object, Object>()));
		assertIssueCount(diagnostics, 1);
	}

	/** A transition from a state to an exit should have a trigger. */
	@Test public void missingTriggerOnStateToExit() {

		assertTrue(validator.validate(stateBBToExitB, diagnostics, new HashMap<Object, Object>()));
		assertIssueCount(diagnostics, 1);
	}

	/** A transition from a state to a state with a trigger must issue no warning. */
	@Test public void existingTriggerOnStateToState() {

		assertTrue(validator.validate(stateCToStateD, diagnostics, new HashMap<Object, Object>()));
		assertIssueCount(diagnostics, 0);
	}

	
	/**
	 * The outgoing transitions of a sync (fork / join) node must not have any
	 * trigger part. Thus empty transitions must not have any warning. This test
	 * case addresses a bug #75 (
	 * https://code.google.com/a/eclipselabs.org/p/yakindu/issues/detail?id=75 )
	 * .
	 */
	@Test public void noTriggerOnSyncOutgoing() {
		statechart = loadStatechart("ValidEmptyTransitionFromSync.sct");
		
		Region validRegion = firstNamed(eAllOfType(statechart, Region.class), "valid");
		Synchronization sync = eAllOfType(validRegion, Synchronization.class).get(0);
		
		for (Transition t : sync.getOutgoingTransitions()) {
			assertTrue(validate(t));
			assertIssueCount(diagnostics, 0);
		}
	}

	/**
	 * If a sync node has multiple incoming transitions then the transitions can omit the trigger.
	 */
	@Test public void noTriggerOnSyncIncoming() {
		statechart = loadStatechart("ValidEmptyTransitionFromSync.sct");
		
		Region validRegion = firstNamed(eAllOfType(statechart, Region.class), "valid");
		Synchronization sync = eAllOfType(validRegion, Synchronization.class).get(0);
		
		for (Transition t : sync.getIncomingTransitions()) {
			assertTrue(validate(t));
			assertIssueCount(diagnostics, 0);
		}
	}

	
	/**
	 * If a sync node has multiple incoming transitions then the transitions can omit the trigger.
	 */
	@Test public void missingTriggerOnSyncSingleIncoming() {
		statechart = loadStatechart("ValidEmptyTransitionFromSync.sct");
		
		Region validRegion = firstNamed(eAllOfType(statechart, Region.class), "warning");
		Synchronization sync = eAllOfType(validRegion, Synchronization.class).get(0);
		
		assertEquals(1, sync.getIncomingTransitions().size());
		assertTrue(validate(sync.getIncomingTransitions().get(0)));
		assertIssueCount(diagnostics, 1);
		assertWarning(diagnostics, ISSUE_TRANSITION_WITHOUT_TRIGGER);
	}


}
