/*
 * Copyright 2018, 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 io.spine.server.delivery;

import io.spine.annotation.Internal;
import io.spine.core.ActorMessageEnvelope;
import io.spine.server.entity.Entity;
import io.spine.server.sharding.ShardedStream;


/**
 * A strategy on delivering the messages to the instances of a certain entity type.
 *
 * @param <I> the ID type of entity, to which the messages are being delivered
 * @param <E> the type of entity
 * @param <M> the type of message envelope, which is used for message delivery
 *
 * @author Alex Tymchenko
 */
@Internal
public abstract class EndpointDelivery<I,
                                       E extends Entity<I, ?>,
                                       M extends ActorMessageEnvelope<?, ?, ?>,
                                       S extends ShardedStream<I, ?, M>,
                                       B extends ShardedStream.AbstractBuilder<I, B, S>> {

    private final Sender<I, M> sender;
    private final Consumer<I, E, M, S, B> consumer;

    protected EndpointDelivery(Consumer<I, E, M, S, B> consumer) {
        this.consumer = consumer;
        this.sender = new Sender<>(consumer.getTag());
    }

    public Sender<I, M> getSender() {
        return sender;
    }

    public Consumer<I, E, M, S, B> getConsumer() {
        return consumer;
    }

    //    public void deliver(I id, M message) {
//        final ShardingTag<M> consumerId = getTag();
//        final Set<ShardedStream<I, M>> streams = sharding().find(consumerId, id);
//
//        for (ShardedStream<I, M> shardedStream : streams) {
//            shardedStream.post(id, message);
//        }
//    }

//    /**
//     * Obtains an endpoint to dispatch the given envelope.
//     *
//     * @param messageEnvelope the envelope to obtain the endpoint for
//     * @return the message endpoint
//     */
//    protected abstract EntityMessageEndpoint<I, E, M, ?> getEndpoint(M messageEnvelope);

//    protected abstract B newShardedStreamBuilder();

//    @Override
//    public ShardingTag<M> getTag() {
//        return shardingTag;
//    }
//
//    /**
//     * Delivers the envelope to the entity of the given ID taking into account
//     * the target tenant.
//     *
//     * <p>Use this method to deliver the previously postponed messages.
//     *
//     * @param id       an ID of an entity to deliver the envelope to
//     * @param envelopeMessage an envelope to deliver
//     */
//    protected void deliverNow(final I id, final M envelopeMessage) {
//        final TenantId tenantId = envelopeMessage.getActorContext()
//                                                 .getTenantId();
//        final TenantAwareOperation operation = new TenantAwareOperation(tenantId) {
//            @Override
//            public void run() {
//                passToEndpoint(id, envelopeMessage);
//            }
//        };
//
//        operation.run();
//    }

//    /**
//     * Calls the dispatching method of endpoint directly.
//     *
//     * @param id an ID of an entity to deliver th envelope to
//     * @param envelopeMessage an envelope to delivery
//     */
//    protected abstract void passToEndpoint(I id, M envelopeMessage);
//


//    @Override
//    public ShardedStream<I, M> bindToTransport(BoundedContextName name,
//                                               ShardingKey key,
//                                               TransportFactory transportFactory) {
//        final ShardedStream<I, M> stream =
//                newShardedStreamBuilder().setBoundedContextName(name)
//                                         .setKey(key)
//                                         .build(transportFactory);
//        stream.setConsumer(this);
//        return stream;
//    }


//    @Override
//    public void onNext(I targetId, M messageEnvelope) {
//        deliverNow(targetId, messageEnvelope);
//    }

//    private enum LogSingleton {
//        INSTANCE;
//        @SuppressWarnings("NonSerializableFieldInSerializableClass")
//        private final Logger value = LoggerFactory.getLogger(EndpointDelivery.class);
//    }
//
//    private static Logger log() {
//        return LogSingleton.INSTANCE.value;
//    }
}

@@ -2294,2879 +2294,7 @@
    %7D
-%0A%0A    //    public void deliver(I id, M message) %7B%0A//        final ShardingTag%3CM%3E consumerId = getTag();%0A//        final Set%3CShardedStream%3CI, M%3E%3E streams = sharding().find(consumerId, id);%0A//%0A//        for (ShardedStream%3CI, M%3E shardedStream : streams) %7B%0A//            shardedStream.post(id, message);%0A//        %7D%0A//    %7D%0A%0A//    /**%0A//     * Obtains an endpoint to dispatch the given envelope.%0A//     *%0A//     * @param messageEnvelope the envelope to obtain the endpoint for%0A//     * @return the message endpoint%0A//     */%0A//    protected abstract EntityMessageEndpoint%3CI, E, M, ?%3E getEndpoint(M messageEnvelope);%0A%0A//    protected abstract B newShardedStreamBuilder();%0A%0A//    @Override%0A//    public ShardingTag%3CM%3E getTag() %7B%0A//        return shardingTag;%0A//    %7D%0A//%0A//    /**%0A//     * Delivers the envelope to the entity of the given ID taking into account%0A//     * the target tenant.%0A//     *%0A//     * %3Cp%3EUse this method to deliver the previously postponed messages.%0A//     *%0A//     * @param id       an ID of an entity to deliver the envelope to%0A//     * @param envelopeMessage an envelope to deliver%0A//     */%0A//    protected void deliverNow(final I id, final M envelopeMessage) %7B%0A//        final TenantId tenantId = envelopeMessage.getActorContext()%0A//                                                 .getTenantId();%0A//        final TenantAwareOperation operation = new TenantAwareOperation(tenantId) %7B%0A//            @Override%0A//            public void run() %7B%0A//                passToEndpoint(id, envelopeMessage);%0A//            %7D%0A//        %7D;%0A//%0A//        operation.run();%0A//    %7D%0A%0A//    /**%0A//     * Calls the dispatching method of endpoint directly.%0A//     *%0A//     * @param id an ID of an entity to deliver th envelope to%0A//     * @param envelopeMessage an envelope to delivery%0A//     */%0A//    protected abstract void passToEndpoint(I id, M envelopeMessage);%0A//%0A%0A%0A//    @Override%0A//    public ShardedStream%3CI, M%3E bindToTransport(BoundedContextName name,%0A//                                               ShardingKey key,%0A//                                               TransportFactory transportFactory) %7B%0A//        final ShardedStream%3CI, M%3E stream =%0A//                newShardedStreamBuilder().setBoundedContextName(name)%0A//                                         .setKey(key)%0A//                                         .build(transportFactory);%0A//        stream.setConsumer(this);%0A//        return stream;%0A//    %7D%0A%0A%0A//    @Override%0A//    public void onNext(I targetId, M messageEnvelope) %7B%0A//        deliverNow(targetId, messageEnvelope);%0A//    %7D%0A%0A//    private enum LogSingleton %7B%0A//        INSTANCE;%0A//        @SuppressWarnings(%22NonSerializableFieldInSerializableClass%22)%0A//        private final Logger value = LoggerFactory.getLogger(EndpointDelivery.class);%0A//    %7D%0A//%0A//    private static Logger log() %7B%0A//        return LogSingleton.INSTANCE.value;%0A//    %7D
 %0A%7D%0A

Drop the commented code.