/*
 * 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.aggregate;

import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.spine.core.EventClass;
import io.spine.core.EventEnvelope;
import io.spine.logging.Logging;
import io.spine.server.event.EventDispatcher;
import io.spine.server.integration.ExternalMessageDispatcher;

import java.util.Optional;
import java.util.Set;

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

/**
 * Dispatches events to be imported to the associated aggregate repository.
 *
 * @param <I> the type of aggregate IDs
 * @author Alexander Yevsyukov
 * @apiNote This class is {@code public} so that it can be used as a generic parameter {@code <D>}
 *          of {@link io.spine.server.aggregate.ImportBus ImportBus}. This way we
 *          allow only instances of this class being registered for dispatching imported events
 *          (thus preventing an arbitrary
 *         {@link io.spine.server.event.EventDispatcher EventDispatcher} being used for import).
 *         This way, only {@linkplain io.spine.server.aggregate.AggregateRepository
 *         Aggregate Repositories}
 *         {@link io.spine.server.aggregate.ImportBus#dispatch(io.spine.core.EventEnvelope)
 *         ImportBus}.
 */
public final class EventImportDispatcher<I> implements EventDispatcher<I>, Logging {

    private final AggregateRepository<I, ?> repository;

    private EventImportDispatcher(AggregateRepository<I, ?> repository) {
        this.repository = repository;
    }

    static <I> EventImportDispatcher<I> of(AggregateRepository<I, ?> repository) {
        checkNotNull(repository);
        return new EventImportDispatcher<>(repository);
    }

    @Override
    public Set<EventClass> getMessageClasses() {
        return repository.getImportableEventClasses();
    }

    /**
     * Always returns empty set because external events cannot be imported.
     */
    @Override
    public Set<EventClass> getExternalEventClasses() {
        return ImmutableSet.of();
    }

    @CanIgnoreReturnValue
    @Override
    public Set<I> dispatch(EventEnvelope envelope) {
        I result = repository.importEvent(envelope);
        return ImmutableSet.of(result);
    }

    @Override
    public void onError(EventEnvelope envelope, RuntimeException exception) {
        EventClass eventClass = envelope.getMessageClass();
        String id = envelope.idAsString();
        _error("Unable to import event class: `{}` id: {``} repository: `{}`",
               eventClass, id, repository);
    }

    @Override
    public Optional<ExternalMessageDispatcher<I>> createExternalDispatcher() {
        return Optional.empty();
    }
}

@@ -1152,16 +1152,53 @@
 nValue;%0A
+import io.spine.annotation.Internal;%0A
 import i
@@ -1222,16 +1222,16 @@
 tClass;%0A
-
 import i
@@ -1681,22 +1681,34 @@
 @apiNote
+%0A *
  This 
+internal 
 class is
@@ -1708,16 +1708,21 @@
 lass is 
+made 
 %7B@code p
@@ -1732,215 +1732,233 @@
 ic%7D 
-so that it can be used as a generic parameter %7B@code %3CD%3E%7D%0A *          of %7B@link io.spine.server.aggregate.ImportBus ImportBus%7D. This way we%0A *          allow only instances of this class being registered for
+for restricting types of dispatchers that can be%0A * registered with an %7B@link io.spine.server.aggregate.ImportBus ImportBus%7D. Since only%0A * %7B@linkplain io.spine.server.aggregate.AggregateRepository Aggregate Repositories%7D can
  dis
@@ -1962,19 +1962,19 @@
 dispatch
-ing
+%0A *
  importe
@@ -1985,105 +1985,77 @@
 ents
-%0A *          (thus preventing an arbitrary%0A *         %7B@link io.spine.server.event.E
+ to their aggregates, we limit the type of the import e
 vent
-D
+ d
 ispatche
 r Ev
@@ -2054,145 +2054,144 @@
 tche
-r EventDispatcher%7D being used for import).%0A *         This way, only %7B@linkplain io.spine.server.aggregate.AggregateRepository%0A *        
+s to%0A * this class, which neither can be extended, nor created from outside of this package.%0A * Instances of this class are proxies that
  Agg
@@ -2213,27 +2213,34 @@
 ries
-%7D%0A *        
+ create and%0A *
  %7B@link
+plain
  io.
@@ -2276,72 +2276,145 @@
 Bus#
-dispatch(io.spine.core.EventEnvelope)%0A *         ImportBus%7D.%0A */
+register(%0A * io.spine.server.bus.MessageDispatcher) register%7D with an %7B@code ImportBus%7D of their parent%0A * Bounded Context.%0A */%0A@Internal
 %0Apub

Document restricting event import dispatcher API