/*

Copyright: All contributers to the Umple Project

This file is made available subject to the open source license found at:
http://umple.org/license

This file analyzes token related State Machines and populates the State Machine
related meta model entities in Umple.

Please refer to UmpleInternalParser.ump for more details.

*/

namespace cruise.umple.compiler;

class UmpleInternalParser
{
  internal StateMachine placeholderStateMachine = null;
  internal Map<String,Token> stateMachineNameToToken = new HashMap<String, Token>();
  internal Map<State,List<Token>> possiblyUnknownStates = new HashMap<State,List<Token>>();
  internal List<Token> stateNames = null;
  internal List<Token> transitionNames = null;
  internal List<Token> ignoredTransitions = new ArrayList<Token>();
  
  
  // Allow to declare several active objects within the same class, the name of the generated state-machine
  // will be "stateMachine<numberOfActiveObjects>"
  private static int numberOfActiveObjects = 1;
  
  //Issue 531. Number of ocurrences of a given state in a state machine. 
  //More than one means that the transition is ambiguous and should generate a warning message 
  private int numberOfOcurrences;
   
  //Check if "extra code" is likely a malformed state machine
  private boolean extraCodeIsMalformedStateMachine(Token extraCodeToken){
    String code = extraCodeToken.getValue();
    String[] parts = code.split("\\{");
    if(parts.length < 2) return false; // This means there are no opening brackets
    if(!Token.isValidIdentifier(parts[0].trim())) return false; //This means we don't have an identifier. If the SM name is misspelt, that should be picked up elsewhere.
    return true; //We have an identifier followed by a { followe by something else that is probably a state machine
  }
  
  //Checks to see if stateName is alpha-numeric or HStar, for deep history
  private void checkStateNameOfToken(Token myToken){
    
    boolean shouldBeLast = false;
    
    //Issue 148
    if (myToken.is("transition") || myToken.is("autoTransition") || myToken.is("standAloneTransition"))
    {
      String[] stateNames;
      
      if(myToken.is("transition") || myToken.is("autoTransition") )	
        stateNames = myToken.getValue("stateName").split("\\.");
      else
    	stateNames = myToken.getValue("fromState").split("\\.");
    	
      for(String stateName : stateNames)
      {
        if(shouldBeLast){setFailedPosition(myToken.getPosition(), 152, myToken.getValue("stateName"));}
        
        if (stateName.equals("HStar") || stateName.equals("H")){
          shouldBeLast = true; //HStar and H must be the last state in dotNotation
          continue;
        }
        
        if(!myToken.isValidIdentifier(stateName)){
          setFailedPosition(myToken.getPosition(), 152, myToken.getValue("stateName"));
        }
      }
    }
    else{
      if(!myToken.isValidIdentifier(myToken.getValue("stateName"))){
          setFailedPosition(myToken.getPosition(), 152, myToken.getValue("stateName"));
        }
    }
  }
  
  // Analyze state machine related tokens
  private void analyzeStateMachineToken(Token token, int analysisStep)
  {
    if (analysisStep != 1)
    {
      return;
    }
  
    if (token.is("stateMachineDefinition"))
    {
      analyzeStateMachineDefinition(token);
    }
  }  
  
  private String getThreadName(Token activeToken)
  {
    Token name = activeToken.getSubToken("name");
    
    if (name == null)
    {
      return "thread1";
    }
    return name.getValue();
  }
  
  private String getActiveCode(Token activeToken)
  {
    Token code = activeToken.getSubToken("code");
    
    return code.getValue();
  }
  
  private void analyzeActiveObject(Token activeToken, UmpleClassifier uClassifier)
  {
    transformIntoStateMachineToken(activeToken);
    analyzeStateMachine(activeToken, uClassifier);
  }
  
  // Restructure the [activeDefinition] token into a state machine token with a do activity
  private void transformIntoStateMachineToken(Token stateMachineToken)
  {
    Token activeToken = stateMachineToken.detach("name","stateMachine" + numberOfActiveObjects);
    Token stateToken = new Token("state","START_TOKEN");
    Token subState = new Token("state", "START_TOKEN");
    Token doActivity = new Token("activity", "START_TOKEN");

    stateMachineToken.addSubToken(new Token("{", "STATIC"));
         
    stateToken.addSubToken(new Token("stateName","topLevel"));
    stateToken.addSubToken(new Token("{", "STATIC"));
    
    subState.addSubToken(new Token("stateName", getThreadName(activeToken)));
    subState.addSubToken(new Token("{", "STATIC"));
    
    doActivity.addSubToken(new Token("do", "STATIC"));
    
    // Add all multilingual code blocks to our do activity
    int numLangs = 0;
    List<Token> subTokens = activeToken.getSubTokens(); 
    for (Token t: subTokens)
    {
      if(t.is("codeLang"))
      {
        doActivity.addSubToken(t.copy());
        doActivity.addSubToken(new Token("{", "STATIC"));
        doActivity.addSubToken(activeToken.getSubToken("code",numLangs).copy());
        doActivity.addSubToken(new Token("}", "STATIC"));
        numLangs++;
      }
    }
    if (numLangs == 0)
    {
      // If no languages are specified in active block
      doActivity.addSubToken(new Token("{", "STATIC"));
      doActivity.addSubToken(activeToken.getSubToken("code",0).copy());
      doActivity.addSubToken(new Token("}", "STATIC"));
    }
    
    subState.addSubToken(doActivity);
    stateToken.addSubToken(new Token("}", "STATIC"));
    stateToken.addSubToken(subState);
    stateToken.addSubToken(new Token("}", "STATIC"));
    stateMachineToken.addSubToken(stateToken);
    stateMachineToken.addSubToken(new Token("}", "STATIC"));
  }

