/*
 * Copyright 2018, 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.server.entity.storage;

import com.google.common.base.Objects;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.spine.annotation.SPI;
import io.spine.client.ColumnFilter;

import java.io.Serializable;
import java.util.Iterator;

/**
 * The parameters of an {@link EntityQuery}.
 *
 * <p>{@code QueryParameters} are passed into the {@link io.spine.server.storage.Storage Storage}
 * implementations.
 *
 * @author Dmytro Dashenkov
 */
@SPI /* Available to SPI users, providing own {@code Storage} implementations. */
public final class QueryParameters implements Iterable<CompositeQueryParameter>, Serializable {

    private static final long serialVersionUID = 0L;

    private final ImmutableCollection<CompositeQueryParameter> parameters;

    /**
     * A flag that shows if the current instance of {@code CompositeQueryParameter} has
     * the {@link io.spine.server.storage.LifecycleFlagField lifecycle attributes} set of not.
     *
     * <p>This flag turns into {@code true} if at least one of the underlying
     * {@linkplain CompositeQueryParameter parameters}
     * {@linkplain CompositeQueryParameter#hasLifecycle() contains Lifecycle attributes}. Otherwise
     * it is {@code false}.
     */
    private final boolean hasLifecycle;

    private QueryParameters(Builder builder) {
        this.parameters = builder.getParameters()
                                 .build();
        this.hasLifecycle = builder.hasLifecycle;
    }

    /**
     * Returns an iterator over the {@linkplain ColumnFilter column filters}.
     *
     * <p>The resulting {@code Iterator} throws {@link UnsupportedOperationException} on call
     * to {@link Iterator#remove() Iterator.remove()}.
     *
     * @return an {@link Iterator}.
     */
    @Override
    public Iterator<CompositeQueryParameter> iterator() {
        return parameters.iterator();
    }

    /**
     * @return whether this parameters include filters by
     * the {@linkplain io.spine.server.entity.LifecycleFlags Entity lifecycle flags} or not
     */
    public boolean isLifecycleAttributesSet() {
        return hasLifecycle;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        QueryParameters that = (QueryParameters) o;
        return Objects.equal(parameters, that.parameters);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(parameters);
    }

    public static Builder newBuilder() {
        return new Builder();
    }

    /**
     * The builder for the {@code QueryParameters}.
     */
    public static class Builder {

        private final ImmutableCollection.Builder<CompositeQueryParameter> parameters;

        private boolean hasLifecycle;

        private Builder() {
            parameters = ImmutableList.builder();
        }

        @CanIgnoreReturnValue
        public Builder add(CompositeQueryParameter parameter) {
            parameters.add(parameter);
            hasLifecycle |= parameter.hasLifecycle();
            return this;
        }

        @CanIgnoreReturnValue
        public Builder addAll(Iterable<CompositeQueryParameter> parameters) {
            for (CompositeQueryParameter parameter : parameters) {
                add(parameter);
            }
            return this;
        }

        public ImmutableCollection.Builder<CompositeQueryParameter> getParameters() {
            return parameters;
        }

        /**
         * Creates a new instance of {@code QueryParameters} with the collected parameters.
         *
         * @return a new instance of {@code QueryParameters}
         */
        public QueryParameters build() {
            return new QueryParameters(this);
        }
    }
}

@@ -1319,16 +1319,46 @@
 nFilter;
+%0Aimport io.spine.client.Order;
 %0A%0Aimport
@@ -1408,16 +1408,82 @@
 rator;%0A%0A
+import static com.google.common.base.Preconditions.checkNotNull;%0A%0A
 /**%0A * T
@@ -2490,24 +2490,85 @@
 asLifecycle;
+%0A    private final long limit;%0A    private final Order order;
 %0A%0A    privat
@@ -2693,24 +2693,100 @@
   .build();%0A
+        this.limit = builder.limit();%0A        this.order = builder.order();%0A
         this
@@ -3232,32 +3232,304 @@
 rator();%0A    %7D%0A%0A
+    public Order order() %7B%0A        return order;%0A    %7D%0A%0A    public boolean ordered() %7B%0A        return !order.equals(Order.getDefaultInstance());%0A    %7D%0A%0A    public long limit() %7B%0A        return limit;%0A    %7D%0A%0A    public boolean limited() %7B%0A        return limit != 0;%0A    %7D%0A%0A
     /**%0A     * @
@@ -3580,24 +3580,32 @@
 rs by%0A     *
+        
  the %7B@linkp
@@ -4491,16 +4491,73 @@
 fecycle;
+%0A        private Order order;%0A        private long limit;
 %0A%0A      
@@ -4624,24 +4624,72 @@
 .builder();%0A
+            order = Order.getDefaultInstance();%0A
         %7D%0A%0A 
@@ -5287,32 +5287,417 @@
 ers;%0A        %7D%0A%0A
+        public Builder limit(long value) %7B%0A            limit = value;%0A            return this;%0A        %7D%0A%0A        public long limit() %7B%0A            return limit;%0A        %7D%0A%0A        public Builder sort(Order order) %7B%0A            checkNotNull(order);%0A            this.order = order;%0A            return this;%0A        %7D%0A%0A        public Order order() %7B%0A            return order;%0A        %7D%0A%0A
         /**%0A    

Add Order and limit to QueryParameters.