/*
 * © Copyright 2015 -  SourceClear Inc
 */

package com.sourceclear.gramtest;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 *
 */
public class GeneratorVisitor extends bnfBaseVisitor {

  ///////////////////////////// Class Attributes \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

  ////////////////////////////// Class Methods \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

  //////////////////////////////// Attributes \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
  
  private final Map<String,bnfParser.RhsContext> productionsMap = new HashMap<>();
  private int max = 100;
  private List<String> tests = new LinkedList<>();
          
  /////////////////////////////// Constructors \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
  
  public GeneratorVisitor() {
  }
  
  public GeneratorVisitor(int max) {
    this.max = max;
  }

  ////////////////////////////////// Methods \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
  //------------------------ Implements:
  //------------------------ Overrides:
  
  @Override
  public Object visitId(bnfParser.IdContext ctx) {
    return super.visitId(ctx);
  }

  @Override
  public String visitText(bnfParser.TextContext ctx) {
    return ctx.getText();
  }

  @Override
  public String visitCaptext(bnfParser.CaptextContext ctx) {
    return ctx.getText();
  }

  @Override
  public Object visitOneormore(bnfParser.OneormoreContext ctx) {
    return super.visitOneormore(ctx); //To change body of generated methods, choose Tools | Templates.
  }

  @Override
  public Object visitZeroormore(bnfParser.ZeroormoreContext ctx) {
    return super.visitZeroormore(ctx); //To change body of generated methods, choose Tools | Templates.
  }

  @Override
  public Object visitOptional(bnfParser.OptionalContext ctx) {
    return super.visitOptional(ctx); //To change body of generated methods, choose Tools | Templates.
  }

  @Override
  public List<String> visitElement(bnfParser.ElementContext ctx) {
    List<String> result = new LinkedList<>();
    if(ctx.zeroormore() != null) {
      result = visitAlternatives(ctx.zeroormore().alternatives()); // one time
      result.add(""); // zero time
    }
    else if(ctx.oneormore() != null) {
      result = visitAlternatives(ctx.oneormore().alternatives()); // one time
      List<String> twoLs = combineTwoLists(result,result); //two times
      result.addAll(twoLs);
    }
    else if(ctx.optional() != null) {
      //currently similar to zero of more times
      result = visitAlternatives(ctx.zeroormore().alternatives()); // one time
      result.add(""); // zero time
    }
    else if(ctx.captext() != null) {
      result.add(visitCaptext(ctx.captext()));
    }
    else if(ctx.text() != null) {
      result.add(visitText(ctx.text()));
    }
    else {
      return visitRhs(productionsMap.get(ctx.id().getText()));
    }
    return result;
  }

  @Override
  public List<String> visitAlternative(bnfParser.AlternativeContext ctx) {
    List<List<String>> comStr = new LinkedList<>();
    for(bnfParser.ElementContext ec1 : ctx.element()) {
     comStr.add(visitElement(ec1));
    }
    List<String> emptyStr = new LinkedList<>();
    emptyStr.add("");
    return generateAllStrings(comStr, emptyStr);
  }

  @Override
  public List<String> visitAlternatives(bnfParser.AlternativesContext ctx) {
    List<String> altStrs = new LinkedList<>();
    for(bnfParser.AlternativeContext ac : ctx.alternative()) {
      altStrs.addAll(visitAlternative(ac));
    }
    return altStrs;
  }

  @Override
  public List<String> visitRhs(bnfParser.RhsContext ctx) {
    return visitAlternatives(ctx.alternatives()); 
  }

  @Override
  public Object visitLhs(bnfParser.LhsContext ctx) {
    return super.visitLhs(ctx); //To change body of generated methods, choose Tools | Templates.
  }

  @Override
  public List<String> visitRule_(bnfParser.Rule_Context ctx) {
    return visitRhs(ctx.rhs());
  }

  @Override
  public Object visitRulelist(bnfParser.RulelistContext ctx) {
    List<String> sentences = new LinkedList();
    for(bnfParser.Rule_Context rc : ctx.rule_()) {
      productionsMap.put(rc.lhs().id().getText(), rc.rhs());
    }
    // Should not visit all the rules, but start from the top one
    /*
    for(bnfParser.Rule_Context rc : ctx.rule_()) {
      sentences.addAll(visitRule_(rc));
    }
    */
    sentences.addAll(visitRule_(ctx.rule_(0)));
    tests = sentences;
    return super.visitRulelist(ctx);
  }
  
  //---------------------------- Abstract Methods -----------------------------

  //---------------------------- Utility Methods ------------------------------
  
  private List<String> generateAllStrings(List<List<String>> strList, List<String> result) {
    if(strList.size() > 0) {
      List<String> newResult = combineTwoLists(result,strList.remove(0));
      if(newResult.isEmpty()) return result;
      else return generateAllStrings(strList, newResult);
    }
    return result;
  }
  
  private List<String> combineTwoLists(List<String> preList, List<String> postList) {
    List<String> combList = new LinkedList<>();
    for(String s1 : preList) {
      for(String s2 : postList) {
        if(combList.size() < max)
          combList.add(s1+s2);
      }
    }
    return combList;
  }

  //---------------------------- Property Methods -----------------------------
  
  public List<String> getTests() {
    return tests;
  }
  
}

@@ -592,16 +592,19 @@
  max
+Num
  = 100;%0A
   pr
@@ -599,16 +599,44 @@
  = 100;%0A
+  private int maxDepth = 4;%0A
   privat
@@ -674,24 +674,91 @@
 edList%3C%3E();%0A
+  private int currentDepth = 0;%0A  private String currentProd = %22%22;%0A
           %0A 
@@ -913,30 +913,61 @@
  max
-) %7B%0A    this.max = max
+, int depth) %7B%0A    maxNum = max;%0A    maxDepth = depth
 ;%0A  
@@ -1553,72 +1553,8 @@
 x); 
-//To change body of generated methods, choose Tools %7C Templates.
 %0A  %7D
@@ -1677,72 +1677,8 @@
 x); 
-//To change body of generated methods, choose Tools %7C Templates.
 %0A  %7D
@@ -1795,72 +1795,8 @@
 x); 
-//To change body of generated methods, choose Tools %7C Templates.
 %0A  %7D
@@ -2697,62 +2697,294 @@
     
-return visitRhs(productionsMap.get(ctx.id().getText())
+String lhs = ctx.id().getText();%0A      if(currentProd.equals(lhs)) %0A        currentDepth++;%0A      else %7B%0A        currentDepth = 0;%0A        currentProd = lhs;%0A      %7D%0A      if (currentDepth %3C maxDepth)%0A        return visitRhs(productionsMap.get(lhs));%0A      else return new LinkedList%3C%3E(
 );%0A 
@@ -3847,100 +3847,73 @@
     
-return super.visitLhs(ctx); //To change body of generated methods, choose Tools %7C Templates.
+currentProd = ctx.id().getText();%0A    return super.visitLhs(ctx);
 %0A  %7D
@@ -5284,16 +5284,19 @@
 () %3C max
+Num
 )%0A      

add support for depth