  // Analyze state machine related tokens within the context of an Umple class
  private void analyzeStateMachineToken(Token token, UmpleClassifier uClassifier, int analysisStep)
  {
    if (analysisStep != 1)
    {
      return;
    }
    
    if (token.is("stateMachine"))
    {
      if (uClassifier instanceof UmpleClass) {
	  	if (((UmpleClass)uClassifier).isImmutable())
		{
			setFailedPosition(token.getPosition(), 15, uClassifier.getName());
		    return;
		}
	  } else if (uClassifier instanceof UmpleTrait) {
	  	if (((UmpleTrait)uClassifier).isImmutable())
	  	{
	  		//TODO--- trait's code	
	  	    setFailedPosition(token.getPosition(), 15, uClassifier.getName());
	  	    return;
	  	}	
	  }
      
      Token subToken = token.getSubToken(0);
      if (subToken.is("activeDefinition"))
      {
        analyzeActiveObject(subToken, uClassifier);
        numberOfActiveObjects++;
      }
      
      if (subToken.is("enum") || subToken.is("inlineStateMachine"))
      {
        analyzeStateMachine(subToken,uClassifier);
      }
      
      if (subToken.is("referencedStateMachine"))
      {
        analyzedReferencedStateMachine(subToken,uClassifier);
      }
    }
  }

  private int numberOfActiveObjectsInClass(Token token, UmpleClassifier uClassifier)
  {
    int activeObjects = 0;
    
    Token parent = token.getParentToken();
    if (parent != null)
    {
      for (Token sub : parent.getSubTokens())
      {
        if (sub.is("stateMachine"))
        {
          if (sub.getSubToken(0).is("activeDefinition"))
          {
            activeObjects++;
          }
        }
      }
    }
    
    return activeObjects;
  }
  
  private void postTokenStateMachineAnalysis()
  {
    // Generate error message for issue 354. Check for conflicting names between
    // state machines and attributes as well as state machines and association names
    checkStateMachineNameConflict();

    for(Iterator<Map.Entry<State, List<Token>>> i = possiblyUnknownStates.entrySet().iterator(); i.hasNext();){
    	Map.Entry<State, List<Token>> entry = (Map.Entry<State, List<Token>>)i.next();
    	List<Token> tokens = (List<Token>)entry.getValue();
		for(int j = 0; j < tokens.size(); j++){
			//Issue 148
			if(tokens.get(j).is("standAloneTransition"))
			{
			  setFailedPosition(tokens.get(j).getPosition(), 50, entry.getKey().getName());
			}
			else
			  setFailedPosition(tokens.get(j).getPosition(), 50, tokens.get(j).getValue("stateName"));
		}    
    }
    possiblyUnknownStates = new HashMap<State,List<Token>>();
  }
  
  /*
  	Check for name conflicts between state machines and attributes/association names
  	Author: Blake Quebec Desloges
  */
  private void checkStateMachineNameConflict()
  {
    for(UmpleClass C : model.getUmpleClasses())
    {    
      // Create the list of attribute names 
      List<String> existingAttributeNames = new ArrayList<String>();
      for (Attribute attr : C.getAttributes())
      {
      	existingAttributeNames.add(attr.getName());
      }
      
      //Create the list of state machine names while checking for conflicts with attributes
      List<String> existingStateMachineNames = new ArrayList<String>();
      for (StateMachine sm : C.getStateMachines())
      {
      	// Check if the current state machine conflicts with an attribute name
      	if (existingAttributeNames.contains(sm.getName()))
      	{
      	   Token stateMachineToken = stateMachineNameToToken.get(sm.getName());
          setFailedPosition(stateMachineToken.getPosition(), 52, sm.getName());
       }
       else
          existingStateMachineNames.add(sm.getName());
      }
      
      Boolean foundQueued = false;
      Boolean foundPooled = false;
      Boolean foundRegularSM = false;
      Boolean foundEventlessRegularSM = false;
      Token stateMachineToken=null;
    
      for (StateMachine sm : C.getStateMachines())
      {
        if(sm.getQueued())
        {
          foundQueued = true;
          stateMachineToken = stateMachineNameToToken.get(sm.getName());
        }
        else if(sm.getPooled())
        {
          foundPooled = true;
          stateMachineToken = stateMachineNameToToken.get(sm.getName());
        }
        else if(!sm.getQueued() && !sm.getPooled())
        {
          if(sm.getEvents().isEmpty())
          {
            foundEventlessRegularSM = true;
            foundRegularSM = true;
            stateMachineToken = stateMachineNameToToken.get(sm.getName());
          }
          else
          {
            foundEventlessRegularSM = false;
            foundRegularSM = true;
            stateMachineToken = stateMachineNameToToken.get(sm.getName());
          }
        }  
      }
    
      if (foundQueued == true && foundPooled == true && foundRegularSM == true)
      {
        //raise an error if QSM, PooledSM and RegularSM are used in the same class
        setFailedPosition(stateMachineToken.getPosition(), 58, C.getName());
      }
      else if(foundQueued == true && foundPooled == true && foundRegularSM == false)
      {
        //raise an error if QSM and PooledSM are used in the same class
        setFailedPosition(stateMachineToken.getPosition(), 59, C.getName());
      }
      else if(foundQueued == false && foundPooled == true && foundRegularSM == true && foundEventlessRegularSM == false)
      {
        //raise an error if PooledSM and RegularSM are used in the same class
        setFailedPosition(stateMachineToken.getPosition(), 60, C.getName());
      }
      else if(foundQueued == true && foundPooled == false && foundRegularSM == true && foundEventlessRegularSM == false)
      {
        //raise an error if QSM and RegularSM are used in the same class
        setFailedPosition(stateMachineToken.getPosition(), 61, C.getName());
      }

      List<String> existingAssociationNames = new ArrayList<String>();
      List<Association> visitedAssociations = new ArrayList<Association>();
      for(Association assoc : C.getAssociations())
      {
        if (visitedAssociations.contains(assoc))
        {
          continue;
        }
        
      	AssociationEnd firstEnd = assoc.getEnd(0);
      	AssociationEnd secondEnd = assoc.getEnd(1);
      	
      	Boolean checkFirstEnd = !firstEnd.getClassName().equals(C.getName());
      	Boolean checkSecondEnd = !secondEnd.getClassName().equals(C.getName());
      	Boolean associationIsReflexive = !checkFirstEnd && !checkSecondEnd;
      	
      	// check names on other-class end of associations to other classes
        if ((checkFirstEnd || associationIsReflexive) && assoc.getIsLeftNavigable())
        {
          // Check if the association name is the same as a state machine name
          if (existingStateMachineNames.contains(firstEnd.getRoleName()))
          {
             setFailedPosition(assoc.getTokenPosition(), 52, firstEnd.getRoleName());
          }
          else
          {
            existingAssociationNames.add(firstEnd.getRoleName());
          }
          
        }
        if ((checkSecondEnd || associationIsReflexive) && assoc.getIsRightNavigable())
        {
          // Check if the association name is the same as a state machine name
          if (existingStateMachineNames.contains(secondEnd.getRoleName()))
          {
             setFailedPosition(assoc.getTokenPosition(), 52, secondEnd.getRoleName());
          }
          else
          {
            existingAssociationNames.add(secondEnd.getRoleName());
          }
        }
         
        if (associationIsReflexive)
        { 
          // The UmpleClass is only expected to have duplicate references to reflexive associations
          visitedAssociations.add(assoc);
        }
      }
    }
  }
  
  
  private void analyzeStateMachineDefinition(Token stateMachineDefinitionToken)
  {
    StateMachine smd = analyzeStateMachine(stateMachineDefinitionToken,null);
    model.addStateMachineDefinition(smd);
  }

