package com.sap.olingo.jpa.processor.cb.impl;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import javax.annotation.Nonnull;

import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CollectionJoin;
import jakarta.persistence.criteria.CompoundSelection;
import jakarta.persistence.criteria.CriteriaDelete;
import jakarta.persistence.criteria.CriteriaUpdate;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.ListJoin;
import jakarta.persistence.criteria.MapJoin;
import jakarta.persistence.criteria.Order;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Selection;
import jakarta.persistence.criteria.SetJoin;
import jakarta.persistence.criteria.Subquery;

import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAServiceDocument;
import com.sap.olingo.jpa.processor.cb.ProcessorCriteriaBuilder;
import com.sap.olingo.jpa.processor.cb.ProcessorCriteriaQuery;
import com.sap.olingo.jpa.processor.cb.ProcessorSqlPatternProvider;
import com.sap.olingo.jpa.processor.cb.exceptions.NotImplementedException;
import com.sap.olingo.jpa.processor.cb.impl.ExpressionImpl.ParameterExpression;
import com.sap.olingo.jpa.processor.cb.impl.PredicateImpl.BinaryExpressionPredicate.Operation;
import com.sap.olingo.jpa.processor.cb.joiner.SqlConvertible;

class CriteriaBuilderImpl implements ProcessorCriteriaBuilder { // NOSONAR

  private final JPAServiceDocument sd;
  private ParameterBuffer parameter;
  private final ProcessorSqlPatternProvider sqlPattern;

  CriteriaBuilderImpl(final JPAServiceDocument sd, final ProcessorSqlPatternProvider sqlPattern) {
    this.sd = sd;
    this.parameter = new ParameterBuffer();
    this.sqlPattern = sqlPattern;
  }

  @Override
  public ParameterBuffer getParameterBuffer() {
    return parameter;
  }

  @Override
  public void resetParameterBuffer() {
    this.parameter = new ParameterBuffer();
  }

  /**
   * Create an expression that returns the absolute value
   * of its argument.
   * @param x expression
   * @return absolute value
   */
  @Override
  public <N extends Number> Expression<N> abs(@Nonnull final Expression<N> expression) {
    throw new NotImplementedException();
  }

  /**
   * Create an all expression over the subquery results.
   * @param subquery subquery
   * @return all expression
   */
  @Override
  public <Y> Expression<Y> all(@Nonnull final Subquery<Y> subquery) {
    return new ExpressionImpl.SubQuery<>(subquery, SqlSubQuery.ALL);
  }

  @Override
  public Predicate and(final Expression<Boolean> x, final Expression<Boolean> y) {
    return new PredicateImpl.AndPredicate(x, y);
  }

  @Override
  public Predicate and(final Predicate... restrictions) {
    return PredicateImpl.and(restrictions);
  }

  /**
   * Create an any expression over the subquery results.
   * This expression is equivalent to a <code>some</code> expression.
   * @param subquery subquery
   * @return any expression
   */
  @Override
  public <Y> Expression<Y> any(@Nonnull final Subquery<Y> subquery) {
    return new ExpressionImpl.SubQuery<>(subquery, SqlSubQuery.ANY);
  }

  /**
   * Create an array-valued selection item.
   * @param selections selection items
   * @return array-valued compound selection
   * @throws IllegalArgumentException if an argument is a
   * tuple- or array-valued selection item
   */
  @Override
  public CompoundSelection<Object[]> array(@Nonnull final Selection<?>... selections) {
    throw new NotImplementedException();
  }

  /**
   * Create an ordering by the ascending value of the expression.
   * @param x expression used to define the ordering
   * @return ascending ordering corresponding to the expression
   */
  @Override
  public Order asc(@Nonnull final Expression<?> x) {
    return new OrderImpl(true, Objects.requireNonNull((SqlConvertible) x));
  }

  /**
   * Create an aggregate expression applying the avg operation.
   * @param x expression representing input value to avg operation
   * @return avg expression
   */
  @Override
  public <N extends Number> Expression<Double> avg(@Nonnull final Expression<N> x) {
    throw new NotImplementedException();
  }

