/*
 * Copyright 2019, 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.delivery;

import com.google.errorprone.annotations.Immutable;

import java.io.Serializable;

import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.Math.abs;

/**
 * The strategy of splitting the entities into a number of shards uniformly.
 *
 * <p>Uses a hash code of the entity identifier and the remainder of its division by the total
 * number of shards to determine the index of a shard, at which the modification is allowed.
 */
@Immutable
public final class UniformAcrossAllShards implements DeliveryStrategy, Serializable {

    private static final long serialVersionUID = 0L;

    private final int numberOfShards;

    /**
     * Creates an instance of this strategy.
     *
     * @param numberOfShards
     *         a number of shards; must be greater than zero
     */
    private UniformAcrossAllShards(int numberOfShards) {
        checkArgument(numberOfShards > 0, "Number of shards must be positive");
        this.numberOfShards = numberOfShards;
    }

    @Override
    public ShardIndex indexFor(Object entityId) {
        if (1 == numberOfShards) {
            return newIndex(0, 1);
        }
        int hashValue = entityId.hashCode();
        int totalShards = shardCount();
        int indexValue = abs(hashValue % totalShards);
        ShardIndex result = newIndex(indexValue, totalShards);
        return result;
    }

    @Override
    public int shardCount() {
        return numberOfShards;
    }

    /**
     * Creates a strategy of uniform target distribution across shards, for a given shard number.
     *
     * @param totalShards
     *         a number of shards
     * @return a uniform distribution strategy instance for a given shard number
     */
    public static DeliveryStrategy forNumber(int totalShards) {
        UniformAcrossAllShards result = new UniformAcrossAllShards(totalShards);
        return result;
    }

    private static ShardIndex newIndex(int indexValue, int totalShards) {
        return ShardIndex.newBuilder()
                         .setIndex(indexValue)
                         .setOfTotal(totalShards)
                         .build();
    }

    private enum SingleShard {
        INSTANCE;
        private final UniformAcrossAllShards strategy = new UniformAcrossAllShards(1);
    }

    /**
     * Returns a pre-defined strategy instance, which defines a single shard and puts all
     * the targets into it.
     *
     * @return a strategy that puts all entities in a single shard
     */
    @SuppressWarnings("WeakerAccess")   // a part of the public API.
    public static DeliveryStrategy singleShard() {
        return SingleShard.INSTANCE.strategy;
    }
}

@@ -2032,32 +2032,469 @@
 fShards;%0A    %7D%0A%0A
+    /**%0A     * Creates a strategy of uniform target distribution across shards, for a given shard number.%0A     *%0A     * @param totalShards%0A     *         a number of shards%0A     * @return a uniform distribution strategy instance for a given shard number%0A     */%0A    public static DeliveryStrategy forNumber(int totalShards) %7B%0A        UniformAcrossAllShards result = new UniformAcrossAllShards(totalShards);%0A        return result;%0A    %7D%0A%0A
     @Override%0A  
@@ -2483,32 +2483,32 @@
 %0A%0A    @Override%0A
-
     public Shard
@@ -2609,11 +2609,8 @@
 ex(0
-, 1
 );%0A 
@@ -2809,21 +2809,8 @@
 alue
-, totalShards
 );%0A 
@@ -2928,459 +2928,15 @@
     
-/**%0A     * Creates a strategy of uniform target distribution across shards, for a given shard number.%0A     *%0A     * @param totalShards%0A     *         a number of shards%0A     * @return a uniform distribution strategy instance for a given shard number%0A     */%0A    public static DeliveryStrategy forNumber(int totalShards) %7B%0A        UniformAcrossAllShards result = new UniformAcrossAllShards(totalShards);%0A        return result;%0A    %7D%0A%0A    private static
+private
  Sha
@@ -2970,25 +2970,8 @@
 alue
-, int totalShards
 ) %7B%0A
@@ -3093,27 +3093,28 @@
 OfTotal(
-totalShards
+shardCount()
 )%0A      

Clean up