  private void analyzedReferencedStateMachine(Token stateMachineToken, UmpleClassifier uClassifier)
  {
    String name = stateMachineToken.getValue("name");

    if(!Token.isValidIdentifier(stateMachineToken.getValue("name"))){
    	setFailedPosition(stateMachineToken.getPosition(), 150, stateMachineToken.getValue("name"));
    }

    String definitionName = stateMachineToken.getValue("definitionName");
      
    Token stateMachineDefinitionToken = stateMachineNameToToken.get(definitionName);
    if (stateMachineDefinitionToken == null)
    {
      return;
    }

    StateMachine sm = new StateMachine(name);
    //------------
    boolean wasSet = true;
    if (uClassifier instanceof UmpleClass) {
    	wasSet = sm.setUmpleClass((UmpleClass)uClassifier);
    } else if (uClassifier instanceof UmpleTrait) {
    	wasSet = sm.setUmpleTrait((UmpleTrait)uClassifier);
    }
    //------------
    if (!wasSet)
    {
    	// Future-proofing: currently all paths cause wasSet to be true

    	if (uClassifier instanceof UmpleClass) {
	    	setFailedPosition(stateMachineToken.getPosition(), 15, uClassifier.getName());
	    } else if (uClassifier instanceof UmpleTrait) {
	    	//TODO --Trait's code
	    	setFailedPosition(stateMachineToken.getPosition(), 15, uClassifier.getName());
	    } 
    }
    
    populateStateMachine(stateMachineDefinitionToken,sm, uClassifier);
    Token extendedStateMachineTokens = stateMachineToken.getSubToken("extendedStateMachine");
    if (extendedStateMachineTokens != null)
    {
      populateStateMachine(extendedStateMachineTokens, sm, uClassifier);
    }

  }
  //Issue 531
  private void checkUnclearTransition(Token t){
       for(Token subToken : t.getSubTokens()){
           if(subToken.getValue("stateName") != null && subToken.is("state"))
               stateNames.add(subToken);
           if((subToken.is("transition")||subToken.is("autoTransition")) && subToken.getValue("stateName") != null)
               transitionNames.add(subToken);
           if(subToken.hasSubTokens())
               checkUnclearTransition(subToken);
       }       
   }

