/*

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 analyzses mixset related tokens to populate the Umple meta model for
mixsets.

Please refer to UmpleInternalParser.ump for more details.

*/


namespace cruise.umple.compiler;

class UmpleModel{
/*
 * This method returns a mixset from UmpleModel based on its name.
 * return null if the mixset is not found.
 * @Abdulaziz   
 */
  Mixset getMixset(String name) {
    Mixset mixset = null;
    for(MixsetOrFile mOrF : getMixsetOrFiles())
      { 
      if (mOrF.getIsMixset() && mOrF.getName().equals(name))
        mixset = (Mixset)mOrF;
      }
    return mixset;
  }
    
/*
 * This method returns UmpleFile from UmpleModel based on its name. 
 * return null if the mixset is not found.
 * usage: to know if the file in which a mixset is mentioned was already used before. 
 * @Abdulaziz   
 */	
  UmpleFile getUmpleFile(String name){
    UmpleFile uFile = null;
    for(MixsetOrFile mOrF : getMixsetOrFiles())
	  {
	    if (! mOrF.getIsMixset() && mOrF.getName().equals(name))
	      uFile = (UmpleFile)mOrF;
	  }
	return uFile;
  }
 
	
}

class UmpleInternalParser
{
	depend cruise.umple.compiler.UmpleFile;
	depend  java.util.stream.*;
	 
 // prepare mixsets that are inside an Umple class
  private void analyzeMixsetDefinition(Token aToken , String className)
  {
  	aToken.addSubToken(new Token("entityName",className) );
	aToken.addSubToken(new Token("entityType","class") );
	analyzeMixset(aToken);		
	}
/*
 * This method handles mixset use statements appearing in both code and in the console. The method adds mixset use statements to umple model in the first round on analysis, before
 * other umple elements are added to umple model. So, there is no issue regarding which line the mixset use statements are mentioned in.   
 */
  private void analyzeMixsetUseStatement(Token t, int analysisStep){
    if (analysisStep != 1) // the analyze occurs just for analysisStep == 1. 
    {
      return;
    } 
    //else
    
    if (t.is("useStatement"))
    {
       String value = t.getValue("use");
       
       // ignore .ump files since they are proccessed in UseStatementParserAction class (UmpleInternalParser_CodeParserHandlers.ump).
       if (value.endsWith(".ump"))
       {
         return;   
       } 
       //Otherwise
       
       String mixsetName = value;
       int useLineNumber = t.getPosition().getLineNumber();
       //UmpleFile mixsetUseFile = model.getUmpleFile(); 
       String fileName = t.getPosition().getFilename();
       UmpleFile mixsetUseFile = new UmpleFile(fileName);
       // check if the mixset was added before 
       Mixset mixset = model.getMixset(mixsetName);
       if(mixset == null)
       {
         mixset = new Mixset(mixsetName);
	     mixset.setUseUmpleFile(mixsetUseFile);
	     mixset.setUseUmpleLine(useLineNumber);
	     model.addMixsetOrFile(mixset);
	   }
	   else if (mixset.getUseUmpleFile() == null)
	   {
	     mixset.setUseUmpleFile(mixsetUseFile);
	     mixset.setUseUmpleLine(useLineNumber);
	   }
	   
	   // this handles the case when a mixset definition is in a file and the mixset use exists in a subsequent file.
	   parseMixset(mixset);
       
      
    }
  }
  
/*
 * This method parses all waiting fragments of a mixset, if there is a mixset-use-statment specified in some of the files.  
 */	
 /*
 private void parseMixset(){
 
   for (MixsetOrFile mOrF : model.getMixsetOrFiles())
   {
     if(mOrF.getIsMixset() &&  (mOrF.getUseUmpleFile() != null) ) // the second condition is to check if there is a use statement.
     {
       Mixset mixset =(Mixset) mOrF;
       for(MixsetFragment mixsetWaitingFrag: mixset.getFragments())
       {  
         //String allMixsetBody = modelMixset.getWaitingFragments().stream().map(mixsetFrag -> mixsetFrag.getBody()).collect(Collectors.joining(" "));
         ParseResult pResult= parse("MixsetFragmentParsing",  mixsetWaitingFrag.getBody()); 
         Token answer = parser.getRootToken(); // result of parsing the body of a mixset waitingFragments
         analyzeAllTokens(answer); 
       }
     }
   }
   
}
*/

/*
 * This method loops through a mixset to parse its waiting fragments.
 * It should be used after checking existing of a mixset-use-statment.  
 */	

