/*

Copyright: All contributers to the Umple Project

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

 */

namespace cruise.umple.compiler;

class XtextGenerator
{

  public void generate()
  {
    StringBuilder xtext = new StringBuilder();
    patterns.add(new PatternHandler("\\[=(.*):\\[\\]\\]"){
      @Override
      public String manipulate(String[] groups, Integer frequency){
        return groups[0] + "_" + frequency + "?=" + "\'[]\'";
      }
    });
    patterns.add(new PatternHandler("\\[\\[(.*)\\]\\]"){
      @Override
      public String manipulate(String[] groups, Integer frequency){
        return getXtextRulename(groups[0]) + frequency + "+=" + getXtextRulename(groups[0]);
      }
    });
    patterns.add(new PatternHandler("\\[([^~=!*].*),(.*),(.*)>([0-9]),([0-9]),([0-9])\\]"){
      @Override
      public String manipulate(String[] groups, Integer frequency){
        return "("+groups[Integer.parseInt(groups[5])] + "_" + frequency + "=ID)?("+groups[Integer.parseInt(groups[4])] + "_" + frequency + "=ID)?("+groups[Integer.parseInt(groups[3])] + "_" + frequency + "=ID)";
      }
    });
    patterns.add(new PatternHandler("\\[([^~=!*].*),(.*)>([0-9]),([0-9])\\]"){
      @Override
      public String manipulate(String[] groups, Integer frequency){
        return "("+groups[Integer.parseInt(groups[3])] + "_" + frequency + "=ID)?("+groups[Integer.parseInt(groups[2])] + "_" + frequency + "=ID)";
      }
    });
    patterns.add(new PatternHandler("\\[([^~=!*].*),(.*)\\]"){
      @Override
      public String manipulate(String[] groups, Integer frequency){
        return "("+groups[0] + "_" + frequency + "=ID)" + "("+groups[1] + "_" + frequency + "=ID)";
      }
    });
    patterns.add(new PatternHandler("\\[([^~=!*].*)\\]"){
      @Override
      public String manipulate(String[] groups, Integer frequency){
        return groups[0] + "_" + frequency + "=ID";
      }
    });
    
    patterns.add(new PatternHandler("\\[~(.*)\\]"){
      @Override
      public String manipulate(String[] groups, Integer frequency){
        terminals.add("nameID:('a'..'z'|'A'..'Z'|'_')('a'..'z'|'A'..'Z'|'0'..'9'|'-'|'_')*");
        return groups[0] + "_" + frequency + "=nameID";
      }
    });
    patterns.add(new PatternHandler("\\[!(.*):(.*)\\]"){
      @Override
      public String manipulate(String[] groups, Integer frequency){
        if(groups[1].equals("\\d+")){
          return groups[0] + "_int=INT";
        }
        else {
          terminals.add(groups[0]+":"+groups[1].replace("\\d","(INT)"));
          return groups[0] + "_" + frequency + "=" + groups[0];
        }
      }
    });
    patterns.add(new PatternHandler("\\[=([^:]*)\\]"){
      @Override
      public String manipulate(String[] groups, Integer frequency){
        if(!groups[0].equals("||")){
          return groups[0] + "_" + frequency + "?=" + "\'"+groups[0]+"\'";
        }
        else {
          
          return "\'||\'";
        }
      }
    });
    
    patterns.add(new PatternHandler("\\[=(.*):(.*)\\]"){
      @Override
      public String manipulate(String[] groups, Integer frequency){
        String options = "";
        String pipe = "";
        for(String option:groups[1].split("[|]")){
          options+=pipe+query(option);
          pipe = "|";
        }
        return groups[0] + "_" + frequency + "=" + "("+options+")";
      }
    });
    patterns.add(new PatternHandler("\\[\\Q**\\E(.*)\\]"){
      @Override
      public String manipulate(String[] groups, Integer frequency){
      terminals.add("block:\'begin\'(.*)\'end\'");
        return groups[0] + "_" + frequency + "=block";
      }
    });
    patterns.add(new PatternHandler("\\[\\Q*\\E(.*)\\]"){
      @Override
      public String manipulate(String[] groups, Integer frequency){
      terminals.add("block:\'begin\'(.*)\'end\'");
        return groups[0] + "_" + frequency + "=block";
      }
    });
    xtext.append("grammar cruise.umple.Umple with org.eclipse.xtext.common.Terminals\n\ngenerate umple \"http://www.umple.cruise/Umple\"\n\nModel:\n\tmodel=Program_;\n\n");
    HashSet<String> used = new HashSet<String>();
    for(Rule rule:(new UmpleInternalParser()).getRules()){
      if(used.add(rule.getName())){
        xtext.append(translate(rule)+"\n");
      }
    }
    for(String terminal:terminals){
      xtext.append("terminal "+terminal+";\n");
    }
    try {
      writeFile(xtext.toString());
    }
    catch(IOException e1){
    }
    model.setCode(xtext.toString());
  }
  
  private String translate(Rule rule){
    StringBuilder xtext = new StringBuilder();
    xtext.append(getXtextRulename(rule.getName())+":\n\t");
    String pipe = "";
    for(String definition:rule.getDefinitions()){
       xtext.append(pipe);
       pipe = "|\n\t";
       for(PatternHandler pattern:patterns){
         pattern.reset();
       }
       for(String fragment: definition.split(" ")){
         xtext.append(query(fragment));
       }
    }
    xtext.append(";");
    return xtext.toString();
  }
  
  private String query(String fragment){
    if(fragment.equals("")){
      return "";
    }
    for(PatternHandler pattern:patterns){
      if(pattern.canHandle(fragment)){
        return pattern.handle();
      }
    }
    if(!(fragment.equals("\'"))){
      return "\'"+fragment+"\'";
    }
    else {
      return "\""+fragment+"\"";
    }
  }
  
  public abstract class PatternHandler {
    private Pattern pattern;
	private Matcher matcher;
	protected HashMap<String,Integer> frequency = new HashMap<String,Integer>();
    public PatternHandler(String regex){
      pattern = Pattern.compile("(.*)"+regex+"(.*)");
    }
    public boolean canHandle(String input){
      matcher = pattern.matcher(input);
      return matcher.matches();
    }
    public void reset(){
      frequency.clear();
    }
    public String handle(){
      String[] groups = new String[matcher.groupCount()-2];
      String middle = "";
      for(int i=0;i<matcher.groupCount()-2;i++){
        groups[i]=matcher.group(i+2);
        middle += groups[i];
      }
      if(frequency.containsKey(middle))
      {
        frequency.put(middle,frequency.get(middle)+1);
      }
      else
      {
        frequency.put(middle,1);
      }
      String first = matcher.group(1);
      String last = matcher.group(matcher.groupCount());
      return first +"("+manipulate(groups,frequency.get(middle))+")"+last;
    }
    public abstract String manipulate(String[] groups, Integer middle);
  }
  
  private String getXtextRulename(String rulename){
    return rulename.substring(0,1).toUpperCase()+rulename.substring(1).replace("::","_")+"_";
  }
  
  private void writeFile(String contents) throws IOException
  {
    String path = StringFormatter.addPathOrAbsolute( 
                  model.getUmpleFile().getPath(), 
                            getOutput());
    
    String filename = path + "Umple.xtext";
    File file = new File(path);
    file.mkdirs();

    BufferedWriter bw = new BufferedWriter(new FileWriter(filename));
    try
    {
      bw.write(contents);
      bw.flush();
    }
    finally
    {
      bw.close();
    }
  }
}
