/*
 *
 * Copyright 2016, TeamDev Ltd. 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 org.spine3.base;

import com.google.protobuf.Any;
import com.google.protobuf.FieldMask;
import com.google.protobuf.Message;
import org.spine3.client.EntityFilters;
import org.spine3.client.EntityId;
import org.spine3.client.EntityIdFilter;
import org.spine3.client.Query;
import org.spine3.client.Target;
import org.spine3.protobuf.AnyPacker;
import org.spine3.protobuf.TypeUrl;

import javax.annotation.Nullable;
import java.util.Set;

/**
 * Client-side utilities for working with queries.
 *
 * @author Alex Tymchenko
 */
public class Queries {

    private Queries() {
    }

    public static Query readByIds(Class<? extends Message> entityClass, Set<Message> ids) {
        final Query result = compose(entityClass, ids, null);
        return result;
    }

    public static Query readAll(Class<? extends Message> entityClass) {
        final Query result = compose(entityClass, null, null);
        return result;
    }


    // TODO[alex.tymchenko]: think of Optional instead. Consider Java 8 vs Guava Optional.
    private static Query compose(Class<? extends Message> entityClass, @Nullable Set<Message> ids, @Nullable FieldMask fieldMask) {
        final TypeUrl type = TypeUrl.of(entityClass);

        final boolean includeAll = ids == null;

        final EntityIdFilter.Builder idFilterBuilder = EntityIdFilter.newBuilder();

        if (!includeAll) {
            for (Message rawId : ids) {
                final Any packedId = AnyPacker.pack(rawId);
                final EntityId entityId = EntityId.newBuilder()
                                                  .setId(packedId)
                                                  .build();
                idFilterBuilder.addIds(entityId);
            }
        }
        final EntityIdFilter idFilter = idFilterBuilder.build();
        final EntityFilters filters = EntityFilters.newBuilder()
                                                   .setIdFilter(idFilter)
                                                   .build();

        final Target target = Target.newBuilder()
                                    .setIncludeAll(includeAll)
                                    .setType(type.getTypeName())
                                    .setFilters(filters)
                                    .build();


        final Query.Builder queryBuilder = Query.newBuilder()
                                                .setTarget(target);
        if (fieldMask != null) {
            queryBuilder.setFieldMask(fieldMask);
        }
        final Query result = queryBuilder
                .build();
        return result;
    }
}

@@ -1723,32 +1723,37 @@
 result = compose
+Query
 (entityClass, id
@@ -1900,16 +1900,21 @@
  compose
+Query
 (entityC
@@ -1962,16 +1962,364 @@
 %0A    %7D%0A%0A
+    public static Target someOf(Class%3C? extends Message%3E entityClass, Set%3CMessage%3E ids) %7B%0A        final Target result = composeTarget(entityClass, ids);%0A        return result;%0A    %7D%0A%0A    public static Target allOf(Class%3C? extends Message%3E entityClass) %7B%0A        final Target result = composeTarget(entityClass, null);%0A        return result;%0A    %7D%0A%0A
 %0A    // 
@@ -2434,16 +2434,21 @@
  compose
+Query
 (Class%3C?
@@ -2535,24 +2535,518 @@
 ieldMask) %7B%0A
+        final Target target = composeTarget(entityClass, ids);%0A%0A%0A        final Query.Builder queryBuilder = Query.newBuilder()%0A                                                .setTarget(target);%0A        if (fieldMask != null) %7B%0A            queryBuilder.setFieldMask(fieldMask);%0A        %7D%0A        final Query result = queryBuilder%0A                .build();%0A        return result;%0A    %7D%0A%0A    private static Target composeTarget(Class%3C? extends Message%3E entityClass, @Nullable Set%3CMessage%3E ids) %7B%0A
         fina
@@ -3888,37 +3888,22 @@
         
-final Target target =
+return
  Target.
@@ -3932,39 +3932,24 @@
             
-      
-         
 .setIncludeA
@@ -3980,39 +3980,24 @@
             
-    
-           
 .setType(typ
@@ -4030,39 +4030,24 @@
             
- 
-              
 .setFilters(
@@ -4072,39 +4072,24 @@
             
-           
-    
 .build();%0A%0A%0A
@@ -4090,324 +4090,8 @@
 ();%0A
-%0A%0A        final Query.Builder queryBuilder = Query.newBuilder()%0A                                                .setTarget(target);%0A        if (fieldMask != null) %7B%0A            queryBuilder.setFieldMask(fieldMask);%0A        %7D%0A        final Query result = queryBuilder%0A                .build();%0A        return result;%0A
     

Add Target-related methods.