 private void parseMixset(Mixset mixset){
   
   for(MixsetFragment mixsetFragment: mixset.getMixsetFragments())
   {
    if(mixsetFragment.getIsParsed())
    continue;
    //Otherwise
    parseMixsetWaitingFragment(mixsetFragment);
    
   }
 }

/*
 * This method parses a waiting fragment of a mixset.
 * It should be used after checking existing of a mixset-use-statment.  
 */	
  depend cruise.umple.compiler.exceptions.*;
  
  private void parseMixsetWaitingFragment(MixsetFragment mixsetFragment){
  
  if(! mixsetFragment.getIsParsed()) // a fragment that is not parsed before. 
  {
    String mixsetFragmentFile = mixsetFragment.getOriginalUmpFile().getPath()+"/"+mixsetFragment.getOriginalUmpFile().getName()+".ump";
    int mixsetFragmentLine = mixsetFragment.getOriginalUmpLine() - 1;
    ParseResult result= parse("MixsetFragmentParsing",  mixsetFragment.getBody(), mixsetFragmentFile, null, mixsetFragmentLine,0); 
    setParseResult(result);
    Token answer = parser.getRootToken(); // result of parsing the body of a mixset waitingFragments
    setRootToken(answer);
    model.setLastResult(result);
    analyzeAllTokens(answer);
    mixsetFragment.setIsParsed(true);
   }
   
 }

 private void analyzeMixsetToken(Token t, int analysisStep)
  {
   if (analysisStep != 2)
    {
      shouldProcessAgain = shouldProcessAgain || (analysisStep == 1);
      return;
    }      
    if (t.is("mixsetDefinition"))
    {
      analyzeMixset(t);
    }
    
  }
  
  
  private Mixset analyzeMixset(Token token)
  {
    String mixsetName = token.getValue("mixsetName");
  	
    // check if the mixset is was not added before
    Mixset mixset = model.getMixset(mixsetName);
    if(mixset  == null)
	{
	  mixset  = new Mixset(mixsetName);
	  model.addMixsetOrFile(mixset);
	}
	
	
	Position mixsetFragmentPosition = null;
	int mixsetFragmentLineNumber = 0;
	
	String mixsetBody = token.getValue("extraCode");
	if(mixsetBody != null)
	{
	  mixsetFragmentPosition = token.getSubToken("extraCode").getPosition();
	}		
	
	//inline mixset def.
	String entityType = token.getValue("entityType");
	String entityName = token.getValue("entityName");
	
	// mixset with one element
	String oneElementMixset = token.getValue("oneElement");
	
	if(entityType != null) {
	  if (oneElementMixset != null)
	  {
	    mixsetBody = entityType + " "+entityName + " { "+ oneElementMixset + " }";
	    mixsetFragmentPosition = token.getSubToken("oneElementMixset").getPosition();
	    
	  }
	  else
	    mixsetBody = entityType + " "+entityName + " { "+ mixsetBody + " }";
	    mixsetFragmentPosition = token.getSubToken("extraCode").getPosition();	
	  }
	else if (oneElementMixset != null) 
	  {
	    mixsetBody = oneElementMixset;
	    mixsetFragmentPosition = token.getSubToken("oneElementMixset").getPosition();	  
	  }
	  
	  if(mixsetBody == null)
	  return mixset; // becuase there is no fragment to add. 
	  
	  mixsetFragmentLineNumber = mixsetFragmentPosition.getLineNumber();
	  UmpleFile mixsetFragmentUmpleFile = model.getUmpleFile(); // where the mixset keyword is encountered. Not the use statement 
	  MixsetFragment mixsetFragment = new MixsetFragment(mixsetFragmentUmpleFile, mixsetFragmentLineNumber, mixsetBody);
	  
	  // Here the mixset fragmet is considered as waitingFragment (mixsetFragment.isParsed==flase). 
	  mixset.addMixsetFragment(mixsetFragment);
	  
	  // parse mixset fragments right away if there is a use statment.
	  // 
	  if(mixset.getUseUmpleFile() != null)
	  parseMixsetWaitingFragment(mixsetFragment);
	  
	  
	return  mixset ;
  }
    

  
}