  private StateMachine analyzeStateMachine(Token stateMachineToken, UmpleClassifier uClassifier)
  {
    placeholderStateMachine = new StateMachine("PLACE_HOLDER");
    String name = stateMachineToken.getValue("name");

    if(!Token.isValidIdentifier(stateMachineToken.getValue("name"))){
    	setFailedPosition(stateMachineToken.getPosition(), 150, stateMachineToken.getValue("name"));
    }
    
    //Issue 531
    stateNames = new ArrayList<Token>();
    transitionNames = new ArrayList<Token>();   
    checkUnclearTransition(stateMachineToken);
    for(Token transition : transitionNames){
        numberOfOcurrences = 0;
        for(Token state : stateNames)
            if(transition.getValue("stateName").equals(state.getValue("stateName")))
                numberOfOcurrences++;
        if(numberOfOcurrences > 1)
            getParseResult().addErrorMessage(new ErrorMessage(66,transition.getPosition(), transition.getValue("stateName")));
    }

     //Issue200
    StateMachine sm = null;
    
    if ((uClassifier != null) && (uClassifier instanceof UmpleClass)) 
    {
	    if(((UmpleClass)uClassifier).getStateMachine(name) != null)
	    {
	      	sm = ((UmpleClass)uClassifier).getStateMachine(name);
	    }
    }
    
    if(sm == null)
    {
    
	    if(model.getStateMachineDefinition(name) != null)
	    {
	      sm = model.getStateMachineDefinition(name);
	      Token stateToken;
	      
	      List<Token> smToken = stateMachineToken.getSubTokens();
	      int i = 0;
	      
	      while(i < stateMachineToken.numberOfSubTokens())
	      {
	    	  stateToken = smToken.get(i);
	    	  if(stateToken.is("state"))
	    	  {
	    		  
	    		  stateMachineNameToToken.get(name).addSubToken(stateToken.copy());
	    		  i++;
	    	  }
	    	  else
	    	  {
	    		  i++;
	    	  }
	    		  
	      }
	      
	    }
	    else
	    {
	      sm = new StateMachine(name);
	      stateMachineNameToToken.put(name,stateMachineToken);
	    }
    }
    
    boolean wasSet = true;
    if (uClassifier instanceof UmpleClass) {
    	wasSet = sm.setUmpleClass((UmpleClass)uClassifier);
    } else if (uClassifier instanceof UmpleTrait) {
    	wasSet = sm.setUmpleTrait((UmpleTrait)uClassifier);
    }
    if (!wasSet)
    {
    	// Future-proofing: currently all paths cause wasSet to be true
    	if (uClassifier instanceof UmpleClass) {
	    	setFailedPosition(stateMachineToken.getPosition(), 15, uClassifier.getName());
	    } else if (uClassifier instanceof UmpleTrait) {
	    	//TODO --Trait's code
	    	setFailedPosition(stateMachineToken.getPosition(), 15, uClassifier.getName());
	    } 
    }
      
    
    if(stateMachineToken.is("pooled"))
    {
      sm.setPooled(true);
    }
    
    if(stateMachineToken.is("queued"))
    {
      sm.setQueued(true);
    }
    
    populateStateMachine(stateMachineToken, sm, uClassifier);

    //Place all states from transitions that weren't declared in the StateMachine
    //TODO: start at level of transition
    //TOcheck: multiple transitions to same state?
    ArrayList<Transition> allTransitions = sm.getAllTransitions();
    ArrayList<Transition> removeTransitions = new ArrayList<Transition>();
    
    while (placeholderStateMachine.numberOfStates() > 0)
    {
      State s = placeholderStateMachine.getState(0);
      if (s.getName().indexOf('.') > 0)
      {
        for (Transition t : allTransitions)
        {
          boolean notFound = false;
          
          if (t.getNextState().equals(s))
          {
            String toState = s.getName();
            String tmpName = toState;
            String[] stateNames = tmpName.split("\\.");
            //Strip last state from dotNotation
            //If the super state does not have a state machine then the state will be added to the top level sm
            //TODO: check that the top level doesn't have a state with the same name
            while(tmpName.contains(".") && !notFound){
              tmpName = tmpName.substring(0,tmpName.lastIndexOf('.'));
              State superState = sm.findState(tmpName);
              if(superState != null)
              {
                for (StateMachine nesSm : superState.getNestedStateMachines()){
                  //TODO: add support for concurrent sm
                  s.setStateMachine(nesSm);
                  break;
                }
                s.setName(stateNames[stateNames.length - 1]);
                if (s.getIsHistoryState())
                {
                  possiblyUnknownStates.remove(s);
                  s.getStateMachine().setContainsHistoryState(true);
                }
                if (s.getIsDeepHistoryState())
                {
                  possiblyUnknownStates.remove(s);
                  s.getStateMachine().setContainsDeepHistoryState(true);
                  addDeepHistoryStatesToNestedStateMachines(s);
                }
                break;
              }
              else
                notFound = true;
              
              if(!tmpName.contains("."))
              {
                //TODO: check to make sure there are no other states at this level with same name
                s.setName(stateNames[stateNames.length - 1]);
                if (s.getIsHistoryState() || s.getIsDeepHistoryState())
                {
                  setFailedPosition(stateMachineToken.getPosition(), 64, sm.getName());
                }
                s.setStateMachine(sm);
                break;
              }
            }
            
            if(notFound) 
	        {
	          notFound = true;
	          removeTransitions.add(t);
	          placeholderStateMachine.deleteState(s);
	          possiblyUnknownStates.remove(s);
	          t.getFromState().deleteTransition(t);
	          getParseResult().addErrorMessage(new ErrorMessage(68,t.getPosition(), toState,t.getPosition().getLineNumber()+""));
	        }
          }
        }
      }
      else
      {
        if (s.getIsHistoryState() || s.getIsDeepHistoryState())
        {
          //TODO: find transition token to raise error at correct position
          setFailedPosition(stateMachineToken.getPosition(), 65, sm.getName());
        }
        s.setStateMachine(sm);
      }
    }
    
    for (Transition t : removeTransitions)
    {
    	sm.getAllTransitions().remove(t);
    }
    
    
    if(sm.getQueued() || sm.getPooled())
    {
      
      if(sm.getEvents().isEmpty())
      { 
        if(!sm.getNestedStateMachines().isEmpty())
        {
          boolean nsmEvents=false;
          for(StateMachine nsm: sm.getNestedStateMachines())
          {
            if(!nsm.getEvents().isEmpty())
            {
              nsmEvents=true;
            }
          }
          if(!nsmEvents && sm.getQueued())
          {
            setFailedPosition(stateMachineToken.getPosition(), 56, sm.getName());
          }
          else if (!nsmEvents && sm.getPooled())
          {
            setFailedPosition(stateMachineToken.getPosition(), 57, sm.getName());
          }
        }
        else if(sm.getNestedStateMachines().isEmpty() && sm.getQueued())
        {
          setFailedPosition(stateMachineToken.getPosition(), 56, sm.getName());
        }
        else if(sm.getNestedStateMachines().isEmpty() && sm.getPooled())
        {
          setFailedPosition(stateMachineToken.getPosition(), 57, sm.getName());
        }
      }
    }
    
    return sm;
  }

