/*
 * Copyright 2020, TeamDev. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.spine.query;

import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Joins the {@linkplain SubjectParameter subject parameters} with
 * {@linkplain LogicalOperator logical operators}.
 *
 * @param <R> the type of the record which is stored for subject
 */
public final class QueryPredicate<R> {

    /**
     * Defines whether the parameters are evaluated in conjunction or disjunction with each other.
     */
    private final LogicalOperator operator;

    /**
     * The list of parameters, each joined with the rest of parameters (including the custom params)
     * with the same logical operator.
     */
    private final ImmutableList<SubjectParameter<R, ?, ?>> parameters;

    /**
     * The list of parameters, each joined with the rest of parameters
     * with the same logical operator.
     */
    private final ImmutableList<CustomSubjectParameter<?, ?>> customParameters;

    /**
     * Creates a new {@code Predicate}.
     */
    private QueryPredicate(Builder<R> builder) {
        this.operator = checkNotNull(builder.operator);
        checkNotNull(builder.parameters);
        this.parameters = ImmutableList.copyOf(builder.parameters);
        checkNotNull(builder.customParameters);
        this.customParameters = ImmutableList.copyOf(builder.customParameters);
    }

    /**
     * Creates a new instance of {@code Predicate.Builder} for a specified logical operator.
     *
     * @param <R> the type of the record which is stored for subject
     */
    static <R> Builder<R> newBuilder(LogicalOperator operator) {
        return new Builder<>(operator);
    }

    /**
     * Returns an operator with which the parameters are joined.
     */
    public LogicalOperator operator() {
        return operator;
    }

    /**
     * Returns the list of parameters of this {@code Predicate}.
     */
    public ImmutableList<SubjectParameter<R, ?, ?>> parameters() {
        return parameters;
    }

    /**
     * Returns the list of custom parameters of this {@code Predicate}.
     */
    public ImmutableList<CustomSubjectParameter<?, ?>> customParameters() {
        return customParameters;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof QueryPredicate)) {
            return false;
        }
        QueryPredicate<?> predicate = (QueryPredicate<?>) o;
        return operator == predicate.operator &&
                parameters.equals(predicate.parameters) &&
                customParameters.equals(predicate.customParameters);
    }

    @Override
    public int hashCode() {
        return Objects.hash(operator, parameters, customParameters);
    }

    /**
     * Builds {@link QueryPredicate} instances.
     *
     * @param <R> the type of the record which is stored for subject
     */
    static final class Builder<R> {

        private final LogicalOperator operator;
        private final List<SubjectParameter<R, ?, ?>> parameters = new ArrayList<>();
        private final List<CustomSubjectParameter<?, ?>> customParameters = new ArrayList<>();

        /**
         * Does not allow to instantiate this class directly.
         *
         * @param operator
         *         the operator which defines whether the parameters are evaluated
         *         in conjunction or disjunction with each other
         * @see QueryPredicate#newBuilder(LogicalOperator)
         */
        private Builder(LogicalOperator operator) {
            this.operator = operator;
        }

        /**
         * Adds a parameter to the predicate.
         */
        @CanIgnoreReturnValue
        Builder<R> add(SubjectParameter<R, ?, ?> parameter) {
            checkNotNull(parameter);
            this.parameters.add(parameter);
            return this;
        }

        /**
         * Adds a parameter, which targets some custom or computed property of the record.
         */
        @CanIgnoreReturnValue
        Builder<R> addCustom(CustomSubjectParameter<?, ?> parameter) {
            checkNotNull(parameter);
            customParameters.add(parameter);
            return this;
        }

        /**
         * Tells if there is at least one parameter added.
         */
        boolean hasParams() {
            return !parameters.isEmpty() || !customParameters.isEmpty();
        }

        /**
         * Builds a new instance of a {@code Predicate} based on the data in this {@code Builder}.
         */
        QueryPredicate<R> build() {
            return new QueryPredicate<>(this);
        }
    }
}

@@ -1360,16 +1360,18 @@
  with%0A *
+ a
  %7B@linkp
@@ -1407,17 +1407,16 @@
 operator
-s
 %7D.%0A *%0A *
@@ -1723,117 +1723,62 @@
 ters
-, each joined with the rest of parameters (including
+ which target the columns of
  the 
-cu
 sto
-m params)%0A     * with the same logical operator
+red record subject
 .%0A  
@@ -1874,32 +1874,39 @@
   * The list of 
+custom 
 parameters, each
@@ -1903,87 +1903,59 @@
 ters
-, each joined with the rest of parameters%0A     * with the same logical operator
+ targeting the custom columns of the stored subject
 .%0A  
@@ -2924,24 +2924,16 @@
 rns the 
-list of 
 paramete
@@ -2952,32 +2952,64 @@
 @code Predicate%7D
+ which target the record columns
 .%0A     */%0A    pu
@@ -3130,31 +3130,16 @@
 rns the 
-list of custom 
 paramete
@@ -3158,32 +3158,64 @@
 @code Predicate%7D
+ which target the custom columns
 .%0A     */%0A    pu
@@ -3316,32 +3316,518 @@
 ameters;%0A    %7D%0A%0A
+    /**%0A     * Returns the list of all parameters of this predicate, including both%0A     * %7B@linkplain #parameters() targeting the own record columns%7D%0A     * and those %7B@linkplain #customParameters() defined for the custom columns%7D of the subject.%0A     */%0A    public ImmutableList%3CSubjectParameter%3C?, ?, ?%3E%3E allParams() %7B%0A        return ImmutableList.%3CSubjectParameter%3C?, ?, ?%3E%3Ebuilder()%0A                .addAll(parameters())%0A                .addAll(customParameters()).build();%0A    %7D%0A%0A
     @Override%0A  

Allow to return all the parameters of the predicate.