/**
 * generated by Xtext
 */
package de.ugoe.cs.swe.bnftools.validation;

import com.google.common.base.Objects;
import de.ugoe.cs.swe.bnftools.ebnf.DefinitionList;
import de.ugoe.cs.swe.bnftools.ebnf.EbnfPackage;
import de.ugoe.cs.swe.bnftools.ebnf.EtsiBnf;
import de.ugoe.cs.swe.bnftools.ebnf.Rule;
import de.ugoe.cs.swe.bnftools.ebnf.RuleReference;
import de.ugoe.cs.swe.bnftools.ebnf.SingleDefinition;
import de.ugoe.cs.swe.bnftools.validation.AbstractEbnfValidator;
import de.ugoe.cs.swe.bnftools.validation.EbnfAnalysisUtils;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.validation.Check;

/**
 * Custom validation rules.
 * 
 * see http://www.eclipse.org/Xtext/documentation.html#validation
 */
@SuppressWarnings("all")
public class EbnfValidator extends AbstractEbnfValidator {
  private final static String ruleReferencedOneDescription = "The rule is only referenced by one other rule";
  
  private final static String passthroughRuleDescription = "The rule is a passthrough rule";
  
  private final static String unreferencedPassthroughRuleDescription = "The rule is an unreferenced passthrough rule";
  
  private final static String unusedRuleDescription = "The rule is not referenced anywhere";
  
  private final static String equalAlternativeDescription = "The rule contains equal alternatives";
  
  private final static String duplicateRulesDescription = "The rule is a duplicate";
  
  private final static String nonUniqueNameDescription = "The rule has the same Name as the Rule in Line ";
  
  /**
   * Checks if a rule is only referenced by one other Rule, e.g.:
   * a ::= b
   * b ::= "foo"
   */
  @Check
  public void checkReferencedOnlyOnce(final Rule rule) {
    boolean _isTokenRule = EbnfAnalysisUtils.isTokenRule(rule);
    if (_isTokenRule) {
      return;
    }
    List<RuleReference> references = EbnfAnalysisUtils.findReferences(rule);
    boolean _and = false;
    int _size = references.size();
    boolean _equals = (_size == 1);
    if (!_equals) {
      _and = false;
    } else {
      int _rulenumber = rule.getRulenumber();
      boolean _notEquals = (_rulenumber != 1);
      _and = _notEquals;
    }
    if (_and) {
      this.warning(EbnfValidator.ruleReferencedOneDescription, EbnfPackage.Literals.RULE__NAME);
    }
  }
  
  /**
   * Checks if a a rule has the same definition as another rule e.g.:
   * a ::= "test"
   * b ::= "test"
   * (Problem: does not check if there is a permutation)
   */
  @Check
  public void checkDuplicateRules(final Rule rule) {
    EObject _eContainer = rule.eContainer();
    EObject _eContainer_1 = _eContainer.eContainer();
    EtsiBnf etsiBnf = ((EtsiBnf) _eContainer_1);
    DefinitionList _definitionList = rule.getDefinitionList();
    ICompositeNode definitionList = NodeModelUtils.findActualNodeFor(_definitionList);
    String _text = definitionList.getText();
    String _trim = _text.trim();
    String rightHandSideText = _trim.replaceAll("[ \t\n\r]", "");
    List<Rule> allRules = EbnfAnalysisUtils.getAllRules(etsiBnf);
    for (final Rule currentRule : allRules) {
      boolean _notEquals = (!Objects.equal(currentRule, rule));
      if (_notEquals) {
        DefinitionList _definitionList_1 = currentRule.getDefinitionList();
        ICompositeNode currentRuleDefinitionList = NodeModelUtils.findActualNodeFor(_definitionList_1);
        String _text_1 = currentRuleDefinitionList.getText();
        String _trim_1 = _text_1.trim();
        String currentRuleRightHandSideText = _trim_1.replaceAll("[ \t\n\r]", 
          "");
        boolean _equals = currentRuleRightHandSideText.equals(rightHandSideText);
        if (_equals) {
          String _name = currentRule.getName();
          String _plus = ((EbnfValidator.duplicateRulesDescription + " with rule \"") + _name);
          String _plus_1 = (_plus + 
            "\" (Line ");
          ICompositeNode _findActualNodeFor = NodeModelUtils.findActualNodeFor(currentRule);
          int _startLine = _findActualNodeFor.getStartLine();
          String _plus_2 = (_plus_1 + Integer.valueOf(_startLine));
          String description = (_plus_2 + ")");
          this.warning(description, EbnfPackage.Literals.RULE__NAME);
        }
      }
    }
  }
  