  private State createStateFromTransition(Token transitionToken, StateMachine sm)
  {
    checkStateNameOfToken(transitionToken);
    
    String name = transitionToken.getValue("stateName");
    State nextState;
    
    nextState = sm.findState(name);
    
    if ("Final".equals(name))
    {
      nextState = new State(name,sm);
      //Issue492
      nextState.setPosition(transitionToken.getSubToken("stateName").getPosition());
 
    }
    else
    {
      if (nextState == null)
      {
        nextState = placeholderStateMachine.findState(name);
        if(nextState != null)possiblyUnknownStates.get(nextState).add(transitionToken);
      }

      if (nextState == null)
      {
        nextState = new State(name,placeholderStateMachine);
        //Issue492
        nextState.setPosition(transitionToken.getSubToken("stateName").getPosition());
        possiblyUnknownStates.put(nextState, new ArrayList<Token>());
        possiblyUnknownStates.get(nextState).add(transitionToken);
      }
    }
    
   
    return nextState;
  }
  
   private State createStateFromStandAloneTransition(Token transitionToken, StateMachine sm){
	    checkStateNameOfToken(transitionToken);
	    
	    String name = transitionToken.getValue("toState");
	    State nextState;
	    
	    nextState = sm.findState(name);
	    
	    if ("Final".equals(name))
	    {
	      nextState = new State(name,sm);
	      //Issue 492
	      nextState.setPosition(transitionToken.getSubToken("toState").getPosition());
	    }
	    else
	    {
	      if (nextState == null)
	      {
	        nextState = placeholderStateMachine.findState(name);
	        if(nextState != null)possiblyUnknownStates.get(nextState).add(transitionToken);
	      }

	      if (nextState == null)
	      {
	        nextState = new State(name,placeholderStateMachine);
	        //Issue 492
	        nextState.setPosition(transitionToken.getSubToken("toState").getPosition());
	        possiblyUnknownStates.put(nextState, new ArrayList<Token>());
	        possiblyUnknownStates.get(nextState).add(transitionToken);
	      }
	    }
	    
	    return nextState;
	  }
	  
	  
  private void addDeepHistoryStatesToNestedStateMachines(State deepState)
  {
    StateMachine sm = deepState.getStateMachine();
    for (StateMachine nesSm : sm.getNestedStateMachines())
    {
      if (nesSm.getDeepHistoryState() == null)
      {
        State s = new State("HStar", nesSm);
        nesSm.setContainsDeepHistoryState(true);
      }
    }
  }
  
  private String createDotNotationName(String stateName, StateMachine sm)
  {
    String finalName = stateName;
    State pState = sm.getParentState();
    StateMachine root = null;
    while (pState != null)
    {
      finalName = pState.getName().concat("." + finalName);
      root = pState.getStateMachine();
      pState = root.getParentState();
    }
    return finalName;
  }

  private State createStateFromDefinition(Token stateToken, StateMachine sm)
  {
    checkStateNameOfToken(stateToken);
    
    State s = sm.findState(stateToken.getValue("stateName"),false);
    if (s == null)
    {
      s = placeholderStateMachine.findState(stateToken.getValue("stateName"));
      if (s == null)
      {
        String dotName = createDotNotationName(stateToken.getValue("stateName"), sm);
        s = placeholderStateMachine.findState(dotName);
      
      }
      if (s != null)
      {
        s.setName(stateToken.getValue("stateName"));
        s.setStateMachine(sm);
      }
    }
    if (s == null)
    {
      s = new State(stateToken.getValue("stateName"),sm);
      //Issue 492
      s.setPosition(stateToken.getPosition());
    }
    if (s.getIsHistoryState() || s.getIsDeepHistoryState())
    {
      setFailedPosition(stateToken.getPosition(), 63, stateToken.getName());
    }
    possiblyUnknownStates.remove(s);
    

    return s;
  }

  private void populateStateMachine(Token stateMachineToken, StateMachine sm, UmpleClassifier uClassifier)
  {
    boolean isFirst;
    boolean isFinalState = false;
    String changeType = null;
    
    if (sm.getStates().size() != 0)
    	isFirst = false;
    else
    	isFirst = true;
    
    checkStateMachineForDuplicateEvents(stateMachineToken);
    
    for(Token stateToken : stateMachineToken.getSubTokens())
    {
      if(stateToken.is("pooled"))
      {
        sm.setPooled(true);
      }
      
      if(stateToken.is("queued"))
      {
        sm.setQueued(true);
      }    
      
      //Issue 148
       if(stateToken.is("standAloneTransition") && !ignoredTransitions.contains(stateToken))
      {
    	  if(stateToken.getValue("event") != null)
    	    analyzeStandAloneTransition(false,stateToken, stateToken.getValue("fromState"), null, sm);
    	  else
    		analyzeStandAloneTransition(true,stateToken, stateToken.getValue("fromState"), null, sm);
      }
      
      // Concurrency is only allowed between nested state-machines
      if (stateToken.is("||"))
      {
        setFailedPosition(stateMachineToken.getPosition(), 53, sm.getName());
      }
      
      if(stateToken.is("trace"))
      {
      	if (uClassifier instanceof UmpleClass) {
    		analyzeTraceStatement((UmpleClass)uClassifier,stateToken); 
    	} else {
    		//TODO--- trait's functionality for trace;
    	}
      }
      
      if (!stateToken.is("state") && !stateToken.is("stateName"))
      {
        if (stateToken.is("changeType")) { changeType = stateToken.getValue(); }      
        continue;
      }

      if ("-".equals(changeType))
      {
        State deleteMe = sm.findState(stateToken.getValue("stateName"));
        if (deleteMe != null) { deleteMe.delete(); }
      }
      else
      {
        State s = createStateFromDefinition(stateToken,sm);
        s.setFinalState(isFinalState);
        if (isFirst)
        {
          s.setIsStartState(true);
        }
        isFirst = false;
        isFinalState = false;
        analyzeState(stateToken, s);
        changeType = null;
        
        for(Token state : stateToken.getSubTokens())
        {
        	  if(state.is("standAloneTransition") && !ignoredTransitions.contains(state))
             {
           	  analyzeStandAloneTransition(false,state, state.getValue("fromState"), null, sm); 
             }
        }
      }
    }

  }