  /**
   * Create a predicate for testing whether the first argument is
   * between the second and third arguments in value.
   * @param v expression
   * @param x expression
   * @param y expression
   * @return between predicate
   */
  @Override
  public <Y extends Comparable<? super Y>> Predicate between(@Nonnull final Expression<? extends Y> v,
      @Nonnull final Expression<? extends Y> x, @Nonnull final Expression<? extends Y> y) {
    return new PredicateImpl.BetweenExpressionPredicate((ExpressionImpl<?>) v, x, y);
  }

  /**
   * Create a predicate for testing whether the first argument is
   * between the second and third arguments in value.
   * @param v expression
   * @param x value
   * @param y value
   * @return between predicate
   */
  @Override
  public <Y extends Comparable<? super Y>> Predicate between(@Nonnull final Expression<? extends Y> v,
      @Nonnull final Y x, @Nonnull final Y y) {
    return between(v, literal(x, v), literal(y, v));
  }

  /**
   * Create a coalesce expression.
   * @return coalesce expression
   */
  @Override
  public <T> Coalesce<T> coalesce() {
    return new ExpressionImpl.CoalesceExpression<>();
  }

  /**
   * Create an expression that returns null if all its arguments
   * evaluate to null, and the value of the first non-null argument
   * otherwise.
   * @param x expression
   * @param y expression
   * @return coalesce expression
   */
  @Override
  public <Y> Expression<Y> coalesce(@Nonnull final Expression<? extends Y> x,
      @Nonnull final Expression<? extends Y> y) {
    return new ExpressionImpl.CoalesceExpression<Y>().value(x).value(y);
  }

  /**
   * Create an expression that returns null if all its arguments
   * evaluate to null, and the value of the first non-null argument
   * otherwise.
   * @param x expression
   * @param y value
   * @return coalesce expression
   */
  @Override
  public <Y> Expression<Y> coalesce(@Nonnull final Expression<? extends Y> expression, @Nonnull final Y y) {
    return new ExpressionImpl.CoalesceExpression<Y>().value(expression).value(literal(y));
  }

  /**
   * Create an expression for string concatenation.
   * @param x string expression
   * @param second string expression
   * @return expression corresponding to concatenation
   */
  @Override
  public Expression<String> concat(@Nonnull final Expression<String> first, @Nonnull final Expression<String> second) {
    return new ExpressionImpl.ConcatExpression(first, second, sqlPattern);
  }

  /**
   * Create an expression for string concatenation.
   * @param x string expression
   * @param value string
   * @return expression corresponding to concatenation
   */
  @Override
  public Expression<String> concat(@Nonnull final Expression<String> expression, @Nonnull final String value) {
    return new ExpressionImpl.ConcatExpression(expression, literal(value), sqlPattern);
  }

  /**
   * Create an expression for string concatenation.
   * @param x string
   * @param expression string expression
   * @return expression corresponding to concatenation
   */
  @Override
  public Expression<String> concat(@Nonnull final String value, @Nonnull final Expression<String> expression) {
    return new ExpressionImpl.ConcatExpression(literal(value), expression, sqlPattern);
  }

  /**
   * Create a conjunction (with zero conjuncts).
   * A conjunction with zero conjuncts is true.
   * @return and predicate
   */
  @Override
  public Predicate conjunction() {
    throw new NotImplementedException();
  }

  /**
   * Create a selection item corresponding to a constructor.
   * This method is used to specify a constructor that will be
   * applied to the results of the query execution. If the
   * constructor is for an entity class, the resulting entities
   * will be in the new state after the query is executed.
   * @param resultClass class whose instance is to be constructed
   * @param selections arguments to the constructor
   * @return compound selection item
   * @throws IllegalArgumentException if an argument is a
   * tuple- or array-valued selection item
   */
  @Override
  public <Y> CompoundSelection<Y> construct(@Nonnull final Class<Y> resultClass,
      @Nonnull final Selection<?>... selections) {
    throw new NotImplementedException();
  }

  /**
   * Create an aggregate expression applying the count operation.
   * @param x expression representing input value to count
   * operation
   * @return count expression
   */
  @Override
  public Expression<Long> count(@Nonnull final Expression<?> x) {
    return new ExpressionImpl.AggregationExpression<>(SqlAggregation.COUNT, x);
  }

