/*
 * Copyright 2017 Red Hat, Inc, and individual 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 me.snowdrop.data.hibernatesearch.core.query;

import java.util.Arrays;
import java.util.Collection;

import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.flexible.core.util.StringUtils;
import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.RegexpQuery;
import org.apache.lucene.search.WildcardQuery;
import org.hibernate.search.query.dsl.BooleanJunction;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hibernate.search.query.dsl.RangeMatchingContext;
import org.hibernate.search.query.dsl.TermMatchingContext;

/**
 * @author <a href="mailto:mluksa@redhat.com">Marko Luksa</a>
 */
public class LuceneQueryBuilder {

  private QueryBuilder queryBuilder;

  public LuceneQueryBuilder(QueryBuilder queryBuilder) {
    this.queryBuilder = queryBuilder;
  }

  public Query matchAll() {
    return queryBuilder.all().createQuery();
  }

  public Query all(Collection<Query> subQueries) {
    BooleanJunction<BooleanJunction> bool = queryBuilder.bool();
    for (Query subQuery : subQueries) {
      bool.must(subQuery);
    }
    return bool.createQuery();
  }

  public Query any(Query... subQueries) {
    return any(Arrays.asList(subQueries));
  }

  public Query any(Collection<Query> subQueries) {
    BooleanJunction<BooleanJunction> bool = queryBuilder.bool();
    for (Query subQuery : subQueries) {
      bool.should(subQuery);
    }
    return bool.createQuery();
  }

  public Query in(String fieldName, Collection<?> values) {
    BooleanJunction<BooleanJunction> bool = queryBuilder.bool();
    for (Object value : values) {
      bool.should(equal(fieldName, value));
    }
    return bool.createQuery();
  }

  public Query notIn(String fieldName, Collection<?> values) {
    BooleanJunction<BooleanJunction> bool = queryBuilder.bool();
    for (Object value : values) {
      bool.must(equal(fieldName, value)).not();
    }
    return bool.createQuery();
  }

  public Query notEqual(String fieldName, Object value) {
    return any(
      lessThan(fieldName, value),
      greaterThan(fieldName, value)
    );
  }

  public Query not(Query query) {
    return queryBuilder.bool().must(query).not().createQuery();
  }

  public Query equal(String fieldName, Object value) {
    return keywordOnField(fieldName)
      .matching(value)
      .createQuery();
  }

  private TermMatchingContext keywordOnField(String fieldName) {
    return queryBuilder
      .keyword().onField(fieldName)
      .ignoreFieldBridge();
  }

  public Query greaterThan(String fieldName, Object value) {
    return rangeOnField(fieldName)
      .above(value).excludeLimit()
      .createQuery();
  }

  public Query greaterThanOrEqual(String fieldName, Object value) {
    return rangeOnField(fieldName)
      .above(value)
      .createQuery();
  }

  public Query lessThan(String fieldName, Object value) {
    return rangeOnField(fieldName)
      .below(value).excludeLimit()
      .createQuery();
  }

  public Query lessThanOrEqual(String fieldName, Object value) {
    return rangeOnField(fieldName)
      .below(value)
      .createQuery();
  }

  public Query between(String fieldName, Object min, Object max) {
    BooleanJunction<BooleanJunction> bool = queryBuilder.bool();
    bool.must(greaterThanOrEqual(fieldName, min)).must(lessThanOrEqual(fieldName, max));
    return bool.createQuery();
  }

  public Query fuzzy(String fieldName, Object value) {
    return new FuzzyQuery(new Term(fieldName, StringUtils.toString(value)));
  }

  public Query contains(String fieldName, Object value) {
    return new WildcardQuery(new Term(fieldName, "*" + value + "*"));
  }

  public Query notContains(String fieldName, Object value) {
    BooleanJunction<BooleanJunction> bool = queryBuilder.bool();
    bool.must(contains(fieldName, value)).not();
    return bool.createQuery();
  }

  public Query startsWith(String fieldName, Object value) {
    return new WildcardQuery(new Term(fieldName, value + "*"));
  }

  public Query endsWith(String fieldName, Object value) {
    return new WildcardQuery(new Term(fieldName, "*" + value));
  }

  public Query reqexp(String fieldName, String reqexp) {
    return new RegexpQuery(new Term(fieldName, reqexp));
  }

  private RangeMatchingContext rangeOnField(String fieldName) {
    return queryBuilder
      .range().onField(fieldName)
      .ignoreFieldBridge();
  }
}

@@ -2597,164 +2597,8 @@
  %7D%0A%0A
-  public Query notEqual(String fieldName, Object value) %7B%0A    return any(%0A      lessThan(fieldName, value),%0A      greaterThan(fieldName, value)%0A    );%0A  %7D%0A%0A
   pu
@@ -2623,24 +2623,24 @@
 ry query) %7B%0A
+
     return q
@@ -2845,49 +2845,28 @@
 %0A  p
-rivate TermMatchingContext keywordOnField
+ublic Query notEqual
 (Str
@@ -2870,32 +2870,46 @@
 String fieldName
+, Object value
 ) %7B%0A    return q
@@ -2911,82 +2911,35 @@
 urn 
-queryBuilder%0A      .keyword().onField(fieldName)%0A      .ignoreFieldBridge(
+not(equal(fieldName, value)
 );%0A 
@@ -4665,16 +4665,147 @@
 );%0A  %7D%0A%0A
+  private TermMatchingContext keywordOnField(String fieldName) %7B%0A    return queryBuilder%0A      .keyword().onField(fieldName);%0A  %7D%0A%0A
   privat
@@ -4921,35 +4921,8 @@
 ame)
-%0A      .ignoreFieldBridge()
 ;%0A  

Change notEq, remove ignoreFieldBridge.