  private void analyzeState(Token stateToken, State fromState)
  {
    boolean addNewSm = true;
    boolean isConcurrentState = false;
    boolean isFinalState = false;
    String changeType = null;
    
    for(Token subToken : stateToken.getSubTokens())
    {
      
      if (subToken.is("extraCode"))
      {
        if(!subToken.getValue().trim().equals(""))
        {
          setFailedPosition(subToken.getPosition(),1006, subToken.getValue() );
        }
      }
      else if (subToken.is("changeType"))
      {
        changeType = subToken.getValue();
      }
      else if (subToken.is("final"))
      {
        fromState.setFinalState(true);
      }
	  else if (subToken.is("transition") && !ignoredTransitions.contains(subToken))
      {
        analyzeTransition(false,subToken, fromState, changeType); 
      }
      else if (subToken.is("autoTransition") && !ignoredTransitions.contains(subToken))
      {
        analyzeTransition(true,subToken, fromState, changeType);
      }
      else if (subToken.is("activity"))
      {
        analyzeActivity(subToken, fromState);
      }
      else if (subToken.is("entryOrExitAction"))
      {
      	fromState.addAction(analyzeAction(subToken, fromState));
      }
      else if (subToken.is("||"))
      {
        if (fromState.numberOfNestedStateMachines() == 0) { continue; }
        int previousSmIndex = fromState.numberOfNestedStateMachines() - 1;
              StateMachine nestedSm = fromState.getNestedStateMachine(previousSmIndex);
              if (nestedSm.numberOfStates() == 0) { continue; }
              nestedSm.setName(nestedSm.getState(0).getName());
              addNewSm = true;
              isConcurrentState = true;
      }
      else if (subToken.is("state"))
      {
        StateMachine nestedStateMachine = null;
        if (addNewSm)
        {
          //Issue 580
          for(StateMachine nested : fromState.getNestedStateMachines())
            if (nested.getName() == fromState.getName())
            	nestedStateMachine = nested;
          
          if(nestedStateMachine == null)
          {	  
            nestedStateMachine = new StateMachine(fromState.getName());
            fromState.addNestedStateMachine(nestedStateMachine);
          }
        }
        else
        {
          int lastIndex = fromState.numberOfNestedStateMachines() - 1;
          nestedStateMachine = fromState.getNestedStateMachine(lastIndex);
        }
        State s = createStateFromDefinition(subToken,nestedStateMachine);
        //alignStateDefinitionWithStateMachine(s,nestedStateMachine);
        if (addNewSm)
        {
                  if (isConcurrentState)
                  {
                    nestedStateMachine.setName(s.getName());
                  }
          s.setIsStartState(true);
        }
        addNewSm = false;
        
        analyzeState(subToken, s);
      }
    }
  }
  
  //Issue 148
   
   private void analyzeStandAloneTransition(boolean isAutoTransition, Token transitionToken, String state, String changeType, StateMachine sm2){
	   
	   State fromState;
	   
	   fromState = sm2.findState(state);
	   
	   if (fromState == null)
	   {
		    
		   fromState = placeholderStateMachine.findState(state);
	       if(fromState != null)possiblyUnknownStates.get(fromState).add(transitionToken);
	   }

	   if (fromState == null)
	   {
		    fromState = new State(state,placeholderStateMachine);
		    
		    //Issue 492
		    
		    fromState.setPosition(transitionToken.getSubToken("fromState").getPosition());
		    
	        possiblyUnknownStates.put(fromState, new ArrayList<Token>());
	        possiblyUnknownStates.get(fromState).add(transitionToken);
	   }
	   
	   State nextState = createStateFromStandAloneTransition(transitionToken,fromState.getStateMachine());
	    
	    Transition t; 
	    if ("-".equals(changeType))
	    {
	      t = Transition.createPlaceholder(nextState);
	    }
	    else
	    {
	      t = new Transition(fromState, nextState);
	    }

	    t.setAutoTransition(isAutoTransition);
	    
	    //Issue 519
	    t.setPosition(transitionToken.getPosition());

	    String eventName = transitionToken.getValue("event");
	    String eventTimerAmount = transitionToken.getValue("timer");
	    String name="";
	    String type="";
	    String paramType="";
	    String paramName="";
	    if (eventName == null && eventTimerAmount != null)
	    {
	      eventName = fromState.newTimedEventName(nextState);
	    }

	    Token guardToken = transitionToken.getSubToken("guard");
	    if (guardToken != null)
	    {
	      ((GuardAnalyzer)model.getAnalyzer("guard")).setUClassifier( fromState.getStateMachine().getRootStateMachine().getUmpleClass());
              ((GuardAnalyzer)model.getAnalyzer("guard")).setT(t);
              model.getAnalyzer("guard").analyzeToken(guardToken);
	    }

	    Token actionToken = transitionToken.getSubToken("action");
	    if (actionToken != null)
	    {
	      
	      t.setAction(analyzeAction(actionToken,fromState));
	    }
	    if (eventName != null || isAutoTransition)
	    {
	      StateMachine sm = fromState.getStateMachine();
	      UmpleClass uClass = sm.getUmpleClass();
	      Event event = isAutoTransition ? Event.createAutoTransition() : uClass == null ? sm.findOrCreateEvent(eventName) : uClass.findOrCreateEvent(eventName);
	      if (eventTimerAmount != null)
	      {
	        event.setIsTimer(true);
	        event.setTimerInSeconds(eventTimerAmount);
	      }
	      
	      if(event.getName().equals("unspecified") && !sm.getPooled())
	      {
	        event.setUnspecified(true);
	      }
	      
	      if(event.getName().equals("unspecified") && sm.getPooled())
	      {
	        Token eventToken = transitionToken.getSubToken("event");
	        setFailedPosition(eventToken.getPosition(),62, sm.getName());
	      }
	      
	      t.setEvent(event);
	      
	      if ("-".equals(changeType))
	      {
	        fromState.removeTransition(t);      
	      }
	     
	     for (Token subEventToken : transitionToken.getSubTokens())
	     {
	     	Event aEvent = sm.getEvent(event.getName());
	        
	      	if (subEventToken.is("parameterList"))
	      	{
	      		if ( aEvent == event && ! event.hasParams() )
	      		{
	      			for(Token parameterToken : subEventToken.getSubTokens())
	      			{
	      				boolean isList = false;
	      				if (parameterToken.is("parameter"))
	      				{
	      					if (parameterToken.getSubToken("type") != null)
	      					{
	      						paramType = parameterToken.getSubToken("type").getValue();
	      					}
	      					if (parameterToken.getSubToken("list") != null)
	      					{
	      						isList = parameterToken.getSubToken("list").getValue() != null;
	      					}
	      					
	      					paramName = parameterToken.getSubToken("name").getValue();
	      					MethodParameter aParameter  = new MethodParameter(paramName,paramType,null,null, false);
	      					aParameter.setIsList(isList);
	      					if (event.getParams().contains(aParameter))
	      					{
	      						break;
	      					}
	      					else
	      					{
	      						event.addParam(aParameter);
	      					}
	      				}
	      			}
	      		}
	      		else
	      		{ 
	      			for(Token parameterSecondToken : subEventToken.getSubTokens())
	      			{
	      				boolean isListt = false;
	      				if (parameterSecondToken.is("parameter"))
	      				{
	      					String paramTypee="";
	      					if (parameterSecondToken.getSubToken("type") != null)
	      					{
	      						type = parameterSecondToken.getSubToken("type").getValue();
	      					}
	      					if (parameterSecondToken.getSubToken("list") != null)
	      					{
	      						isListt = parameterSecondToken.getSubToken("list").getValue() != null;
	      					}
	      					name = parameterSecondToken.getSubToken("name").getValue();
	      				}	
	      			}
	      		
	      			for (int i=1; i <= event.getParams().size(); i++)
	      			{
	      			    if (!event.getArgs().contains(type) || !event.getArgs().contains(name))
	      			    {
	      			    	setFailedPosition(transitionToken.getPosition(), 51, event.getArgs());
	      			    	
	      			    }
	      			}
	      		}
	      	}
	      }
	      
	      for(Token subToken : transitionToken.getSubTokens())
	      {
	        if (subToken.is("activity"))
	        {
	          t.setAutoTransition(false);
	          Activity act = analyzeActivity(subToken, fromState);
	          act.setOnCompletionEvent(event);
	        }
	    }      
	    
	    }
	  }