  /**
   * Create an aggregate expression applying the count distinct
   * operation.
   * @param x expression representing input value to count distinct operation
   * @return count distinct expression
   */
  @Override
  public Expression<Long> countDistinct(@Nonnull final Expression<?> x) {
    return count(new ExpressionImpl.DistinctExpression<>(x));
  }

  @Override
  public <T> CriteriaDelete<T> createCriteriaDelete(final Class<T> targetEntity) {
    throw new NotImplementedException();
  }

  @Override
  public <T> CriteriaUpdate<T> createCriteriaUpdate(final Class<T> targetEntity) {
    return new CriteriaUpdateImpl<>(sd, this, parameter, targetEntity, sqlPattern);
  }

  @Override
  public ProcessorCriteriaQuery<Object> createQuery() {
    return new CriteriaQueryImpl<>(Object.class, sd, this, sqlPattern);
  }

  @Override
  public <T> ProcessorCriteriaQuery<T> createQuery(final Class<T> resultClass) {
    return new CriteriaQueryImpl<>(resultClass, sd, this, sqlPattern);
  }

  @Override
  public ProcessorCriteriaQuery<Tuple> createTupleQuery() {
    return new CriteriaQueryImpl<>(Tuple.class, sd, this, sqlPattern);
  }

  @Override
  public Expression<Date> currentDate() {
    throw new NotImplementedException();
  }

  @Override
  public Expression<Time> currentTime() {
    throw new NotImplementedException();
  }

  @Override
  public Expression<Timestamp> currentTimestamp() {
    return new ExpressionImpl.TimeExpression<>(SqlTimeFunctions.TIMESTAMP);
  }

  /**
   * Create an ordering by the descending value of the expression.
   * @param x expression used to define the ordering
   * @return descending ordering corresponding to the expression
   */
  @Override
  public Order desc(@Nonnull final Expression<?> x) {
    return new OrderImpl(false, Objects.requireNonNull((SqlConvertible) x));
  }

  /**
   * Create an expression that returns the difference
   * between its arguments.
   * @param x expression
   * @param y expression
   * @return difference
   */
  @Override
  public <N extends Number> Expression<N> diff(@Nonnull final Expression<? extends N> x,
      @Nonnull final Expression<? extends N> y) {
    return new ExpressionImpl.ArithmeticExpression<>(x, y, SqlArithmetic.DIFF);
  }

  /**
   * Create an expression that returns the difference
   * between its arguments.
   * @param x expression
   * @param y value
   * @return difference
   */
  @Override
  public <N extends Number> Expression<N> diff(@Nonnull final Expression<? extends N> x, @Nonnull final N y) {
    return new ExpressionImpl.ArithmeticExpression<>(x, literal(y), SqlArithmetic.DIFF);
  }

  /**
   * Create an expression that returns the difference
   * between its arguments.
   * @param x value
   * @param y expression
   * @return difference
   */
  @Override
  public <N extends Number> Expression<N> diff(@Nonnull final N x, @Nonnull final Expression<? extends N> y) {
    return new ExpressionImpl.ArithmeticExpression<>(literal(x), y, SqlArithmetic.DIFF);
  }

  /**
   * Create a disjunction (with zero disjuncts).
   * A disjunction with zero disjuncts is false.
   * @return or predicate
   */
  @Override
  public Predicate disjunction() {
    throw new NotImplementedException();
  }

