package com.orientechnologies.orient.core.sql.executor;

import com.orientechnologies.orient.core.command.OCommandContext;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.exception.OCommandExecutionException;
import com.orientechnologies.orient.core.sql.parser.*;

import java.util.*;

/**
 * Created by luigidellaquila on 14/10/16.
 */
public class MatchMultiEdgeTraverser extends MatchEdgeTraverser {
  public MatchMultiEdgeTraverser(OResult lastUpstreamRecord, EdgeTraversal edge) {
    super(lastUpstreamRecord, edge);
  }

  protected Iterable<OResultInternal> traversePatternEdge(OIdentifiable startingPoint, OCommandContext iCommandContext) {

    Iterable possibleResults = null;
    //    if (this.edge.edge.item.getFilter() != null) {
    //      String alias = this.edge.edge.item.getFilter().getAlias();
    //      Object matchedNodes = iCommandContext.getVariable(MatchPrefetchStep.PREFETCHED_MATCH_ALIAS_PREFIX + alias);
    //      if (matchedNodes != null) {
    //        if (matchedNodes instanceof Iterable) {
    //          possibleResults = (Iterable) matchedNodes;
    //        } else {
    //          possibleResults = Collections.singleton(matchedNodes);
    //        }
    //      }
    //    }

    OMultiMatchPathItem item = (OMultiMatchPathItem) this.item;
    List<OResultInternal> result = new ArrayList<>();

    List<Object> nextStep = new ArrayList<>();
    nextStep.add(startingPoint);

    Object oldCurrent = iCommandContext.getVariable("$current");
    for (OMatchPathItem sub : item.getItems()) {
      List<OResultInternal> rightSide = new ArrayList<>();
      for (Object o : nextStep) {
        OWhereClause whileCond = sub.getFilter() == null ? null : sub.getFilter().getWhileCondition();

        OMethodCall method = sub.getMethod();
        if (sub instanceof OMatchPathItemFirst) {
          method = ((OMatchPathItemFirst) sub).getFunction().toMethod();
        }

        if (whileCond != null) {
          Object current = o;
          if (current instanceof OResult) {
            current = ((OResult) current).getElement().orElse(null);
          }
          MatchEdgeTraverser subtraverser = new MatchEdgeTraverser(null, sub);
          subtraverser.executeTraversal(iCommandContext, sub, (OIdentifiable) current, 0, null).forEach(x -> rightSide.add(x));

        } else {
          iCommandContext.setVariable("$current", o);
          Object nextSteps = method.execute(o, possibleResults, iCommandContext);
          if (nextSteps instanceof Collection) {
            ((Collection) nextSteps).stream().map(x -> toOResultInternal(x)).filter(Objects::nonNull)
                .forEach(i -> rightSide.add((OResultInternal) i));
          } else if (nextSteps instanceof OIdentifiable) {
            rightSide.add(new OResultInternal((OIdentifiable) nextSteps));
          } else if (nextSteps instanceof OResultInternal) {
            rightSide.add((OResultInternal) nextSteps);
          } else if (nextSteps instanceof Iterable) {
            for (Object step : (Iterable) nextSteps) {
              OResultInternal converted = toOResultInternal(step);
              if (converted != null) {
                rightSide.add(converted);
              }
            }
          } else if (nextSteps instanceof Iterator) {
            Iterator iterator = (Iterator) nextSteps;
            while (iterator.hasNext()) {
              OResultInternal converted = toOResultInternal(iterator.next());
              if (converted != null) {
                rightSide.add(converted);
              }
            }
          }
        }
      }
      nextStep = (List) rightSide;
      result = rightSide;
    }

    iCommandContext.setVariable("$current", oldCurrent);
    //    return (qR instanceof Iterable) ? (Iterable) qR : Collections.singleton((OIdentifiable) qR);
    return (Iterable) result;
  }

  private OResultInternal toOResultInternal(Object x) {
    if (x instanceof OResultInternal) {
      return (OResultInternal) x;
    }
    if (x instanceof OIdentifiable) {
      return new OResultInternal((OIdentifiable) x);
    }
    throw new OCommandExecutionException("Cannot execute traversal on " + x);
  }
}

@@ -2669,16 +2669,118 @@
 onNull)%0A
+                .filter(x -%3E matchesCondition((OResultInternal) x, sub.getFilter(), iCommandContext))%0A
         
@@ -2909,30 +2909,38 @@
         
-rightSide.add(
+OResultInternal res = 
 new ORes
@@ -2981,72 +2981,295 @@
 eps)
-);%0A          %7D else if (nextSteps instanceof OResultInternal) %7B%0A
+;%0A            if (matchesCondition(res, sub.getFilter(), iCommandContext)) %7B%0A              rightSide.add(res);%0A            %7D%0A          %7D else if (nextSteps instanceof OResultInternal) %7B%0A            if (matchesCondition((OResultInternal) nextSteps, sub.getFilter(), iCommandContext)) %7B%0A  
     
@@ -3312,32 +3312,46 @@
 al) nextSteps);%0A
+            %7D%0A
           %7D else
@@ -3537,32 +3537,97 @@
 onverted != null
+ && matchesCondition(converted, sub.getFilter(), iCommandContext)
 ) %7B%0A            
@@ -3940,32 +3940,97 @@
 onverted != null
+ && matchesCondition(converted, sub.getFilter(), iCommandContext)
 ) %7B%0A            
@@ -4378,24 +4378,314 @@
 esult;%0A  %7D%0A%0A
+  private boolean matchesCondition(OResultInternal x, OMatchFilter filter, OCommandContext ctx) %7B%0A    if (filter == null) %7B%0A      return true;%0A    %7D%0A    OWhereClause where = filter.getFilter();%0A    if (where == null) %7B%0A      return true;%0A    %7D%0A    return where.matchesFilters(x, ctx);%0A  %7D%0A%0A
   private OR

Fix nested MATCH path evaluations with WHERE conditions