	//Code written by Eric Telmer
  private void checkStateMachineForDuplicateEvents(Token stateMachineToken){
    for (Token subToken : stateMachineToken.getSubTokens()){
      if (subToken.is("state")){
        checkStateForDuplicateEvents(subToken, null);
      }
    }
  }
  
  //Checks if there is a transition in a state that will never be reached
	private void checkStateForDuplicateEvents(Token stateToken, Set<String> superStateEvents)
	{
    Set<String> unguardedEvents = new HashSet<String>();
    List<Token> subStates = new ArrayList<Token>();
    boolean priorAutoTransitionExists = false;
    List<Token> transitions = new ArrayList<Token>();
    
    
    if(superStateEvents == null){
      superStateEvents = new HashSet<String>();
    }
    
    if(superStateEvents.contains(null)){
      priorAutoTransitionExists = true;
    }
    
    for(Token subToken : stateToken.getSubTokens()){
      if(subToken.is("transition"))
      {
        transitions.add(subToken);  
      }
      else if(subToken.is("autoTransition")){
        priorAutoTransitionExists = true;
        superStateEvents.add(null);
      }
      else if(subToken.is("state")){
        subStates.add(subToken);
      }
    }
    
    for(Token subToken : transitions)
    {
    	//If there is ever a transition past an auto transition, raise an error        
        if(priorAutoTransitionExists){
          setFailedPosition(subToken.getPosition(), 54, subToken.getValue("event"));
          ignoredTransitions.add(subToken);
        }
        //if a unguarded transition with the same event has been seen,
        // in this state or it's super, raise an error
        if(unguardedEvents.contains(subToken.getValue("event"))){
          setFailedPosition(subToken.getPosition(), 54, subToken.getValue("event"));
          ignoredTransitions.add(subToken);
        }
        if(superStateEvents.contains(subToken.getValue("event"))){
          setFailedPosition(subToken.getPosition(), 55, subToken.getValue("event"));
          ignoredTransitions.add(subToken);	
        }
        if(subToken.getValue("guard") == null){
          if((subToken.getValue("event") == null) && (subToken.getValue("timer") == null)){
            {
              priorAutoTransitionExists = true;
            }
          }
          //System.out.println("added: " + subToken.getValue("event"));
          
          if(subToken.getValue("timer") == null)
          	unguardedEvents.add(subToken.getValue("event"));
        }
    }
    
    unguardedEvents.addAll(superStateEvents);
    
    for(Token subState : subStates){
      Set<String> unguardedEventsAux = new HashSet<String>();
      unguardedEventsAux.addAll(unguardedEvents);
      checkStateForDuplicateEvents(subState, unguardedEventsAux);
    }
    
    return;
	}
	//End Telmer

  private Activity analyzeActivity(Token activityToken, State fromState)
  {
    Activity act= new Activity("", fromState);
  	CodeBlock cb = new CodeBlock();
  	
  	List<String> codelangs = new ArrayList<String>();
  	for(Token tkn: activityToken.getSubTokens()){
  	  if(tkn.is("codeLang")){
  		codelangs.add(tkn.getValue());
  	  } else if(tkn.is("code")){
  		if(codelangs.isEmpty())
  		{
  		  cb.setCode(tkn.getValue());
  		} 
  		else {
  		  for(String lang: codelangs){
  			cb.setCode(lang, tkn.getValue());
          }
          codelangs.clear();
    	}
      }
    }
    act.setCodeblock(cb);
    
    act.setPosition(activityToken.getPosition());
    act.setEndPosition(activityToken.getEndPosition());
    return act;
  }
  