  /**
   * Create a predicate for testing the arguments for equality.
   * @param x expression
   * @param y expression
   * @return equality predicate
   */
  @Override
  public Predicate equal(@Nonnull final Expression<?> x, @Nonnull final Expression<?> y) {// NOSONAR
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.EQ);
  }

  /**
   * Create a predicate for testing the arguments for equality.
   * @param x expression
   * @param y object
   * @return equality predicate
   */
  @Override
  public Predicate equal(@Nonnull final Expression<?> x, final Object y) { // NOSONAR
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.EQ);
  }

  /**
   * Create a predicate testing the existence of a subquery result.
   * @param subquery subquery whose result is to be tested
   * @return exists predicate
   */
  @Override
  public Predicate exists(@Nonnull final Subquery<?> subquery) {
    return new PredicateImpl.SubQuery(subquery, SqlSubQuery.EXISTS);
  }

  /**
   * Create an expression for the execution of a database
   * function.
   * @param name function name
   * @param type expected result type
   * @param args function arguments
   * @return expression
   */
  @SuppressWarnings("unchecked")
  @Override
  public <T> Expression<T> function(@Nonnull final String name, @Nonnull final Class<T> type,
      final Expression<?>... args) {
    final List<Expression<Object>> parameters = args == null ? Collections.emptyList() : Arrays.asList(args).stream()
        .map(p -> ((Expression<Object>) p)).toList();
    return new ExpressionImpl.FunctionExpression<>(name, type, parameters);
  }

  /**
   * Create a predicate for testing whether the first argument is
   * greater than or equal to the second.
   * @param x expression
   * @param y expression
   * @return greater-than-or-equal predicate
   */
  @Override
  public Predicate ge(@Nonnull final Expression<? extends Number> x, @Nonnull final Expression<? extends Number> y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.GE);
  }

  /**
   * Create a predicate for testing whether the first argument is
   * greater than or equal to the second.
   * @param x expression
   * @param y expression
   * @return greater-than-or-equal predicate
   */
  @Override
  public Predicate ge(@Nonnull final Expression<? extends Number> x, @Nonnull final Number y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.GE);
  }

  /**
   * Create a predicate for testing whether the first argument is
   * greater than the second.
   * @param x expression
   * @param y expression
   * @return greater-than predicate
   */
  @Override
  public <Y extends Comparable<? super Y>> Predicate greaterThan(@Nonnull final Expression<? extends Y> x,
      @Nonnull final Expression<? extends Y> y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.GT);
  }

  @Override
  public <Y extends Comparable<? super Y>> Predicate greaterThan(@Nonnull final Expression<? extends Y> x,
      @Nonnull final Y y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.GT);
  }

  /**
   * Create a predicate for testing whether the first argument is
   * greater than or equal to the second.
   * @param x expression
   * @param y expression
   * @return greater-than-or-equal predicate
   */
  @Override
  public <Y extends Comparable<? super Y>> Predicate greaterThanOrEqualTo(@Nonnull final Expression<? extends Y> x,
      @Nonnull final Expression<? extends Y> y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.GE);
  }

  /**
   * Create a predicate for testing whether the first argument is
   * greater than or equal to the second.
   * @param x expression
   * @param y value
   * @return greater-than-or-equal predicate
   */
  @Override
  public <Y extends Comparable<? super Y>> Predicate greaterThanOrEqualTo(@Nonnull final Expression<? extends Y> x,
      @Nonnull final Y y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.GE);
  }

  /**
   * Create an aggregate expression for finding the greatest of
   * the values (strings, dates, etc).
   * @param x expression representing input value to greatest
   * operation
   * @return greatest expression
   */
  @Override
  public <X extends Comparable<? super X>> Expression<X> greatest(@Nonnull final Expression<X> x) {
    throw new NotImplementedException();
  }

  /**
   * Create a predicate for testing whether the first argument is
   * greater than the second.
   * @param x expression
   * @param y expression
   * @return greater-than predicate
   */
  @Override
  public Predicate gt(@Nonnull final Expression<? extends Number> x, @Nonnull final Expression<? extends Number> y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.GT);
  }

  /**
   * Create a predicate for testing whether the first argument is
   * greater than the second.
   * @param x expression
   * @param y value
   * @return greater-than predicate
   */
  @Override
  public Predicate gt(@Nonnull final Expression<? extends Number> x, @Nonnull final Number y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.GT);
  }

  /**
   * Create predicate to test whether given expression
   * is contained in a list of values.
   * @param expression to be tested against list of values
   * @return in predicate
   */
  @Override
  public <T> In<T> in(final Expression<? extends T> expression) {
    return new PredicateImpl.In<>((Path<? extends T>) expression, parameter);
  }

  /**
   * Create predicate to test whether given expression
   * is contained in a list of values.
   * @param paths to be tested against list of values
   * @return in predicate
   */
  @Override
  public In<List<Comparable<?>>> in(final List<Path<Comparable<?>>> paths,
      final Subquery<List<Comparable<?>>> subquery) {
    return new PredicateImpl.In<List<Comparable<?>>>(paths, parameter).value(subquery);
  }

  /**
   * Create predicate to test whether given expression
   * is contained in a list of values.
   * @param path to be tested against list of values
   * @return in predicate
   */
  @Override
  public <T> In<T> in(final Path<?> path) {
    return new PredicateImpl.In<>(path, parameter);
  }

  /**
   * Create a predicate that tests whether a collection is empty.<br>
   * Example: "WHERE employee.projects IS EMPTY"
   * @param collection expression
   * @return is-empty predicate
   */
  @Override
  public <C extends Collection<?>> Predicate isEmpty(@Nonnull final Expression<C> collection) {
    throw new NotImplementedException();
  }

  /**
   * Create a predicate testing for a false value.
   * @param x expression to be tested
   * @return predicate
   */
  @Override
  public Predicate isFalse(@Nonnull final Expression<Boolean> x) {
    throw new NotImplementedException();
  }

  /**
   * Create a predicate that tests whether an element is
   * a member of a collection (property).
   * If the collection is empty, the predicate will be false.
   * @param elem element expression
   * @param collection expression
   * @return is-member predicate
   */
  @Override
  public <E, C extends Collection<E>> Predicate isMember(@Nonnull final E elem,
      @Nonnull final Expression<C> collection) {
    throw new NotImplementedException();
  }

  /**
   * Create a predicate that tests whether an element is
   * a member of a collection (property).
   * If the collection is empty, the predicate will be false.
   * @param elem element
   * @param collection expression
   * @return is-member predicate
   */
  @Override
  public <E, C extends Collection<E>> Predicate isMember(@Nonnull final Expression<E> elem,
      @Nonnull final Expression<C> collection) {
    throw new NotImplementedException();
  }

  /**
   * Create a predicate that tests whether a collection is not empty.<br>
   * Example: WHERE projects.employees IS NOT EMPTY
   * @param collection expression
   * @return is-not-empty predicate
   */
  @Override
  public <C extends Collection<?>> Predicate isNotEmpty(@Nonnull final Expression<C> collection) {
    throw new NotImplementedException();
  }

  /**
   * Create a predicate that tests whether an element is not a member of a collection.
   * If the collection is empty, the predicate will be true.<br>
   * Example:
   * @param elem element
   * @param collection expression
   * @return is-not-member predicate
   */
  @Override
  public <E, C extends Collection<E>> Predicate isNotMember(@Nonnull final E elem,
      @Nonnull final Expression<C> collection) {
    throw new NotImplementedException();
  }

  /**
   * Create a predicate that tests whether an element is
   * not a member of a collection.
   * If the collection is empty, the predicate will be true.
   * @param elem element expression
   * @param collection expression
   * @return is-not-member predicate
   */
  @Override
  public <E, C extends Collection<E>> Predicate isNotMember(@Nonnull final Expression<E> elem,
      @Nonnull final Expression<C> collection) {
    throw new NotImplementedException();
  }

  /**
   * Create a predicate to test whether the expression is not null.
   * @param x expression
   * @return is-not-null predicate
   */
  @Override
  public Predicate isNotNull(@Nonnull final Expression<?> x) {
    return new PredicateImpl.NullPredicate(x, SqlNullCheck.NOT_NULL);
  }

  /**
   * Create a predicate to test whether the expression is null.
   * @param x expression
   * @return is-null predicate
   */
  @Override
  public Predicate isNull(@Nonnull final Expression<?> x) {
    return new PredicateImpl.NullPredicate(x, SqlNullCheck.NULL);
  }

  /**
   * Create a predicate testing for a true value.
   * @param x expression to be tested
   * @return predicate
   */
  @Override
  public Predicate isTrue(final Expression<Boolean> x) {
    throw new NotImplementedException();
  }

  /**
   * Create an expression that returns the keys of a map.
   * @param map map
   * @return set expression
   */
  @Override
  public <K, M extends Map<K, ?>> Expression<Set<K>> keys(@Nonnull final M map) {
    throw new NotImplementedException();
  }

  /**
   * Create a predicate for testing whether the first argument is
   * less than or equal to the second.
   * @param x expression
   * @param y expression
   * @return less-than-or-equal predicate
   */
  @Override
  public Predicate le(@Nonnull final Expression<? extends Number> x, @Nonnull final Expression<? extends Number> y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.LE);
  }

  /**
   * Create a predicate for testing whether the first argument is
   * less than or equal to the second.
   * @param x expression
   * @param y value
   * @return less-than-or-equal predicate
   */
  @Override
  public Predicate le(@Nonnull final Expression<? extends Number> x, @Nonnull final Number y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.LE);
  }

  /**
   * Create an aggregate expression for finding the least of
   * the values (strings, dates, etc).
   * @param x expression representing input value to least
   * operation
   * @return least expression
   */
  @Override
  public <X extends Comparable<? super X>> Expression<X> least(@Nonnull final Expression<X> expression) {
    throw new NotImplementedException();
  }

  /**
   * Create expression to return length of a string.
   * @param x string expression
   * @return length expression
   */
  @Override
  public Expression<Integer> length(@Nonnull final Expression<String> expression) {
    return new ExpressionImpl.LengthExpression(expression, sqlPattern);
  }

  /**
   * Create a predicate for testing whether the first argument is
   * less than the second.
   * @param x expression
   * @param y expression
   * @return less-than predicate
   */
  @Override
  public <Y extends Comparable<? super Y>> Predicate lessThan(@Nonnull final Expression<? extends Y> x,
      @Nonnull final Expression<? extends Y> y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.LT);
  }

  @Override
  public <Y extends Comparable<? super Y>> Predicate lessThan(@Nonnull final Expression<? extends Y> x,
      @Nonnull final Y y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.LT);
  }

  /**
   * Create a predicate for testing whether the first argument is
   * less than or equal to the second.
   * @param x expression
   * @param y expression
   * @return less-than-or-equal predicate
   */
  @Override
  public <Y extends Comparable<? super Y>> Predicate lessThanOrEqualTo(@Nonnull final Expression<? extends Y> x,
      @Nonnull final Expression<? extends Y> y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.LE);
  }

  @Override
  public <Y extends Comparable<? super Y>> Predicate lessThanOrEqualTo(@Nonnull final Expression<? extends Y> x,
      @Nonnull final Y y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.LE);
  }

  /**
   * Create a predicate for testing whether the expression
   * satisfies the given pattern.
   * @param x string expression
   * @param pattern string expression
   * @return like predicate
   */
  @Override
  public Predicate like(@Nonnull final Expression<String> x, @Nonnull final Expression<String> pattern) {
    return new PredicateImpl.LikePredicate(Objects.requireNonNull(x),
        (ParameterExpression<String, ?>) Objects.requireNonNull(pattern));
  }

  /**
   * Create a predicate for testing whether the expression
   * satisfies the given pattern.
   * @param x string expression
   * @param pattern string expression
   * @param escapeChar escape character
   * @return like predicate
   */
  @Override
  public Predicate like(@Nonnull final Expression<String> x, @Nonnull final Expression<String> pattern,
      final char escapeChar) {
    return like(x, pattern, literal(escapeChar));
  }

  /**
   * Create a predicate for testing whether the expression
   * satisfies the given pattern.
   * @param x string expression
   * @param pattern string expression
   * @param escapeChar escape character expression
   * @return like predicate
   */
  @Override
  public Predicate like(@Nonnull final Expression<String> x, @Nonnull final Expression<String> pattern,
      final Expression<Character> escapeChar) {

    return new PredicateImpl.LikePredicate(Objects.requireNonNull(x),
        (ParameterExpression<String, ?>) Objects.requireNonNull(pattern),
        Optional.ofNullable((ParameterExpression<Character, ?>) (escapeChar)));
  }

  /**
   * Create a predicate for testing whether the expression
   * satisfies the given pattern.
   * @param x string expression
   * @param pattern string
   * @return like predicate
   */
  @Override
  public Predicate like(@Nonnull final Expression<String> x, @Nonnull final String pattern) {
    return like(x, literal(pattern, x));
  }

  /**
   * Create a predicate for testing whether the expression
   * satisfies the given pattern.
   * @param x string expression
   * @param pattern string expression
   * @param escapeChar escape character
   * @return like predicate
   */
  @Override
  public Predicate like(@Nonnull final Expression<String> x, @Nonnull final String pattern,
      final char escapeChar) {
    return like(x, literal(pattern, x), literal(escapeChar));
  }

  @Override
  public Predicate like(final Expression<String> x, final String pattern, final Expression<Character> escapeChar) {
    return like(x, literal(pattern, x), escapeChar);
  }

  /**
   * Create an expression for a literal.
   * @param value value represented by the expression
   * @return expression literal
   * @throws IllegalArgumentException if value is null
   */
  @Override
  public <T> Expression<T> literal(@Nonnull final T value) {
    if (value == null) // NOSONAR
      throw new IllegalArgumentException("Literal value must not be null");
    return parameter.addValue(value);
  }

  private <T> Expression<T> literal(@Nonnull final T value, @Nonnull final Expression<?> x) {
    return parameter.addValue(value, x);
  }

  /**
   * Create expression to locate the position of one string
   * within another, returning position of first character
   * if found.
   * The first position in a string is denoted by 1. If the
   * string to be located is not found, 0 is returned.
   * @param expression expression for string to be searched
   * @param pattern expression for string to be located
   * @return expression corresponding to position
   */
  @Override
  public Expression<Integer> locate(@Nonnull final Expression<String> expression,
      @Nonnull final Expression<String> pattern) {
    return new ExpressionImpl.LocateExpression(expression, pattern, null, sqlPattern);
  }

  /**
   * Create expression to locate the position of one string
   * within another, returning position of first character
   * if found.
   * The first position in a string is denoted by 1. If the
   * string to be located is not found, 0 is returned.
   * @param expression expression for string to be searched
   * @param pattern expression for string to be located
   * @param from expression for position at which to start search
   * @return expression corresponding to position
   */
  @Override
  public Expression<Integer> locate(@Nonnull final Expression<String> expression,
      @Nonnull final Expression<String> pattern,
      @Nonnull final Expression<Integer> from) {
    return new ExpressionImpl.LocateExpression(expression, pattern, from, sqlPattern);
  }

  /**
   * Create expression to locate the position of one string
   * within another, returning position of first character
   * if found.
   * The first position in a string is denoted by 1. If the
   * string to be located is not found, 0 is returned.
   * @param expression expression for string to be searched
   * @param pattern string to be located
   * @return expression corresponding to position
   */
  @Override
  public Expression<Integer> locate(@Nonnull final Expression<String> expression, @Nonnull final String pattern) {
    return new ExpressionImpl.LocateExpression(expression, literal(pattern, expression), null, sqlPattern);
  }

  /**
   * Create expression to locate the position of one string
   * within another, returning position of first character
   * if found.
   * The first position in a string is denoted by 1. If the
   * string to be located is not found, 0 is returned.
   * @param expression expression for string to be searched
   * @param pattern string to be located
   * @param from position at which to start search
   * @return expression corresponding to position
   */
  @Override
  public Expression<Integer> locate(@Nonnull final Expression<String> expression, @Nonnull final String pattern,
      final int from) {
    return new ExpressionImpl.LocateExpression(expression, literal(pattern), literal(from), sqlPattern);
  }

  /**
   * Create expression for converting a string to lowercase.
   * @param x string expression
   * @return expression to convert to lowercase
   */
  @Override
  public Expression<String> lower(@Nonnull final Expression<String> x) {
    return new ExpressionImpl.UnaryFunctionalExpression<>(x, SqlStringFunctions.LOWER);
  }

  /**
   * Create a predicate for testing whether the first argument is
   * less than the second.
   * @param x expression
   * @param y expression
   * @return less-than predicate
   */
  @Override
  public Predicate lt(@Nonnull final Expression<? extends Number> x, @Nonnull final Expression<? extends Number> y) {
    return binaryExpression(x, y, PredicateImpl.BinaryExpressionPredicate.Operation.LT);
  }

  /**
   * Create a predicate for testing whether the first argument is
   * less than the second.
   * @param x expression
   * @param y value
   * @return less-than predicate
   */
  @Override
  public Predicate lt(@Nonnull final Expression<? extends Number> x, @Nonnull final Number y) {