  /**
   * Checks if a Rule got the same Name as another Rule, e.g.:
   * a ::= "foo"
   * a ::= "bar"
   */
  @Check
  public void checkNameIsUnique(final Rule rule) {
    EObject _eContainer = rule.eContainer();
    EObject _eContainer_1 = _eContainer.eContainer();
    final EtsiBnf bnf = ((EtsiBnf) _eContainer_1);
    List<Rule> _allRules = EbnfAnalysisUtils.getAllRules(bnf);
    for (final Rule r : _allRules) {
      String _name = rule.getName();
      String _name_1 = r.getName();
      boolean _equals = _name.equals(_name_1);
      if (_equals) {
        boolean _equals_1 = r.equals(rule);
        boolean _not = (!_equals_1);
        if (_not) {
          ICompositeNode _findActualNodeFor = NodeModelUtils.findActualNodeFor(r);
          int _startLine = _findActualNodeFor.getStartLine();
          String _plus = (EbnfValidator.nonUniqueNameDescription + Integer.valueOf(_startLine));
          this.error(_plus, 
            EbnfPackage.Literals.RULE__NAME);
        }
      }
    }
  }
  
  /**
   * Checks if a Rule, except for the #1 is not referenced, e.g.:
   * a::= b
   * b::="foo"
   * c ::= "bar"
   */
  @Check
  public void checkUnusedRule(final Rule rule) {
    List<RuleReference> references = EbnfAnalysisUtils.findReferences(rule);
    boolean _and = false;
    int _size = references.size();
    boolean _equals = (_size == 0);
    if (!_equals) {
      _and = false;
    } else {
      int _rulenumber = rule.getRulenumber();
      boolean _notEquals = (_rulenumber != 1);
      _and = _notEquals;
    }
    if (_and) {
      this.warning(EbnfValidator.unusedRuleDescription, EbnfPackage.Literals.RULE__NAME);
    }
  }
  
  /**
   * Checks if a rule got two equal alternatives, e.g.:
   * a ::= b | "foo" | b
   * (Problem:ignores whitespaces in literals)
   */
  @Check
  public void checkEqualAlternative(final Rule rule) {
    DefinitionList definitionList = rule.getDefinitionList();
    List<SingleDefinition> singleDefinitions = definitionList.getSingleDefinition();
    for (final SingleDefinition sDef1 : singleDefinitions) {
      for (final SingleDefinition sDef2 : singleDefinitions) {
        boolean _equals = sDef1.equals(sDef2);
        boolean _not = (!_equals);
        if (_not) {
          ICompositeNode _findActualNodeFor = NodeModelUtils.findActualNodeFor(sDef1);
          String _text = _findActualNodeFor.getText();
          String _trim = _text.trim();
          String d1 = _trim.replaceAll("[ \t\n\r]", "");
          ICompositeNode _findActualNodeFor_1 = NodeModelUtils.findActualNodeFor(sDef2);
          String _text_1 = _findActualNodeFor_1.getText();
          String _trim_1 = _text_1.trim();
          String d2 = _trim_1.replaceAll("[ \t\n\r]", "");
          boolean _equals_1 = d1.equals(d2);
          if (_equals_1) {
            this.warning(EbnfValidator.equalAlternativeDescription, EbnfPackage.Literals.RULE__NAME, EbnfValidator.equalAlternativeDescription);
          }
        }
      }
    }
  }
  
  /**
   * Checks if a rule gets just passed through, e.g.:
   * a ::= b | "literal"
   * b ::= c
   * c ::= "foo.bar"
   */
  @Check
  public void checkPassthroughRule(final Rule rule) {
    List<RuleReference> references = EbnfAnalysisUtils.findReferences(rule);
    boolean _isPassthroughRule = EbnfAnalysisUtils.isPassthroughRule(rule);
    if (_isPassthroughRule) {
      int _size = references.size();
      boolean _equals = (_size == 0);
      if (_equals) {
        this.warning(EbnfValidator.unreferencedPassthroughRuleDescription, EbnfPackage.Literals.RULE__NAME);
      } else {
        this.warning(EbnfValidator.passthroughRuleDescription, EbnfPackage.Literals.RULE__NAME, EbnfValidator.passthroughRuleDescription);
      }
    }
  }
}