  private Action analyzeAction(Token actionToken, State fromState){
  	Action action= new Action("");
  	CodeBlock cb = new CodeBlock();
  	List<String> codelangs = new ArrayList<String>();
  	for(Token tkn: actionToken.getSubTokens()){
  	  if(tkn.is("codeLang")){
  		codelangs.add(tkn.getValue());
  	  } else if(tkn.is("code")){
  		if(codelangs.isEmpty())
  		{
  		  cb.setCode(tkn.getValue());
  		} 
  		else {
  		  for(String lang: codelangs){
  			cb.setCode(lang, tkn.getValue());
          }
          codelangs.clear();
    	}
      }
    }
    action.setCodeblock(cb);
    	
    action.setPosition(actionToken.getPosition());
    action.setEndPosition(actionToken.getEndPosition());
    action.setActionType(actionToken.getValue("type"));
        
    return action;
  }

  private void analyzeTransition(boolean isAutoTransition, Token transitionToken, State fromState, String changeType)
  {
    State nextState = createStateFromTransition(transitionToken,fromState.getStateMachine());
    
    Transition t; 
    if ("-".equals(changeType))
    {
      t = Transition.createPlaceholder(nextState);
    }
    else
    {
      t = new Transition(fromState, nextState);
    }

    t.setAutoTransition(isAutoTransition);
    
    //Issue 519
    t.setPosition(transitionToken.getPosition());

    String eventName = transitionToken.getValue("event");
    String eventTimerAmount = transitionToken.getValue("timer");
    String name="";
    String type="";
    String paramType="";
    String paramName="";
    if (eventName == null && eventTimerAmount != null)
    {
      eventName = fromState.newTimedEventName(nextState);
    }

    Token guardToken = transitionToken.getSubToken("guard");
    if (guardToken != null)
    {
      if(((GuardAnalyzer)model.getAnalyzer("guard"))!=null){
        ((GuardAnalyzer)model.getAnalyzer("guard")).setUClassifier(fromState.getStateMachine().getRootStateMachine().getUmpleClass());
        ((GuardAnalyzer)model.getAnalyzer("guard")).setT(t);
        model.getAnalyzer("guard").analyzeToken(guardToken);
      }
    }

    Token actionToken = transitionToken.getSubToken("action");
    if (actionToken != null)
    {
      
      t.setAction(analyzeAction(actionToken,fromState));
    }
    if (eventName != null || isAutoTransition)
    {
      StateMachine sm = fromState.getStateMachine();
      UmpleClass uClass = sm.getUmpleClass();
      Event event = isAutoTransition ? Event.createAutoTransition() : uClass == null ? sm.findOrCreateEvent(eventName) : uClass.findOrCreateEvent(eventName);
      if (eventTimerAmount != null)
      {
        event.setIsTimer(true);
        event.setTimerInSeconds(eventTimerAmount);
      }
      
      if(event.getName().equals("unspecified") && !sm.getPooled())
      {
        event.setUnspecified(true);
      }
      
      if(event.getName().equals("unspecified") && sm.getPooled())
      {
        Token eventToken = transitionToken.getSubToken("event");
        setFailedPosition(eventToken.getPosition(),62, sm.getName());
      }
      
      t.setEvent(event);
      
      if ("-".equals(changeType))
      {
        fromState.removeTransition(t);      
      }
     
     for (Token subEventToken : transitionToken.getSubTokens())
     {
     	Event aEvent = sm.getEvent(event.getName());
        
      	if (subEventToken.is("parameterList"))
      	{
      		if ( aEvent == event && ! event.hasParams() )
      		{
      			for(Token parameterToken : subEventToken.getSubTokens())
      			{
      				boolean isList = false;
      				if (parameterToken.is("parameter"))
      				{
      					if (parameterToken.getSubToken("type") != null)
      					{
      						paramType = parameterToken.getSubToken("type").getValue();
      					}
      					if (parameterToken.getSubToken("list") != null)
      					{
      						isList = parameterToken.getSubToken("list").getValue() != null;
      					}
      					
      					paramName = parameterToken.getSubToken("name").getValue();
      					MethodParameter aParameter  = new MethodParameter(paramName,paramType,null,null, false);
      					aParameter.setIsList(isList);
      					if (event.getParams().contains(aParameter))
      					{
      						break;
      					}
      					else
      					{
      						event.addParam(aParameter);
      					}
      				}
      			}
      		}
      		else
      		{ 
      			for(Token parameterSecondToken : subEventToken.getSubTokens())
      			{
      				boolean isListt = false;
      				if (parameterSecondToken.is("parameter"))
      				{
      					String paramTypee="";
      					if (parameterSecondToken.getSubToken("type") != null)
      					{
      						type = parameterSecondToken.getSubToken("type").getValue();
      					}
      					if (parameterSecondToken.getSubToken("list") != null)
      					{
      						isListt = parameterSecondToken.getSubToken("list").getValue() != null;
      					}
      					name = parameterSecondToken.getSubToken("name").getValue();
      				}	
      			}
      		
      			for (int i=1; i <= event.getParams().size(); i++)
      			{
      			    if (!event.getArgs().contains(type) || !event.getArgs().contains(name))
      			    {
      			    	setFailedPosition(transitionToken.getPosition(), 51, event.getArgs());
      			    	
      			    }
      			}
      		}
      	}
      }
      
      for(Token subToken : transitionToken.getSubTokens())
      {
        if (subToken.is("activity"))
        {
          t.setAutoTransition(false);
          Activity act = analyzeActivity(subToken, fromState);
          act.setOnCompletionEvent(event);
        }
    }      
    
    }

  }  

} 
