package premun.mps.ingrid.importer.steps;

import org.jetbrains.mps.openapi.model.*;
import premun.mps.ingrid.parser.grammar.*;

/**
 * Import step that creates concepts, constraint data concepts and interface concepts for grammar rules.
 */
public class ConceptImporter extends ImportStep {

    @Override
    public void Execute() {
        // Creates constraint data concepts
        this.grammar.rules
            .values()
            .stream()
            .filter(r -> r instanceof RegexRule)
            .map(r -> (RegexRule) r)
            .forEach(this::importToken);

        // Creates interfaces for parser rules and concepts for alternatives
        this.grammar.rules
            .values()
            .stream()
            .filter(r -> r instanceof ParserRule)
            .map(r -> (ParserRule) r)
            .forEach(this::importRule);
    }

    /**
     * Imports parser rule as an interface or a concept (not their children or editors).
     *
     * For split rules, all alternatives are imported as empty concepts.
     * Example:
     * element:   '<' Name '>' Content '</' Name '>'
     *        |   '<' Name '/>'
     *        ;
     *
     * There will be created one interface and two concepts for this rule that will implements this concept.
     * They will be named element (interface) and element_1 and element_2 (concepts).
     *
     * @param rule Rule to be imported.
     */
    private void importRule(ParserRule rule) {
        // Generate unique name
        rule.name = this.namingService.generateName(rule.name);

        if (rule.alternatives.size() > 1) {
            // Rule with more alternatives - we will create an interface
            // and a child for each alternative that will inherit this interface
            SNode iface = this.nodeFactory.createInterface(rule.name, "Interfaces." + rule.name);
            this.structureModel.addRootNode(iface);
            rule.node = iface;

            // For each alternative, there will be a concept
            for (int i = 0; i < rule.alternatives.size(); ++i) {
                Alternative alternative = rule.alternatives.get(i);
                String name = this.namingService.generateName(rule.name + "_" + (i + 1));
                String description = alternative.comment != null ? alternative.comment : rule.name;

                // Concrete element, we can create a concept
                SNode concept = this.nodeFactory.createConcept(name, name, description, "Rules." + rule.name, rule.equals(this.grammar.rootRule));
                alternative.node = concept;

                this.structureModel.addRootNode(concept);
            }
        } else {
            Alternative alternative = rule.alternatives.get(0);
            String description = alternative.comment != null ? alternative.comment : rule.name;

            // Not a rule that splits into more rules - we create it directly
            SNode concept = this.nodeFactory.createConcept(rule.name, rule.name, description, "Rules." + rule.name, rule.equals(this.grammar.rootRule));
            this.structureModel.addRootNode(concept);
            rule.node = concept;
            alternative.node = concept;
        }
    }

    /**
     * Imports a regex rule as a constraint data type element.
     *
     * @param rule Rule to be imported.
     */
    private void importToken(RegexRule rule) {
        rule.name = this.namingService.generateName(rule.name);
        SNode node = this.nodeFactory.createConstraintDataType(rule.name, rule.regexp, "Tokens");
        this.structureModel.addRootNode(node);
        rule.node = node;
    }
}

@@ -122,16 +122,37 @@
 mar.*;%0A%0A
+import java.util.*;%0A%0A
 /**%0A * I
@@ -1465,32 +1465,80 @@
 serRule rule) %7B%0A
+        if (this.isInterfaceNeeded(rule)) %7B%0A    
         // Gener
@@ -1553,16 +1553,20 @@
 ue name%0A
+    
         
@@ -1634,90 +1634,16 @@
     
-if (rule.alternatives.size() %3E 1) %7B%0A            // Rule with more alternatives - w
+    // W
 e wi
@@ -1664,31 +1664,16 @@
 nterface
-%0A            //
  and a c
@@ -2640,16 +2640,175 @@
  else %7B%0A
+            // Generate unique name%0A            rule.name = this.namingService.generateName(%22I%22 + rule.name);%0A%0A            // We will create plain old concept%0A
         
@@ -3748,11 +3748,1235 @@
 e;%0A    %7D
+%0A%0A    /**%0A     * Detects, whether we need to create an interface for a rule.%0A     *%0A     * 1) When has more alternatives - then we need it to%0A     * 2) Or, when there could a shortcut lead through this concept. Then we need it, so that all end nodes can inherit it.%0A     *    Example situation:%0A     *    a  : a1%0A     *       %7C a2%0A     *       %7C a3%0A     *       ;%0A     *%0A     *    a2 : b1%0A     *       ;%0A     *%0A     *    b1 : C1%0A     *       %7C C2%0A     *       ;%0A     *%0A     *    Then, even when a2 is simple, we need an IA2 interface, so that nodes B1_1 and B1_2 can inherit it.%0A     *%0A     * @param rule Rule, to be verified%0A     * @return True, when we need to create an interface for this node.%0A     */%0A    private boolean isInterfaceNeeded(ParserRule rule) %7B%0A        // Case 1)%0A        if (rule.alternatives.size() %3E 1) %7B%0A            return true;%0A        %7D%0A%0A        // Case 2)%0A        List%3CRuleReference%3E elements = rule.alternatives.get(0).elements;%0A        if (elements.size() == 1) %7B%0A            RuleReference element = elements.get(0);%0A%0A            if (element.quantity == Quantity.EXACTLY_ONE && element.rule instanceof ParserRule) %7B%0A                return true;%0A            %7D%0A        %7D%0A%0A        return false;%0A    %7D
 %0A%7D%0A

Fix bug, when some rules didn't have an interfaces created, even when shortcut could have lead through them.