/*
 * Copyright 2019 Immutables Authors and Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.immutables.criteria.geode;

import com.google.common.base.Preconditions;
import org.immutables.criteria.expression.Call;
import org.immutables.criteria.expression.Constant;
import org.immutables.criteria.expression.Expression;
import org.immutables.criteria.expression.ExpressionVisitor;
import org.immutables.criteria.expression.Operator;
import org.immutables.criteria.expression.Operators;
import org.immutables.criteria.expression.Path;
import org.immutables.criteria.expression.Visitors;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

class GeodeQueryVisitor implements ExpressionVisitor<String> {

  GeodeQueryVisitor() {
  }

  @Override
  public String visit(Call call) {
    final Operator op = call.operator();
    final List<Expression> args = call.arguments();

    if (op == Operators.EQUAL || op == Operators.NOT_EQUAL) {
      Preconditions.checkArgument(args.size() == 2, "Size should be 2 for %s but was %s", op, args.size());

      final Path path = Visitors.maybePath(args.get(0)).orElseThrow(() -> new IllegalArgumentException("Expected path. Got : " + args.get(0)));
      final Constant constant = Visitors.maybeConstant(args.get(1)).orElseThrow(() -> new IllegalArgumentException("Expected constant. Got " + args.get(1)));
      return String.format("%s = %s", path.toStringPath(), toString(constant.value()));
    }

    if (op == Operators.AND || op == Operators.OR) {
      Preconditions.checkArgument(!args.isEmpty(), "Size should be >=1 for %s but was %s", op, args.size());

      return args.stream().map(a -> a.accept(this)).collect(Collectors.joining(op.name()));
    }

    if (op == Operators.IN || op == Operators.NOT_IN) {
      Preconditions.checkArgument(args.size() == 2, "Size should be 2 for %s but was %s", op, args.size());
      final Path field = Visitors.maybePath(args.get(0)).orElseThrow(() -> new IllegalArgumentException("Expected path. Got : " + args.get(0)));
      @SuppressWarnings("unchecked")
      final Iterable<Object> values = (Iterable<Object>) Visitors.maybeConstant(args.get(1))
              .orElseThrow(() -> new IllegalArgumentException("Expected constant. Got " + args.get(1))).value();


      String valuesAsString = StreamSupport.stream(values.spliterator(), false)
              .map(GeodeQueryVisitor::toString).collect(Collectors.joining(", "));

      String query = String.format("%s in SET(%s)", field.toStringPath(), valuesAsString);

      if (op == Operators.NOT_IN) {
        query = "NOT " + query;
      }

      return query;
    }

    throw new UnsupportedOperationException("Don't know how to handle " + call);
  }

  @Override
  public String visit(Constant constant) {
    throw new UnsupportedOperationException();
  }

  @Override
  public String visit(Path path) {
    throw new UnsupportedOperationException();
  }

  private static String toString(Object value) {
    if (value == null) {
      return "null";
    } else if (value instanceof String) {
      return "'" + value + "'";
    } else {
      return Objects.toString(value);
    }
  }

}

@@ -695,16 +695,85 @@
 itions;%0A
+import org.immutables.criteria.expression.AbstractExpressionVisitor;%0A
 import o
@@ -922,69 +922,8 @@
 on;%0A
-import org.immutables.criteria.expression.ExpressionVisitor;%0A
 impo
@@ -1277,19 +1277,24 @@
 tor 
-implement
+extend
 s 
+Abstract
 Expr
@@ -1342,16 +1342,80 @@
 tor() %7B%0A
+    super(e -%3E %7B throw new UnsupportedOperationException(); %7D);%0A
   %7D%0A%0A  @
@@ -1748,37 +1748,34 @@
 path = Visitors.
-maybe
+to
 Path(args.get(0)
@@ -1779,95 +1779,8 @@
 (0))
-.orElseThrow(() -%3E new IllegalArgumentException(%22Expected path. Got : %22 + args.get(0)))
 ;%0A  
@@ -1810,37 +1810,34 @@
 tant = Visitors.
-maybe
+to
 Constant(args.ge
@@ -1845,97 +1845,8 @@
 (1))
-.orElseThrow(() -%3E new IllegalArgumentException(%22Expected constant. Got %22 + args.get(1)))
 ;%0A  
@@ -2398,21 +2398,18 @@
 isitors.
-maybe
+to
 Path(arg
@@ -2421,95 +2421,8 @@
 (0))
-.orElseThrow(() -%3E new IllegalArgumentException(%22Expected path. Got : %22 + args.get(0)))
 ;%0A  
@@ -2526,125 +2526,19 @@
 ors.
-maybeConstant(args.get(1))%0A              .orElseThrow(() -%3E new IllegalArgumentException(%22Expected constant. Got %22 + 
+toConstant(
 args
@@ -2545,17 +2545,16 @@
 .get(1))
-)
 .value()
@@ -2556,23 +2556,28 @@
 lue();%0A%0A
-%0A
       
+final 
 String v
@@ -2732,16 +2732,22 @@
 %0A%0A      
+final 
 String q
@@ -2826,28 +2826,31 @@
 ng);%0A%0A      
-if (
+return 
 op == Operat
@@ -2863,65 +2863,27 @@
 T_IN
-) %7B%0A        query = %22NOT %22 + query;%0A      %7D%0A%0A      return
+ ? %22NOT %22 + query :
  que
@@ -2983,214 +2983,8 @@
  %7D%0A%0A
-  @Override%0A  public String visit(Constant constant) %7B%0A    throw new UnsupportedOperationException();%0A  %7D%0A%0A  @Override%0A  public String visit(Path path) %7B%0A    throw new UnsupportedOperationException();%0A  %7D%0A%0A
   pr
@@ -3106,22 +3106,28 @@
 tanceof 
-String
+CharSequence
 ) %7B%0A    

Simplify GeodeQueryVisitor